triangle_mesh.template.cc
Go to the documentation of this file.
1// LIC// ====================================================================
2// LIC// This file forms part of oomph-lib, the object-oriented,
3// LIC// multi-physics finite-element library, available
4// LIC// at http://www.oomph-lib.org.
5// LIC//
6// LIC// Copyright (C) 2006-2025 Matthias Heil and Andrew Hazel
7// LIC//
8// LIC// This library is free software; you can redistribute it and/or
9// LIC// modify it under the terms of the GNU Lesser General Public
10// LIC// License as published by the Free Software Foundation; either
11// LIC// version 2.1 of the License, or (at your option) any later version.
12// LIC//
13// LIC// This library is distributed in the hope that it will be useful,
14// LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16// LIC// Lesser General Public License for more details.
17// LIC//
18// LIC// You should have received a copy of the GNU Lesser General Public
19// LIC// License along with this library; if not, write to the Free Software
20// LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21// LIC// 02110-1301 USA.
22// LIC//
23// LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
24// LIC//
25// LIC//====================================================================
26#ifndef OOMPH_TRIANGLE_MESH_TEMPLATE_HEADER
27#define OOMPH_TRIANGLE_MESH_TEMPLATE_HEADER
28
29#ifndef OOMPH_TRIANGLE_MESH_HEADER
30#error __FILE__ should only be included from triangle_mesh.h.
31#endif // OOMPH_TRIANGLE_MESH_HEADER
32
33#include <iostream>
34
35#include "generic/map_matrix.h"
37#include "generic/projection.h"
39
40namespace oomph
41{
42 //======================================================================
43 /// Build with the help of the scaffold mesh coming
44 /// from the triangle mesh generator Triangle.
45 //======================================================================
46 template<class ELEMENT>
48 const bool& use_attributes)
49 {
50 // Mesh can only be built with 2D Telements.
51 MeshChecker::assert_geometric_element<TElementGeometricBase, ELEMENT>(2);
52
53 // Create space for elements
54 unsigned nelem = Tmp_mesh_pt->nelement();
55 Element_pt.resize(nelem);
56
57 // Create space for nodes
58 unsigned nnode_scaffold = Tmp_mesh_pt->nnode();
59
60 // Create a map storing the node_id of the mesh used to update the
61 // node position in the update_triangulateio function
62 std::map<Node*, unsigned> old_global_number;
63
64 // Store the TriangulateIO node id
65 for (unsigned inod = 0; inod < nnode_scaffold; inod++)
66 {
67 Node* old_node_pt = Tmp_mesh_pt->node_pt(inod);
69 }
70
71 // Initialize the old node id vector
72 Oomph_vertex_nodes_id.resize(nnode_scaffold);
73
74 // Create space for nodes
76
77 // Set number of boundaries
78 unsigned nbound = Tmp_mesh_pt->nboundary();
79
80 // Resize the boundary information
81 set_nboundary(nbound);
82 Boundary_element_pt.resize(nbound);
83 Face_index_at_boundary.resize(nbound);
84
85 // If we have different regions, then resize the region
86 // information
88 {
89 Boundary_region_element_pt.resize(nbound);
90 Face_index_region_at_boundary.resize(nbound);
91 }
92
93 // Loop over elements in scaffold mesh, visit their nodes
94 for (unsigned e = 0; e < nelem; e++)
95 {
96 Element_pt[e] = new ELEMENT;
97 }
98
99 // Number of nodes per element from the scaffold mesh
100 unsigned nnod_el = Tmp_mesh_pt->finite_element_pt(0)->nnode();
101
102 // Setup map to check the (pseudo-)global node number
103 // Nodes whose number is zero haven't been copied across
104 // into the mesh yet.
105 std::map<Node*, unsigned> global_number;
106 unsigned global_count = 0;
107
108 // Map of Element attribute pairs
109 std::map<double, Vector<FiniteElement*>> element_attribute_map;
110
111 // If we're using attributes
112 if (use_attributes)
113 {
114 // If we're using attributes then we need attribute 0 which will
115 // be associated with region 0
116 element_attribute_map[0].resize(0);
117 }
118
119 // Loop over elements in scaffold mesh, visit their nodes
120 for (unsigned e = 0; e < nelem; e++)
121 {
122 // Loop over all nodes in element
123 for (unsigned j = 0; j < nnod_el; j++)
124 {
125 // Pointer to node in the scaffold mesh
126 Node* scaffold_node_pt = Tmp_mesh_pt->finite_element_pt(e)->node_pt(j);
127
128 // Get the (pseudo-)global node number in scaffold mesh
129 // (It's zero [=default] if not visited this one yet)
131
132 // Haven't done this one yet
133 if (j_global == 0)
134 {
135 // Find and store the node_id in the old nodes map
136 Oomph_vertex_nodes_id[global_count] =
138
139 // Get pointer to set of mesh boundaries that this
140 // scaffold node occupies; NULL if the node is not on any boundary
141 std::set<unsigned>* boundaries_pt;
142 scaffold_node_pt->get_boundaries_pt(boundaries_pt);
143
144 // Storage for the new node
145 Node* new_node_pt = 0;
146
147 // Is it on boundaries
148 if (boundaries_pt != 0)
149 {
150 // Create new boundary node
152 finite_element_pt(e)->construct_boundary_node(j, time_stepper_pt);
153
154 // Add to boundaries
155 for (std::set<unsigned>::iterator it = boundaries_pt->begin();
156 it != boundaries_pt->end();
157 ++it)
158 {
159 add_boundary_node(*it, new_node_pt);
160 }
161 }
162 // Build normal node
163 else
164 {
165 // Create new normal node
167 finite_element_pt(e)->construct_node(j, time_stepper_pt);
168 }
169
170 // Give it a number (not necessarily the global node
171 // number in the scaffold mesh -- we just need something
172 // to keep track...)
173 global_count++;
175
176 // Copy new node, created using the NEW element's construct_node
177 // function into global storage, using the same global
178 // node number that we've just associated with the
179 // corresponding node in the scaffold mesh
181
182 // Assign coordinates
183 for (unsigned i = 0; i < finite_element_pt(e)->dim(); i++)
184 {
186 }
187 }
188 // This one has already been done: Copy accross
189 else
190 {
191 finite_element_pt(e)->node_pt(j) = Node_pt[j_global - 1];
192 }
193 }
194
195 // If we're using attributes
196 if (use_attributes)
197 {
198 element_attribute_map[Tmp_mesh_pt->element_attribute(e)].push_back(
199 finite_element_pt(e));
200 }
201 }
202
203 // Now let's construct lists
204 // Find the number of attributes
205 if (use_attributes)
206 {
208
209 // There are n_attribute different regions
210 this->Region_attribute.resize(n_attribute);
211
212 // Copy the vectors in the map over to our internal storage
213 unsigned count = 0;
214 for (std::map<double, Vector<FiniteElement*>>::iterator it =
215 element_attribute_map.begin();
216 it != element_attribute_map.end();
217 ++it)
218 {
219 this->Region_attribute[count] = it->first;
220 Region_element_pt[static_cast<unsigned>(Region_attribute[count])] =
221 it->second;
222 ++count;
223 }
224 }
225
226 // At this point we've created all the elements and
227 // created their vertex nodes. Now we need to create
228 // the additional (midside and internal) nodes!
229
230 unsigned boundary_id = 0;
231
232 // Get number of nodes along element edge and dimension of element (2)
233 // from first element
234 unsigned n_node_1d = finite_element_pt(0)->nnode_1d();
235 unsigned dim = finite_element_pt(0)->dim();
236
237 // Storage for the local coordinate of the new node
239
240 // Get number of nodes in the element from first element
241 unsigned n_node = finite_element_pt(0)->nnode();
242
243 // Storage for each global edge of the mesh
244 unsigned n_global_edge = Tmp_mesh_pt->nglobal_edge();
246
247 // Loop over elements
248 for (unsigned e = 0; e < nelem; e++)
249 {
250 // Cache pointers to the elements
251 FiniteElement* const elem_pt = finite_element_pt(e);
252 FiniteElement* const tmp_elem_pt = Tmp_mesh_pt->finite_element_pt(e);
253
254 // The number of edge nodes is 3*(nnode_1d-1)
255 unsigned n_edge_node = 3 * (n_node_1d - 1);
256
257 // If there are any more nodes, these are internal and can be
258 // constructed and added directly to the mesh
259 for (unsigned n = n_edge_node; n < n_node; ++n)
260 {
261 // Create new node (it can never be a boundary node)
263
264 // What are the node's local coordinates?
266
267 // Find the coordinates of the new node from the existing
268 // and fully-functional element in the scaffold mesh
269 for (unsigned i = 0; i < dim; i++)
270 {
272 }
273
274 // Add the node to the mesh's global look-up scheme
275 Node_pt.push_back(new_node_pt);
276 }
277
278 // Now loop over the mid-side edge nodes
279 // Start from node number 3
280 unsigned n = 3;
281
282 // Loop over edges
283 for (unsigned j = 0; j < 3; j++)
284 {
285 // Find the boundary id of the edge
286 boundary_id = Tmp_mesh_pt->edge_boundary(e, j);
287
288 // Find the global edge index
289 unsigned edge_index = Tmp_mesh_pt->edge_index(e, j);
290
291 // If the nodes on the edge have not been allocated, construct them
292 if (nodes_on_global_edge[edge_index].size() == 0)
293 {
294 // Loop over the nodes on the edge excluding the ends
295 for (unsigned j2 = 0; j2 < n_node_1d - 2; ++j2)
296 {
297 // Storage for the new node
298 Node* new_node_pt = 0;
299
300 // If the edge is on a boundary, construct a boundary node
301 if (boundary_id > 0)
302 {
305 // Add it to the boundary
306 this->add_boundary_node(boundary_id - 1, new_node_pt);
307 }
308 // Otherwise construct a normal node
309 else
310 {
312 }
313
314 // What are the node's local coordinates?
316
317 // Find the coordinates of the new node from the existing
318 // and fully-functional element in the scaffold mesh
319 for (unsigned i = 0; i < dim; i++)
320 {
322 }
323
324 // Add to the global node list
325 Node_pt.push_back(new_node_pt);
326
327 // Add to the edge index
328 nodes_on_global_edge[edge_index].push_back(new_node_pt);
329 // Increment the node number
330 ++n;
331 }
332 }
333 // Otherwise just set the pointers
334 // using the fact that the next time the edge is visited
335 // the nodes must be arranged in the other order because all
336 // triangles have the same orientation
337 else
338 {
339 // Loop over the nodes on the edge excluding the ends
340 for (unsigned j2 = 0; j2 < n_node_1d - 2; ++j2)
341 {
342 // Set the local node from the edge but indexed the other
343 // way around
344 elem_pt->node_pt(n) =
345 nodes_on_global_edge[edge_index][n_node_1d - 3 - j2];
346 ++n;
347 }
348 }
349
350 // Set the elements adjacent to the boundary from the
351 // boundary id information
352 if (boundary_id > 0)
353 {
354 Boundary_element_pt[boundary_id - 1].push_back(elem_pt);
355 // Need to put a shift in here because of an inconsistent naming
356 // convention between triangle and face elements
357 Face_index_at_boundary[boundary_id - 1].push_back((j + 2) % 3);
358
359 // If using regions set up the boundary information
360 if (use_attributes)
361 {
362 unsigned tmp_region =
363 static_cast<unsigned>(Tmp_mesh_pt->element_attribute(e));
364 // Element adjacent to boundary
365 Boundary_region_element_pt[boundary_id - 1][tmp_region].push_back(
366 elem_pt);
367 // Need to put a shift in here because of an inconsistent naming
368 // convention between triangle and face elements
369 Face_index_region_at_boundary[boundary_id - 1][tmp_region]
370 .push_back((j + 2) % 3);
371 }
372 }
373
374 } // end of loop over edges
375 } // end of loop over elements
376
377 // Lookup scheme has now been setup
378 Lookup_for_elements_next_boundary_is_setup = true;
379 }
380
381#ifdef OOMPH_HAS_MPI
382
383 //======================================================================
384 /// Identify the segments from the old mesh (original mesh)
385 /// in the new mesh (this) and assign initial and final boundary
386 /// coordinates for the segments that create the boundary
387 //======================================================================
388 template<class ELEMENT>
391 const unsigned& b, TriangleMesh<ELEMENT>* original_mesh_pt)
392 {
393 // ------------------------------------------------------------------
394 // First: Get the face elements associated with the current boundary
395 // (nonhalo elements only)
396 // ------------------------------------------------------------------
397 // Temporary storage for face elements
399
400 // Temporary storage for number of elements adjacent to the boundary
401 unsigned nele = 0;
402
403 // Temporary storage for elements adjacent to the boundary that have
404 // a common edge (related with internal boundaries)
405 unsigned n_repeated_ele = 0;
406
407 const unsigned n_regions = this->nregion();
408
409 // map to associate the face element to the bulk element, necessary
410 // to attach halo face elements at both sides of each found segment
411 std::map<FiniteElement*, FiniteElement*> face_to_bulk_element_pt;
412
413 // Temporary storage for already done nodes
415
416 // If there is more than one region then only use boundary
417 // coordinates from the bulk side (region 0)
418 if (n_regions > 1)
419 {
420 for (unsigned rr = 0; rr < n_regions; rr++)
421 {
422 const unsigned region_id =
423 static_cast<unsigned>(this->Region_attribute[rr]);
424
425 // Loop over all elements on boundaries in region i_r
426 const unsigned nel_in_region =
427 this->nboundary_element_in_region(b, region_id);
428
429 unsigned nel_repetead_in_region = 0;
430
431 // Only bother to do anything else, if there are elements
432 // associated with the boundary and the current region
433 if (nel_in_region > 0)
434 {
435 // Flag that activates when a repeated face element is found,
436 // possibly because we are dealing with an internal boundary
437 bool repeated = false;
438
439 // Loop over the bulk elements adjacent to boundary b
440 for (unsigned e = 0; e < nel_in_region; e++)
441 {
442 // Get pointer to the bulk element that is adjacent to boundary b
444 this->boundary_element_in_region_pt(b, region_id, e);
445
446#ifdef OOMPH_HAS_MPI
447 // In a distributed mesh only work with nonhalo elements
448 if (this->is_mesh_distributed() && bulk_elem_pt->is_halo())
449 {
450 // Increase the number of repeated elements
452 // Go for the next element
453 continue;
454 }
455#endif
456
457 // Find the index of the face of element e along boundary b
458 int face_index =
459 this->face_index_at_boundary_in_region(b, region_id, e);
460
461 // Before adding the new element we need to be sure that
462 // the edge that this element represent has not been
463 // already added
466
467 const unsigned n_nodes = tmp_ele_pt->nnode();
468
469 std::pair<Node*, Node*> tmp_pair = std::make_pair(
471
472 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
474
475 // Search for repeated nodes
476 const unsigned n_done_nodes = done_nodes_pt.size();
477 for (unsigned l = 0; l < n_done_nodes; l++)
478 {
479 if (tmp_pair == done_nodes_pt[l] ||
481 {
483 repeated = true;
484 break;
485 }
486 }
487
488 // Create new face element
489 if (!repeated)
490 {
491 // Add the pair of nodes (edge) to the node dones
492 done_nodes_pt.push_back(tmp_pair);
493 // Create the map to know if the element is halo
494 face_el_pt.push_back(tmp_ele_pt);
495 // Add the element to the face elements
497 }
498 else
499 {
500 // Clean up
501 delete tmp_ele_pt;
502 tmp_ele_pt = 0;
503 }
504
505 // Re-start
506 repeated = false;
507
508 } // for (e < nel_in_region)
509
511
513
514 } // if (nel_in_region > 0)
515 } // for (rr < n_regions)
516 } // if (n_regions > 1)
517 // Otherwise it's just the normal boundary functions
518 else
519 {
520 // Loop over all elements on boundaries
521 nele = this->nboundary_element(b);
522
523 // Only bother to do anything else, if there are elements
524 if (nele > 0)
525 {
526 // Flag that activates when a repeated face element is found,
527 // possibly because we are dealing with an internal boundary
528 bool repeated = false;
529
530 // Loop over the bulk elements adjacent to boundary b
531 for (unsigned e = 0; e < nele; e++)
532 {
533 // Get pointer to the bulk element that is adjacent to boundary b
534 FiniteElement* bulk_elem_pt = this->boundary_element_pt(b, e);
535
536#ifdef OOMPH_HAS_MPI
537 // In a distributed mesh only work with nonhalo elements
538 if (this->is_mesh_distributed() && bulk_elem_pt->is_halo())
539 {
540 // Increase the number of repeated elements
542 // Go for the next element
543 continue;
544 }
545#endif
546
547 // Find the index of the face of element e along boundary b
548 int face_index = this->face_index_at_boundary(b, e);
549
550 // Before adding the new element we need to be sure that
551 // the edge that this element represents has not been
552 // already added (only applies for internal boundaries)
555
556 const unsigned n_nodes = tmp_ele_pt->nnode();
557
558 std::pair<Node*, Node*> tmp_pair = std::make_pair(
560
561 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
563
564 // Search for repeated nodes
565 const unsigned n_done_nodes = done_nodes_pt.size();
566 for (unsigned l = 0; l < n_done_nodes; l++)
567 {
568 if (tmp_pair == done_nodes_pt[l] ||
570 {
571 // Increase the number of repeated elements
573 // Mark the element as repeated
574 repeated = true;
575 break;
576 }
577 }
578
579 // Create new face element
580 if (!repeated)
581 {
582 // Add the pair of nodes (edge) to the node dones
583 done_nodes_pt.push_back(tmp_pair);
584 // Add the element to the face elements
585 face_el_pt.push_back(tmp_ele_pt);
586 // Create the map to know if the element is halo
588 }
589 else
590 {
591 // Free the repeated bulk element!!
592 delete tmp_ele_pt;
593 tmp_ele_pt = 0;
594 }
595
596 // Re-start
597 repeated = false;
598
599 } // for (e < nel)
600 } // if (nel > 0)
601
602 } // else (n_regions > 1)
603
604 // Do not consider the repeated elements
606
607#ifdef PARANOID
608 if (nele != face_el_pt.size())
609 {
610 std::ostringstream error_message;
611 error_message
612 << "The independent counting of face elements (" << nele << ") for "
613 << "boundary (" << b << ") is different\n"
614 << "from the real number of face elements in the container ("
615 << face_el_pt.size() << ")\n";
616 throw OomphLibError(error_message.str(),
617 "TriangleMesh::identify_boundary_segments_and_assign_"
618 "initial_zeta_values()",
620 }
621#endif
622
623 // Continue even thought there are no elements, the processor needs
624 // to participate in the communications
625
626 // ----------------------------------------------------------------
627 // Second: Sort the face elements, only consider nonhalo elements
628 // ----------------------------------------------------------------
629
630 // A flag vector to mark those face elements that are considered as
631 // halo in the current processor
632 std::vector<bool> is_halo_face_element(nele, false);
633
634 // Count the total number of non halo face elements
635 unsigned nnon_halo_face_elements = 0;
636
637 // We will have halo face elements if the mesh is distributed
638 for (unsigned ie = 0; ie < nele; ie++)
639 {
640 // Get the face element
642 // Get the bulk element
644 // Check if the bulk element is halo
645 if (!tmp_bulk_ele_pt->is_halo())
646 {
647 is_halo_face_element[ie] = false;
649 }
650 else
651 {
652 // Mark the face element as halo
653 is_halo_face_element[ie] = true;
654 }
655 } // for (ie < nele)
656
657#ifdef PARANOID
658 // Get the total number of halo face elements
660 if (nhalo_face_element > 0)
661 {
662 std::ostringstream error_message;
663 error_message
664 << "There should not be halo face elements since they were not "
665 << "considered when computing the face elements\n\n"
666 << "The number of found halo face elements is: " << nhalo_face_element
667 << "\n\n";
668 throw OomphLibError(error_message.str(),
669 "TriangleMesh::identify_boundary_segments_and_assign_"
670 "initial_zeta_values()",
672 }
673#endif
674
675 // The vector of list to store the "segments" that compound the
676 // boundary (segments may appear only in a distributed mesh)
678
679 // Number of already sorted face elements (only nonhalo elements for
680 // a distributed mesh)
681 unsigned nsorted_face_elements = 0;
682
683 // Keep track of who's done (this apply to nonhalo only, remember we
684 // are only working with nonhalo elements)
685 std::map<FiniteElement*, bool> done_el;
686
687 // Keep track of which element is inverted (in distributed mesh the
688 // elements may be inverted with respect to the segment they belong)
689 std::map<FiniteElement*, bool> is_inverted;
690
691 // Iterate until all possible segments have been created
693 {
694 // The ordered list of face elements (in a distributed mesh a
695 // collection of contiguous face elements define a segment)
696 std::list<FiniteElement*> sorted_el_pt;
697 sorted_el_pt.clear();
698
699#ifdef PARANOID
700 // Select an initial element for the segment
701 bool found_initial_face_element = false;
702#endif
703
705
706 unsigned iface = 0;
707 for (iface = 0; iface < nele; iface++)
708 {
710 {
712 // If not done then take it as initial face element
713 if (!done_el[ele_face_pt])
714 {
715#ifdef PARANOID
717#endif
719 iface++; // The next element number
720 sorted_el_pt.push_back(ele_face_pt);
721 // Mark as done
722 done_el[ele_face_pt] = true;
723 break;
724 }
725 }
726 } // for (iface < nele)
727
728#ifdef PARANOID
730 {
731 std::ostringstream error_message;
732 error_message
733 << "Could not find an initial face element for the current segment\n";
734 throw OomphLibError(error_message.str(),
735 "TriangleMesh::identify_boundary_segments_and_"
736 "assign_initial_zeta_values()",
738 }
739#endif
740
741 // Number of nodes
742 const unsigned nnod = ele_face_pt->nnode();
743
744 // Left and right most nodes (the left and right nodes of the
745 // current face element)
748
749 // Continue iterating if a new face element has been added to the
750 // list
751 bool face_element_added = false;
752
753 // While a new face element has been added to the set of sorted
754 // face elements then re-iterate
755 do
756 {
757 // Start from the next face element since we have already added
758 // the previous one as the initial face element (any previous
759 // face element had to be added on previous iterations)
760 for (unsigned iiface = iface; iiface < nele; iiface++)
761 {
762 // Re-start flag
763 face_element_added = false;
764
765 // Get the candidate element
767
768 // Check that the candidate element has not been done and is
769 // not a halo element
771 {
772 // Get the left and right nodes of the current element
775 // New element fits at the left of segment and is not inverted
777 {
779 sorted_el_pt.push_front(ele_face_pt);
780 is_inverted[ele_face_pt] = false;
781 face_element_added = true;
782 }
783 // New element fits at the left of segment and is inverted
785 {
787 sorted_el_pt.push_front(ele_face_pt);
788 is_inverted[ele_face_pt] = true;
789 face_element_added = true;
790 }
791 // New element fits on the right of segment and is not inverted
793 {
795 sorted_el_pt.push_back(ele_face_pt);
796 is_inverted[ele_face_pt] = false;
797 face_element_added = true;
798 }
799 // New element fits on the right of segment and is inverted
801 {
803 sorted_el_pt.push_back(ele_face_pt);
804 is_inverted[ele_face_pt] = true;
805 face_element_added = true;
806 }
807
809 {
810 done_el[ele_face_pt] = true;
812 break;
813 }
814
815 } // if (!(done_el[ele_face_pt] || is_halo_face_element[iiface]))
816 } // for (iiface<nnon_halo_face_element)
817 } while (face_element_added &&
819
820 // Store the created segment in the vector of segments
822
823 } // while(nsorted_face_elements < nnon_halo_face_elements);
824
825 // The number of segments in this processor
826 const unsigned nsegments = segment_sorted_ele_pt.size();
827
828 // ------------------------------------------------------------------
829 // Third: We have the face elements sorted (nonhalo only), now
830 // assign boundary coordinates to the nodes in the segments. This is
831 // the LOCAL boundary coordinate which is required if the zeta
832 // values need to be inverted
833 // ------------------------------------------------------------------
834 // Necessary in case boundaries with no geom object associated need
835 // to be inverted the zeta values (It is necessary to compute the
836 // arclength but also to store the nodes in a container (set))
837 // ------------------------------------------------------------------
838
839 // Vector of sets that stores the nodes of each segment based on a
840 // lexicographically order starting from the bottom left node of
841 // each segment
843
844 // The arclength of each segment in the current processor
846
847 // The number of vertices of each segment
849
850 // The initial zeta for the segment
852
853 // The final zeta for the segment
855
856#ifdef PARANOID
857 if (nnon_halo_face_elements > 0 && nsegments == 0)
858 {
859 std::ostringstream error_message;
860 error_message
861 << "The number of segments is zero, but the number of nonhalo\n"
862 << "elements is: (" << nnon_halo_face_elements << ")\n";
863 throw OomphLibError(error_message.str(),
864 "TriangleMesh::identify_boundary_segments_and_assign_"
865 "initial_zeta_values()",
867 } // if (nnon_halo_face_elements > 0 && nsegments == 0)
868#endif
869
870 // Go through all the segments and compute the LOCAL boundary
871 // coordinates
872 for (unsigned is = 0; is < nsegments; is++)
873 {
874#ifdef PARANOID
875 if (segment_sorted_ele_pt[is].size() == 0)
876 {
877 std::ostringstream error_message;
878 error_message << "The (" << is << ")-th segment has no elements\n";
879 throw OomphLibError(error_message.str(),
880 "TriangleMesh::identify_boundary_segments_and_"
881 "assign_initial_zeta_values()",
883 } // if (segment_sorted_ele_pt[is].size() == 0)
884#endif
885
886 // Get access to the first element on the segment
888
889 // Number of nodes
890 const unsigned nnod = first_ele_pt->nnode();
891
892 // Get the first node of the current segment
895 {
897 }
898
899 // Get access to the last element on the segment
901
902 // Get the last node of the current segment
905 {
907 }
908
909 // Coordinates of left node
910 double x_left = first_node_pt->x(0);
911 double y_left = first_node_pt->x(1);
912
913 // Initialise boundary coordinate (local boundary coordinate for
914 // boundaries with more than one segment)
915 Vector<double> zeta(1, 0.0);
916
917 // If the boundary has an associated GeomObject then it is not
918 // necessary to compute the arclength, only read the values from
919 // the nodes at the edges
920 if (this->boundary_geom_object_pt(b) != 0)
921 {
922 first_node_pt->get_coordinates_on_boundary(b, zeta);
924 last_node_pt->get_coordinates_on_boundary(b, zeta);
926 }
927
928 // Lexicographically bottom left node
929 std::set<Node*> local_nodes_pt;
931
932 // Now loop over nodes in order
933 for (std::list<FiniteElement*>::iterator it =
935 it != segment_sorted_ele_pt[is].end();
936 it++)
937 {
938 // Get element
940
941 // Start node and increment
942 unsigned k_nod = 1;
943 int nod_diff = 1;
944 if (is_inverted[el_pt])
945 {
946 k_nod = nnod - 2;
947 nod_diff = -1;
948 }
949
950 // Loop over nodes
951 for (unsigned j = 1; j < nnod; j++)
952 {
954 k_nod += nod_diff;
955
956 // Coordinates of right node
957 double x_right = nod_pt->x(0);
958 double y_right = nod_pt->x(1);
959
960 // Increment boundary coordinate (the arclength)
961 zeta[0] += sqrt((x_right - x_left) * (x_right - x_left) +
962 (y_right - y_left) * (y_right - y_left));
963
964 // // When we have a GeomObject associated to the boundary we already
965 // // know the zeta values for the nodes, there is no need to compute
966 // // the arclength
967 // if (this->boundary_geom_object_pt(b)==0)
968 // {
969 // // Set boundary coordinate
970 // nod_pt->set_coordinates_on_boundary(b, zeta);
971 // }
972
973 // Increment reference coordinate
974 x_left = x_right;
975 y_left = y_right;
976
977 // Get lexicographically bottom left node but only
978 // use vertex nodes as candidates
979 local_nodes_pt.insert(nod_pt);
980 } // for (j < nnod)
981
982 } // iterator over the elements in the segment
983
984 // Store the arclength of the segment
986
987 // Store the number of vertices in the segment
989
990 // Add the nodes for the corresponding segment in the container
992
993 } // for (is < nsegments)
994
995 // Get the number of sets for nodes
996#ifdef PARANOID
997 if (segment_all_nodes_pt.size() != nsegments)
998 {
999 std::ostringstream error_message;
1000 error_message << "The number of segments (" << nsegments
1001 << ") and the number of "
1002 << "sets of nodes (" << segment_all_nodes_pt.size()
1003 << ") representing\n"
1004 << "the\nsegments is different!!!\n\n";
1005 throw OomphLibError(
1006 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
1007 }
1008#endif
1009
1010 // Store the initial arclength for each segment of boundary in the
1011 // current processor, initalise to zero in case we have a non
1012 // distributed boundary
1014
1015 // Associated the index of the current segment to the segment index
1016 // in the original mesh (input mesh)
1018
1019 // Each segment needs to know whether it has to be inverted or not
1020 // Store whether a segment needs to be inverted or not
1022
1023 // -----------------------------------------------------------------
1024 // Fourth: Identify the segments with the ones in the original mesh
1025 // (has sense only in the adaptation process)
1026 // -----------------------------------------------------------------
1027
1028 // Now check if there are segments associated to this boundary
1029 if (nsegments > 0)
1030 {
1031#ifdef PARANOID
1032 // Double check that the same number of coordinates (nsegments)
1033 // have been established for the boundary
1034 const unsigned nsegments_initial_coordinates =
1035 original_mesh_pt->boundary_segment_initial_coordinate(b).size();
1036
1037 const unsigned nsegments_final_coordinates =
1038 original_mesh_pt->boundary_segment_final_coordinate(b).size();
1039
1041 {
1042 std::stringstream error_message;
1043 error_message
1044 << "The number of segments that present initial coordinates "
1045 << nsegments_initial_coordinates << " is different from "
1046 << "the\nnumber of segments that present final coordinates "
1047 << nsegments_final_coordinates << "\n\n";
1048 throw OomphLibError(error_message.str(),
1051 } // if (nsegments_initial_coordinates!=nsegments_final_coordinates)
1052
1053 // Also check that the number of segments found in the previous
1054 // mesh is the same as the number of segments found in this mesh
1055 if (nsegments_initial_coordinates != nsegments)
1056 {
1057 std::stringstream error_message;
1058 error_message << "Working with boundary (" << b
1059 << ").\n The number of initial and "
1060 << "final coordinates (" << nsegments_initial_coordinates
1061 << ") is different from\n"
1062 << "the number of found segments (" << nsegments
1063 << ").\n\n";
1064 throw OomphLibError(error_message.str(),
1067 } // if (nsegments_initial_coordinates != nsegments)
1068#endif
1069
1070 // Create a backup for the data from the original mesh
1071 // Backup for the coordinates
1073 nsegments);
1075 // Backup for the zeta values
1078 // Backup for the arclengths
1081 // Do the backup
1082 for (unsigned is = 0; is < nsegments; is++)
1083 {
1086 for (unsigned k = 0; k < 2; k++)
1087 {
1089 original_mesh_pt->boundary_segment_initial_coordinate(b)[is][k];
1091 original_mesh_pt->boundary_segment_final_coordinate(b)[is][k];
1092 }
1093 // Check if the boudary has an associated GeomObject
1094 if (this->boundary_geom_object_pt(b) != 0)
1095 {
1097 original_mesh_pt->boundary_segment_initial_zeta(b)[is];
1099 original_mesh_pt->boundary_segment_final_zeta(b)[is];
1100 }
1101 else
1102 {
1104 original_mesh_pt->boundary_segment_initial_arclength(b)[is];
1106 original_mesh_pt->boundary_segment_final_arclength(b)[is];
1107 }
1108 } // for (is < nsegments)
1109
1110 // Clear all the storage
1111 Boundary_segment_inverted[b].clear();
1112 Boundary_segment_initial_coordinate[b].clear();
1113 Boundary_segment_final_coordinate[b].clear();
1114
1115 Boundary_segment_initial_zeta[b].clear();
1116 Boundary_segment_final_zeta[b].clear();
1117
1118 Boundary_segment_initial_arclength[b].clear();
1119 Boundary_segment_final_arclength[b].clear();
1120
1121 // Identify each segment in the processor with the ones created
1122 // by the original mesh
1123 // -----------------------------------------------------------------
1124 // Keep track of the already identified segments
1125 std::map<unsigned, bool> segment_done;
1126 for (unsigned is = 0; is < nsegments; is++)
1127 {
1128#ifdef PARANOID
1129 // Flag to know if the segment was identified
1130 bool found_original_segment = false;
1131#endif
1132
1133 // Get the initial and final coordinates of the current segment
1136
1137 // Get access to the initial element on the segment
1139 segment_sorted_ele_pt[is].front();
1140
1141 // Number of nodes
1142 const unsigned nnod = current_seg_initial_ele_pt->nnode();
1143
1144 // Get the first node of the current segment
1148 {
1151 }
1152
1153 // Get access to the last element on the segment
1155 segment_sorted_ele_pt[is].back();
1156
1157 // Get the last node of the current segment
1161 {
1163 }
1164
1165 // Get the coordinates for the first and last seg node
1166 for (unsigned i = 0; i < 2; i++)
1167 {
1170 }
1171
1172 // We have got the initial and final coordinates of the current
1173 // segment, compare those with the initial and final coordinates
1174 // of the original mesh segments to identify which segments is
1175 // which
1176 for (unsigned orig_s = 0; orig_s < nsegments; orig_s++)
1177 {
1178 if (!segment_done[orig_s])
1179 {
1180 // Get the coordinates to compare
1185
1186 // Compute the distance initial(current)-initial(original)
1187 // coordinates
1188 double dist =
1193 dist = sqrt(dist);
1194
1195 // If the initial node is the same, check for the last node
1197 {
1198 // Compute the distance final(current)-final(original)
1199 // coordinates
1204 dist = sqrt(dist);
1205
1206 // The final node is the same, we have identified the
1207 // segments
1209 {
1210 // Store the index that relates the previous index with the
1211 // current one
1213
1214 // In this case the segment is not inverted
1215 Boundary_segment_inverted[b].push_back(0);
1216
1217 // Copy the initial and final coordinates for each segment
1218 Boundary_segment_initial_coordinate[b].push_back(
1220 Boundary_segment_final_coordinate[b].push_back(
1222
1223 // Check if the boundary has an associated GeomObject
1224 if (this->boundary_geom_object_pt(b) != 0)
1225 {
1226 // Copy the initial zeta value for the segment
1227 Boundary_segment_initial_zeta[b].push_back(
1229 Boundary_segment_final_zeta[b].push_back(
1231 }
1232 else
1233 {
1234 // Copy the initial and final arclength for each
1235 // segment
1236 Boundary_segment_initial_arclength[b].push_back(
1238 Boundary_segment_final_arclength[b].push_back(
1240 }
1241 // Mark the segment as done
1242 segment_done[orig_s] = true;
1243#ifdef PARANOID
1245#endif
1246 break;
1247 } // The final(current) node matched with the
1248 // final(original) node
1249 } // The initial(current) node matched with the
1250 // initial(original) node
1251 else
1252 {
1253 // Check the inverted case Compute the distance
1254 // initial(current)-final(original) coordinates
1255 double dist_inv =
1261
1262 // If the initial node is the same as the final node of
1263 // the segment, check for the last node
1264 if (dist_inv <
1266 {
1267 // Compute the distance final(current)-initial(original)
1268 // coordinates
1269 dist_inv =
1275
1276 // The final node is the same as the initial node, we
1277 // have identified the segments
1278 if (dist_inv <
1280 {
1281 // Store the index that related the previous index with the
1282 // current one
1284
1285 // In this case the segment is inverted
1286 Boundary_segment_inverted[b].push_back(1);
1287
1288 // Copy the initial and final coordinates for each segment
1289 Boundary_segment_initial_coordinate[b].push_back(
1291 Boundary_segment_final_coordinate[b].push_back(
1293
1294 // Check that the boudary has an associated GeomObject
1295 if (this->boundary_geom_object_pt(b) != 0)
1296 {
1297 // Copy the initial zeta value for the segments
1298 Boundary_segment_initial_zeta[b].push_back(
1300 Boundary_segment_final_zeta[b].push_back(
1302 }
1303 else
1304 {
1305 // Copy the initial and final arclength for each segment
1306 Boundary_segment_initial_arclength[b].push_back(
1308 Boundary_segment_final_arclength[b].push_back(
1310 }
1311 // Mark the segment as done
1312 segment_done[orig_s] = true;
1313#ifdef PARANOID
1315#endif
1316 break;
1317 } // The final(current) node matched with the
1318 // initial(original) node
1319 } // The initial(current) node matched with the
1320 // final(original) node
1321 } // else (the first(current) node did not matched with the
1322 // first(original) node. Else do the inverted case
1323
1324 } // (!segment_done[orig_s])
1325
1326 } // (orig_s < nsegments)
1327
1328#ifdef PARANOID
1330 {
1331 std::stringstream error_message;
1332 error_message
1333 << "The (" << is << ")-th segment on the current segment was not\n"
1334 << "found when trying to identify it with the original mesh's\n"
1335 << "segment coordinates\n";
1336 throw OomphLibError(error_message.str(),
1339 } // if (!found_original_segment)
1340#endif
1341 } // for (is < nsegments)
1342
1343 } // if (nsegments > 0)
1344
1345 // -------------------------------------------------------------------
1346 // Fourth: The original mesh is different from the current mesh
1347 // (this). For boundaries with no geom object associated check if it
1348 // is required to reverse the zeta values. In order to reverse the
1349 // zeta values it is required to previously compute the arclength of
1350 // the segments and store the nodes in a container (set). NOTE that
1351 // the setup_boundary_coordinate() method is not called for
1352 // boundaries with NO GeomObject associated, so this is the LAST
1353 // CHANCE to do it
1354 // -------------------------------------------------------------------
1355 // The original mesh is the same as the current mesh (this). The
1356 // setup_boundary_method() will be called only for the boundaries
1357 // with NO GeomObject associated
1358 // -------------------------------------------------------------------
1359 if (this != original_mesh_pt)
1360 {
1361 // Get the boundary arclength
1362
1363 // Get the initial and final zeta values for the boundary
1364 // (arclength) from the original mesh
1366 original_mesh_pt->boundary_initial_zeta_coordinate(b);
1368 original_mesh_pt->boundary_final_zeta_coordinate(b);
1369
1370 // The boundary arclength is the maximum of the initial and final
1371 // zeta coordinate
1372 const double boundary_arclength =
1374
1375 for (unsigned is = 0; is < nsegments; is++)
1376 {
1377 // Here check if need to invert the elements and the boundary
1378 // coordinates for the segments in a boundary with no GeomObject
1379 // associated
1380 if (boundary_geom_object_pt(b) == 0)
1381 {
1382 // This case only applies for the initial and iterative mesh in
1383 // the adaptation process because the method
1384 // setup_boundary_coordinates() is called by the original mesh
1385 // for boundaries with no GeomObject associated
1386
1387 // We are goind to check if it is necessary to invert the order
1388 // of the zeta values
1389
1390 // Get the first and last node of the current segment and their
1391 // zeta values (arclength)
1392
1393 // There is no need to check for nonhalo elements since the
1394 // container has only nonhalo face elements
1395
1396 // Get access to the first element on the segment
1398
1399 // Number of nodes
1400 const unsigned nnod = first_ele_pt->nnode();
1401
1402 // Get the first node of the current segment
1405 {
1407 }
1408
1409 // Get access to the last element on the segment
1411
1412 // Get the last node of the current segment
1415 {
1417 }
1418
1419 // Get the zeta coordinates for the first and last node
1422 // Is the segment in the current mesh (this) inverted?
1423 if (!Boundary_segment_inverted[b][is]) // Not inverted
1424 {
1425 first_node_pt->get_coordinates_on_boundary(
1427 last_node_pt->get_coordinates_on_boundary(
1429 }
1430 else // Inverted
1431 {
1432 first_node_pt->get_coordinates_on_boundary(
1434 last_node_pt->get_coordinates_on_boundary(
1436 }
1437
1438 // Once the zeta values have been obtained check if they are set
1439 // in increasing or decreasing order
1440
1441 // Flag to state that the values in the segment are in increasing
1442 // order
1443 bool increasing_order = false;
1444
1445 // If the initial zeta value is smaller than the final zeta
1446 // value then they are in increasing order
1449 {
1450 increasing_order = true;
1451 }
1452 // If the initial zeta value is greater than the initial zeta
1453 // value then they are in decreasing order
1454 else if (current_segment_initial_arclen[0] >
1456 {
1457 increasing_order = false;
1458 }
1459#ifdef PARANOID
1460 else
1461 {
1462 std::stringstream error_message;
1463 error_message
1464 << "It was not possible to identify if the zeta values on "
1465 << "boundary (" << b << ")\nand segment (" << is
1466 << ") should go in "
1467 << "increasing or decreasing order.\n--- New mesh ---\n"
1468 << "Current segment initial arclength: ("
1469 << current_segment_initial_arclen[0] << ")\n"
1470 << "First node coordinates: (" << first_node_pt->x(0) << ", "
1471 << first_node_pt->x(1) << ")\n"
1472 << "Current segment final arclength: ("
1473 << current_segment_final_arclen[0] << ")\n"
1474 << "Last node coordinates: (" << last_node_pt->x(0) << ", "
1475 << last_node_pt->x(1) << ")\n"
1476 << "Current segment arclength: (" << segment_arclength[is]
1477 << ")\n";
1478 throw OomphLibError(error_message.str(),
1481 }
1482#endif
1483
1484 // Now get the original initial and final arclengths and check
1485 // if they are in increasing or decreasing order
1488 original_mesh_pt->boundary_segment_initial_arclength(b)[prev_s];
1490 original_mesh_pt->boundary_segment_final_arclength(b)[prev_s];
1491
1492 // Flag to check if the values go in increasing or decreasing
1493 // order in the original mesh segment
1494 bool original_increasing_order = false;
1495
1496 // Now check if the arclengths on the original mesh go in
1497 // increase or decrease order, this is also used to choose the
1498 // starting value to map the values in the current segment
1499 double starting_arclength = 0.0;
1502 {
1503 // ... in increasing order in the original mesh ...
1505 // Select the starting arclength
1507 }
1510 {
1511 // ... in decreasing order in the original mesh ...
1513 // Select the starting arclength
1515 }
1516#ifdef PARANOID
1517 else
1518 {
1519 std::stringstream error_message;
1520 error_message
1521 << "It was not possible to identify if the zeta values on "
1522 << "boundary (" << b << ")\nand segment (" << is
1523 << ") should go in "
1524 << "increasing or decreasing order.\n--- Original mesh ---\n"
1525 << "Original segment initial arclength: ("
1527 << "Original segment final arclength: ("
1529 throw OomphLibError(error_message.str(),
1532 }
1533#endif
1534
1535 // Now scale the zeta values based considering if the zeta
1536 // values from the current mesh (this) go in the same order as
1537 // in the original mesh
1539 {
1540 // Current seg
1541 // |------|
1542 // 0 ---- 1
1543 //
1544 // Is mapped to the new values
1545 // |------|
1546 // a ---- b
1547 // a = original_segment_initial_arclength
1548 // b = original_segment_final_arclength
1549 // s = starting_arclength
1550 // The mapping is given by
1551 // new_z = s + z_old * (b - a)
1552
1553 // Get the nodes associated to the segment
1554 std::set<Node*> seg_nodes_pt = segment_all_nodes_pt[is];
1555 // Go through all the nodes in the segment an change their
1556 // zeta values
1557 for (std::set<Node*>::iterator it = seg_nodes_pt.begin();
1558 it != seg_nodes_pt.end();
1559 it++)
1560 {
1561 // Storing for the zeta value
1563 // Get each node
1564 Node* nod_pt = (*it);
1565 // Get the zeta value of the current node
1566 nod_pt->get_coordinates_on_boundary(b, zeta);
1567 // ... and re-assign it
1568 const double temp =
1570 // The zeta value
1572 // Correct
1573 if (std::fabs(zeta[0] - 1.0) < 1.0e-14)
1574 {
1575 zeta[0] = 1.0;
1576 }
1577 else if (std::fabs(zeta[0]) < 1.0e-14)
1578 {
1579 zeta[0] = 0.0;
1580 }
1581
1582 // Set the new value
1583 nod_pt->set_coordinates_on_boundary(b, zeta);
1584 } // Go through all the nodes
1585 } // if (increasing_order && original_increasing_order)
1587 {
1588 // Current seg
1589 // |------|
1590 // 1 ---- 0
1591 //
1592 // Is mapped to the new values
1593 // |------|
1594 // a ---- b
1595 // a = original_segment_initial_arclength
1596 // b = original_segment_final_arclength
1597 // s = starting_arclength
1598 // The mapping is given by
1599 // new_z = s + (1.0 - z_old) * (b - a)
1600
1601 // Get the nodes associated to the segment
1602 std::set<Node*> seg_nodes_pt = segment_all_nodes_pt[is];
1603 // Go through all the nodes in the segment an change their
1604 // zeta values
1605 for (std::set<Node*>::iterator it = seg_nodes_pt.begin();
1606 it != seg_nodes_pt.end();
1607 it++)
1608 {
1609 // Storing for the zeta value
1611 // Get each node
1612 Node* nod_pt = (*it);
1613 // Get the zeta value of the current node
1614 nod_pt->get_coordinates_on_boundary(b, zeta);
1615 // ... and re-assign it
1616 const double temp =
1617 starting_arclength + ((1.0 - zeta[0]) * segment_arclength[is]);
1618 // The zeta value
1620 // Correct
1621 if (std::fabs(zeta[0] - 1.0) < 1.0e-14)
1622 {
1623 zeta[0] = 1.0;
1624 }
1625 else if (std::fabs(zeta[0]) < 1.0e-14)
1626 {
1627 zeta[0] = 0.0;
1628 }
1629 // Set the new value
1630 nod_pt->set_coordinates_on_boundary(b, zeta);
1631 } // Go through all the nodes
1632 } // else if (!increasing_order && original_increasing_order)
1634 {
1635 // Current seg
1636 // |------|
1637 // 0 ---- 1
1638 //
1639 // Is mapped to the new values
1640 // |------|
1641 // b ---- a
1642 // a = original_segment_initial_arclength
1643 // b = original_segment_final_arclength
1644 // s = starting_arclength
1645 // The mapping is given by
1646 // new_z = s + (1.0 - z_old) * |(b - a)|
1647
1648 // Get the nodes associated to the segment
1649 std::set<Node*> seg_nodes_pt = segment_all_nodes_pt[is];
1650 // Go through all the nodes in the segment an change their
1651 // zeta values
1652 for (std::set<Node*>::iterator it = seg_nodes_pt.begin();
1653 it != seg_nodes_pt.end();
1654 it++)
1655 {
1656 // Storing for the zeta value
1658 // Get each node
1659 Node* nod_pt = (*it);
1660 // Get the zeta value of the current node
1661 nod_pt->get_coordinates_on_boundary(b, zeta);
1662 // ... and re-assign it
1663 const double temp =
1664 starting_arclength + ((1.0 - zeta[0]) * segment_arclength[is]);
1665 // The zeta value
1667 // Correct
1668 if (std::fabs(zeta[0] - 1.0) < 1.0e-14)
1669 {
1670 zeta[0] = 1.0;
1671 }
1672 else if (std::fabs(zeta[0]) < 1.0e-14)
1673 {
1674 zeta[0] = 0.0;
1675 }
1676 // Set the new value
1677 nod_pt->set_coordinates_on_boundary(b, zeta);
1678 } // Go through all the nodes
1679 } // else if (increasing_order && !original_increasing_order)
1681 {
1682 // Current seg
1683 // |------|
1684 // 0 ---- 1
1685 //
1686 // Is mapped to the new values
1687 // |------|
1688 // a ---- b
1689 // a = original_segment_initial_arclength
1690 // b = original_segment_final_arclength
1691 // s = starting_arclength
1692 // The mapping is given by
1693 // new_z = s + z_old * |(b - a)|
1694
1695 // Get the nodes associated to the segment
1696 std::set<Node*> seg_nodes_pt = segment_all_nodes_pt[is];
1697 // Go through all the nodes in the segment an change their
1698 // zeta values
1699 for (std::set<Node*>::iterator it = seg_nodes_pt.begin();
1700 it != seg_nodes_pt.end();
1701 it++)
1702 {
1703 // Storing for the zeta value
1705 // Get each node
1706 Node* nod_pt = (*it);
1707 // Get the zeta value of the current node
1708 nod_pt->get_coordinates_on_boundary(b, zeta);
1709 // ... and re-assign it
1710 const double temp =
1712 // The zeta value
1714 // Correct
1715 if (std::fabs(zeta[0] - 1.0) < 1.0e-14)
1716 {
1717 zeta[0] = 1.0;
1718 }
1719 else if (std::fabs(zeta[0]) < 1.0e-14)
1720 {
1721 zeta[0] = 0.0;
1722 }
1723 // Set the new value
1724 nod_pt->set_coordinates_on_boundary(b, zeta);
1725 } // Go through all the nodes
1726 } // else if (!increasing_order && !original_increasing_order)
1727
1728#ifdef PARANOID
1729 // Verify that the z values of the first and last node are not
1730 // out of the range [0,1]
1731 for (std::list<FiniteElement*>::iterator it_list =
1734 it_list++)
1735 {
1736 // Number of nodes in the segment
1737 const unsigned nnod = (*it_list)->nnode();
1738
1739 // Get the first node of the current segment
1740 Node* first_node_pt = (*it_list)->node_pt(0);
1741 if (is_inverted[(*it_list)])
1742 {
1743 first_node_pt = (*it_list)->node_pt(nnod - 1);
1744 }
1745
1746 // Get the last node of the current segment
1747 Node* last_node_pt = (*it_list)->node_pt(nnod - 1);
1748 if (is_inverted[(*it_list)])
1749 {
1750 last_node_pt = (*it_list)->node_pt(0);
1751 }
1752
1753 // The z value for the first node
1755 first_node_pt->get_coordinates_on_boundary(b, zeta);
1756 if (zeta[0] < 0.0 || zeta[0] > 1.0)
1757 {
1758 std::ostringstream error_message;
1759 error_message
1760 << "The boundary coordinate of the first node on boundary ("
1761 << b << ")\nand segment (" << is << ") is out of the "
1762 << "allowed values [0,1]\n"
1763 << "The node boundary coordinate: (" << zeta[0] << ")\n"
1764 << "The vertex coordinates are: (" << first_node_pt->x(0)
1765 << ", " << first_node_pt->x(1) << ")\n";
1766 throw OomphLibError(error_message.str(),
1769 }
1770
1771 // The z value for the last node
1772 last_node_pt->get_coordinates_on_boundary(b, zeta);
1773 if (zeta[0] < 0.0 || zeta[0] > 1.0)
1774 {
1775 std::ostringstream error_message;
1776 error_message
1777 << "The boundary coordinate of the last node on boundary (" << b
1778 << ")\nand segment (" << is << ") is out of the "
1779 << "allowed values [0,1]\n"
1780 << "The node boundary coordinate: (" << zeta[0] << ")\n"
1781 << "The vertex coordinates are: (" << last_node_pt->x(0) << ", "
1782 << last_node_pt->x(1) << ")\n";
1783 throw OomphLibError(error_message.str(),
1786 }
1787 }
1788#endif // #ifdef PARANOID
1789
1790 } // if (boundary_geom_object_pt(b)==0)
1791
1792 } // for (is < nsegments)
1793
1794 } // if (this != original_mesh_pt)
1795
1796 // ------------------------------------------------------------------
1797 // Copy the corrected (possible reversed) info. to the containers of
1798 // the current mesh
1799 // ------------------------------------------------------------------
1800 // Check if there are segments of b boundary in this processor
1801 if (nsegments > 0)
1802 {
1803 // Copy the initial and final coordinates
1804 Boundary_initial_coordinate[b] =
1805 original_mesh_pt->boundary_initial_coordinate(b);
1806
1807 Boundary_final_coordinate[b] =
1808 original_mesh_pt->boundary_final_coordinate(b);
1809
1810 // The initial and final zeta coordinates (In case of a geometric
1811 // object those are the limits of the geom object)
1812 Boundary_initial_zeta_coordinate[b] =
1813 original_mesh_pt->boundary_initial_zeta_coordinate(b);
1814
1815 Boundary_final_zeta_coordinate[b] =
1816 original_mesh_pt->boundary_final_zeta_coordinate(b);
1817
1818 } // if (nsegments > 0)
1819
1820 // Set the flag to indicate that the zeta values have been assigned
1821 // for the current boundary
1822 Assigned_segments_initial_zeta_values[b] = true;
1823
1824 // Clean all the created face elements
1825 for (unsigned i = 0; i < nele; i++)
1826 {
1827 delete face_el_pt[i];
1828 face_el_pt[i] = 0;
1829 }
1830 }
1831
1832 //======================================================================
1833 /// Compute the boundary segments connectivity for those
1834 /// boundaries that were splited during the distribution process
1835 /// and also the initial zeta values for each segment (the initial
1836 /// and final boundary nodes coordinates)
1837 //======================================================================
1838 template<class ELEMENT>
1841 const unsigned& b)
1842 {
1843 // ------------------------------------------------------------------
1844 // First: Get the face elements associated with the current boundary
1845 // ------------------------------------------------------------------
1846
1847 // Get the communicator of the mesh
1848 OomphCommunicator* comm_pt = this->communicator_pt();
1849
1850 // Get the number of processors
1851 const unsigned nproc = comm_pt->nproc();
1852 // Get the rank of the current processor
1853 const unsigned my_rank = comm_pt->my_rank();
1854
1855 // Temporary storage for face elements
1857
1858 // Flag to know whether we are working with an internal open curve
1859 // and then re-assign the initial and final zeta coordinates for
1860 // each segment (only used when the mesh is distributed)
1861 bool is_internal_boundary = false;
1862
1863 // map to associate the face element to the bulk element, necessary
1864 // to attach halo face elements at both sides of each found segment
1865 std::map<FiniteElement*, FiniteElement*> face_to_bulk_element_pt;
1866
1867 // Select the boundary face elements, using the criteria of highest
1868 // processor in charge and bottom-left element
1869 select_boundary_face_elements(
1871
1872 // Get the number of face elements
1873 const unsigned n_all_face_ele = all_face_ele_pt.size();
1874
1875 // ----------------------------------------------------------------
1876 // Second: Sort the face elements, only consider nonhalo elements
1877 // ----------------------------------------------------------------
1878
1879 // A flag vector to mark those face elements that are considered as
1880 // halo in the current processor
1881 std::vector<bool> is_halo_face_element(n_all_face_ele, false);
1882
1883 // Count the total number of non halo face elements
1884 unsigned nnon_halo_face_elements = 0;
1885
1886 // Only mark the face elements as halo if the mesh is marked as
1887 // distributed
1888 for (unsigned ie = 0; ie < n_all_face_ele; ie++)
1889 {
1891 // Get the bulk element
1893 // Check if the bulk element is halo
1894 if (!tmp_bulk_ele_pt->is_halo())
1895 {
1896 // Set the flag for non halo element
1897 is_halo_face_element[ie] = false;
1898 // Increase the non halo elements counter
1900 }
1901 else
1902 {
1903 // Mark the face element as halo
1904 is_halo_face_element[ie] = true;
1905 }
1906
1907 } // for (ie < n_ele)
1908
1909 // Get the total number of halo face elements
1910 const unsigned nhalo_face_element =
1912
1913 // The vector of list to store the "segments" that compound the
1914 // boundary (segments may appear only in a distributed mesh)
1916
1917 // Number of already sorted face elements (only nonhalo elements for
1918 // a distributed mesh)
1919 unsigned nsorted_face_elements = 0;
1920
1921 // Keep track of who's done (this apply to nonhalo only, remember we
1922 // are only working with halo elements)
1923 std::map<FiniteElement*, bool> done_el;
1924
1925 // Keep track of which element is inverted (in distributed mesh the
1926 // elements may be inverted with respect to the segment they belong)
1927 std::map<FiniteElement*, bool> is_inverted;
1928
1929 // Iterate until all possible segments have been created
1931 {
1932 // The ordered list of face elements (in a distributed mesh a
1933 // collection of contiguous face elements define a segment)
1934 std::list<FiniteElement*> sorted_el_pt;
1935 sorted_el_pt.clear();
1936
1937#ifdef PARANOID
1938 // Select an initial element for the segment (the first not done
1939 // nonhalo element)
1940 bool found_initial_face_element = false;
1941#endif
1942
1944
1945 unsigned iface = 0;
1946 for (iface = 0; iface < n_all_face_ele; iface++)
1947 {
1949 {
1951 // If not done then take it as initial face element
1952 if (!done_el[ele_face_pt])
1953 {
1954#ifdef PARANOID
1956#endif
1958 iface++; // The next element number
1959 sorted_el_pt.push_back(ele_face_pt);
1960 // Mark as done
1961 done_el[ele_face_pt] = true;
1962 break;
1963 }
1964 }
1965 } // for (iface < nele)
1966
1967#ifdef PARANOID
1969 {
1970 std::ostringstream error_message;
1971 error_message
1972 << "Could not find an initial face element for the current segment\n";
1973 // << "----- Possible memory leak -----\n";
1974 throw OomphLibError(error_message.str(),
1977 }
1978#endif
1979
1980 // Number of nodes
1981 const unsigned nnod = ele_face_pt->nnode();
1982
1983 // Left and rightmost nodes (the left and right nodes of the
1984 // current face element)
1987
1988 // Continue iterating if a new face element has been added to the
1989 // list
1990 bool face_element_added = false;
1991
1992 // While a new face element has been added to the set of sorted
1993 // face elements then re-iterate
1994 do
1995 {
1996 // Start from the next face element since we have already added
1997 // the previous one as the initial face element (any previous
1998 // face element had to be added on previous iterations)
1999 for (unsigned iiface = iface; iiface < n_all_face_ele; iiface++)
2000 {
2001 // Re-start flag
2002 face_element_added = false;
2003
2004 // Get the candidate element
2006
2007 // Check that the candidate element has not been done and is
2008 // not a halo element
2010 {
2011 // Get the left and right nodes of the current element
2014
2015 // New element fits at the left of segment and is not inverted
2017 {
2019 sorted_el_pt.push_front(ele_face_pt);
2020 is_inverted[ele_face_pt] = false;
2021 face_element_added = true;
2022 }
2023 // New element fits at the left of segment and is inverted
2024 else if (left_node_pt == local_left_node_pt)
2025 {
2027 sorted_el_pt.push_front(ele_face_pt);
2028 is_inverted[ele_face_pt] = true;
2029 face_element_added = true;
2030 }
2031 // New element fits on the right of segment and is not inverted
2033 {
2035 sorted_el_pt.push_back(ele_face_pt);
2036 is_inverted[ele_face_pt] = false;
2037 face_element_added = true;
2038 }
2039 // New element fits on the right of segment and is inverted
2041 {
2043 sorted_el_pt.push_back(ele_face_pt);
2044 is_inverted[ele_face_pt] = true;
2045 face_element_added = true;
2046 }
2047
2049 {
2050 done_el[ele_face_pt] = true;
2052 break;
2053 }
2054
2055 } // if (!(done_el[ele_face_pt] || is_halo_face_element[iiface]))
2056 } // for (iiface<nnon_halo_face_element)
2057 } while (face_element_added &&
2059
2060 // Store the created segment in the vector of segments
2062
2063 } // while(nsorted_face_elements < nnon_halo_face_elements);
2064
2065 // -----------------------------------------------------------------
2066 // Third: We have the face elements sorted (in segments), now assign
2067 // boundary coordinates to the nodes in the segments, this is the
2068 // LOCAL boundary coordinate and further communication is needed to
2069 // compute the GLOBAL boundary coordinates
2070 // -----------------------------------------------------------------
2071
2072 // Vector of sets that stores the nodes of each segment based on a
2073 // lexicographically order starting from the bottom left node of
2074 // each segment
2076
2077 // The number of segments in this processor
2078 const unsigned nsegments = segment_sorted_ele_pt.size();
2079 // DEBP(nsegments);
2080
2081#ifdef PARANOID
2082 if (nnon_halo_face_elements > 0 && nsegments == 0)
2083 {
2084 std::ostringstream error_message;
2085 error_message
2086 << "The number of segments is zero, but the number of nonhalo\n"
2087 << "elements is: (" << nnon_halo_face_elements << ")\n";
2088 throw OomphLibError(
2089 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
2090 } // if (nnon_halo_face_elements > 0 && nsegments == 0)
2091#endif
2092
2093 // The arclength of each segment in the current processor
2095
2096 // The number of vertices of each segment
2098
2099 // The initial zeta for the segment
2101
2102 // The final zeta for the segment
2104
2105 // Go through all the segments and compute its ARCLENGTH (if the
2106 // boundary has a GeomObject associated then assign the initial and
2107 // final zeta values for the segment)
2108 for (unsigned is = 0; is < nsegments; is++)
2109 {
2110#ifdef PARANOID
2111 if (segment_sorted_ele_pt[is].size() == 0)
2112 {
2113 std::ostringstream error_message;
2114 error_message << "The (" << is << ")-th segment has no elements\n";
2115 throw OomphLibError(error_message.str(),
2118 } // if (segment_sorted_ele_pt[is].size() == 0)
2119#endif
2120
2121 // Get access to the first element on the segment
2123
2124 // Number of nodes
2125 const unsigned nnod = first_ele_pt->nnode();
2126
2127 // Get the first node of the current segment
2130 {
2132 }
2133
2134 // Coordinates of left node
2135 double x_left = first_node_pt->x(0);
2136 double y_left = first_node_pt->x(1);
2137
2138 // Initialise boundary coordinate (local boundary coordinate for
2139 // boundaries with more than one segment)
2140 Vector<double> zeta(1, 0.0);
2141
2142 // If we have associated a GeomObject then it is not necessary to
2143 // compute the arclength, only read the values from the nodes at
2144 // the edges and set the initial and final zeta segment values
2145 if (this->boundary_geom_object_pt(b) != 0)
2146 {
2147 // Get the initial node coordinate
2148 first_node_pt->get_coordinates_on_boundary(b, zeta);
2149 // Set the initial zeta segment value
2151
2152 // Get access to the last element on the segment
2154
2155 // Get the last node of the current segment
2158 {
2160 }
2161
2162 // Get the final node coordinate
2163 last_node_pt->get_coordinates_on_boundary(b, zeta);
2164 // Set the final zeta segment value
2166 }
2167
2168 // Sort the nodes in the segment (lexicographically bottom left
2169 // node)
2170 std::set<Node*> local_nodes_pt;
2171 // Insert the first node
2173
2174 // Now loop over nodes in order and increase the ARCLENGTH
2175 for (std::list<FiniteElement*>::iterator it =
2177 it != segment_sorted_ele_pt[is].end();
2178 it++)
2179 {
2180 // Get the pointer to the element
2181 FiniteElement* el_pt = (*it);
2182
2183 // Start node and increment
2184 unsigned k_nod = 1;
2185 int nod_diff = 1;
2186 // Access nodes in reverse?
2187 if (is_inverted[el_pt])
2188 {
2189 k_nod = nnod - 2;
2190 nod_diff = -1;
2191 }
2192
2193 // Loop over nodes in the face element
2194 for (unsigned j = 1; j < nnod; j++)
2195 {
2197 k_nod += nod_diff;
2198
2199 // Coordinates of right node
2200 double x_right = nod_pt->x(0);
2201 double y_right = nod_pt->x(1);
2202
2203 // Increment boundary coordinate (the arclength)
2204 zeta[0] += sqrt((x_right - x_left) * (x_right - x_left) +
2205 (y_right - y_left) * (y_right - y_left));
2206
2207 // When we have a GeomObject associated to the boundary we already
2208 // know the zeta values for the nodes, there is no need to compute
2209 // the arclength
2210 // if (this->boundary_geom_object_pt(b)==0)
2211 // {
2212 // // Set boundary coordinate
2213 // // nod_pt->set_coordinates_on_boundary(b, zeta);
2214 // }
2215
2216 // Increment reference coordinate
2217 x_left = x_right;
2218 y_left = y_right;
2219
2220 // Get lexicographically bottom left node but only
2221 // use vertex nodes as candidates
2222 local_nodes_pt.insert(nod_pt);
2223
2224 } // for (j < nnod)
2225
2226 } // iterator over the elements in the segment
2227
2228 // Info. to be passed to other processors
2229 // The initial arclength for the segment that goes after this depends
2230 // on the current segment arclength
2232
2233 // Info. to be passed to the other processors
2234 // The initial vertex number for the segment that goes after this
2235 // depends on the current segment vertices number
2237
2238 // Add the nodes for the corresponding segment in the container
2240
2241 // The attaching of the halo elements at both sides of the segments is
2242 // performed only if segments connectivity needs to be computed
2243
2244 } // for (is < nsegments)
2245
2246 // Container to store the number of vertices before each segment,
2247 // initialise to zero in case we have a non distributed boundary
2249
2250 // Store the initial arclength for each segment of boundary in the
2251 // current processor, initalise to zero in case we have a non
2252 // distributed boundary
2254
2255 // Info. to be passed to other processors
2256 // If the boundary is distributed we need to know which processors does
2257 // have the initial and final segments, this helps to get the first and
2258 // last nodes coordinates (info. used to scale the bound coordinates)
2259
2260 // Processors with the initial and final segment
2261 unsigned proc_with_initial_seg = 0;
2262 unsigned proc_with_final_seg = 0;
2263
2264 // ... and the index of those segments (only of interest in the
2265 // processors that have the initial and final segments)
2266 unsigned initial_segment = 0;
2267 unsigned final_segment = 0;
2268
2269 // Each segment needs to know whether it has to be inverted or not
2270 // Store whether a segment needs to be inverted or not
2272
2273 // Before attaching the halo elements create a copy of the data
2274 // structure without halo elements
2276 for (unsigned is = 0; is < nsegments; is++)
2277 {
2278 for (std::list<FiniteElement*>::iterator it_seg =
2281 it_seg++)
2282 {
2284 }
2285
2286 } // for (is < nsegments)
2287
2288 // --------------------------------------------------------------
2289 // Attach the halo elements at both sides of the segments
2290 for (unsigned is = 0; is < nsegments; is++)
2291 {
2292 // Get access to the first element on the segment
2294
2295 // Number of nodes
2296 const unsigned nnod = first_ele_pt->nnode();
2297
2298 // Get the first node of the current segment
2301 {
2303 }
2304
2305 // Get access to the last element on the segment
2307
2308 // Get the last node of the current segment
2311 {
2313 }
2314
2315 // -----------------------------------------------------------------
2316 // Fourth: Now attach the halo elements to the left and right side
2317 // of each segment
2318 // -----------------------------------------------------------------
2319 bool attached_left_halo = false;
2320 bool attached_right_halo = false;
2321 if (nhalo_face_element > 0)
2322 {
2323 for (unsigned iiface = 0; iiface < n_all_face_ele; iiface++)
2324 {
2325 // Get the candidate element
2327
2328 // Check that the element is a halo face element, we do not check
2329 // if the element has been already done since the halo elements
2330 // may be connected to more than one segment (2 at most), to the
2331 // left and right of different segments
2332 //
2333 // Segment k Halo Segment r
2334 // |---|---|---| |xxx| |---|---|---|
2335 //
2336 // Segment k Halo Segment r
2337 // |---|---|---|xxx|---|---|---|
2338 //
2340 {
2341 // Get its left and right nodes
2344 // The halo element fits to the left of segment
2347 {
2348 // Add the halo element to the left of the segment
2350
2351 // Once a halo face element has been added to the left
2352 // mark as found halo to the left
2353 attached_left_halo = true;
2354 }
2355 // The halo element fits to the right of the segment
2358 {
2359 // Add the halo element to the right of the segment
2361 // Once a halo face element has been added to the right
2362 // mark as found halo to the right
2363 attached_right_halo = true;
2364 }
2365 // If we have already found elements to left and right then
2366 // break the loop
2368 {
2369 break;
2370 }
2371
2372 } // if (is_halo_face_element[iiface])
2373
2374 } // for (iiface < nel)
2375
2376 } // if (nhalo_face_element > 0)
2377
2378 } // for (is < nsegments)
2379
2380 // The segments now have local coordinates assigned and halo
2381 // elements attached to them. Store that info. in the corresponding
2382 // data structures and be ready to send that info. to a root
2383 // processor. The root processor will be in charge of computing the
2384 // boundary coordinates for each segment of the boundary.
2385
2386 // For each segment store the following information
2387 // --------------------------------------------------------------------
2388 // Stores the "rank" of the processor to the left of each segment,
2389 // zero if there is no processor to the left which states that the
2390 // segment is the first one on the boundary
2392
2393 // Stores the "rank" of the processor to the right of each segment,
2394 // zero if there is no processor to the right which states that the
2395 // segment is the last one on the boundary
2397
2398 // The id. of the halo element to the left of the segment, note that
2399 // this info. is not necessary if there is no processor to the left
2400 // of the segment
2402
2403 // The id. of the halo element to the right of the segment, note that
2404 // this info. is not necessary if there is no processor to the right
2405 // of the segment
2407
2408 // The id. of the haloed element to the left of the segment, note that
2409 // this info. is not necessary if there is no processor to the left
2410 // of the segment
2412
2413 // The id. of the haloed element to the right of the segment, note
2414 // that this info. is not necessary if there is no processor to the
2415 // right of the segment
2417
2418 // Go through all the segments and get the info.
2419 for (unsigned is = 0; is < nsegments; is++)
2420 {
2421 // Get access to the left most face element on the segment
2423
2424 // Get the corresponding bulk element and check whether it is a halo
2425 // element or not
2428
2429 // Check if the bulk element is halo
2431 {
2432 // Then store the corresponding info.
2434#ifdef PARANOID
2435 if (left_proc < 0)
2436 {
2437 std::ostringstream error_message;
2438 error_message
2439 << "The current bulk element (left) is marked as halo but "
2440 << "the processor holding\nthe non-halo counterpart is "
2441 << "negative!\n";
2442 throw OomphLibError(error_message.str(),
2445 }
2446#endif
2447 // The processor "rank" to the left
2448 unsigned left_processor = static_cast<unsigned>(left_proc);
2450
2451 // Now get the id of the halo element to the left
2452 GeneralisedElement* left_element_pt = tmp_left_bulk_ele_pt;
2453
2454 // Get the halo elements with left processor
2456 this->halo_element_pt(left_processor);
2457
2458#ifdef PARANOID
2459 // Flag to state that the halo element was found
2460 bool left_halo_element_found = false;
2461#endif
2462
2463 const unsigned n_halo_left = left_halo_element_pt.size();
2464 for (unsigned lh = 0; lh < n_halo_left; lh++)
2465 {
2466 if (left_element_pt == left_halo_element_pt[lh])
2467 {
2469#ifdef PARANOID
2471#endif
2472 break;
2473 }
2474 } // for (lh < n_halo_left)
2475
2476#ifdef PARANOID
2478 {
2479 std::ostringstream error_message;
2480 error_message
2481 << "The current bulk element (left) marked as halo was "
2482 << "not found in the vector of halo\nelements associated "
2483 << "with the (" << left_processor << ") processor.\n\n";
2484 throw OomphLibError(error_message.str(),
2487 } // if (!left_halo_element_found)
2488#endif
2489
2490 // Get the left-most nonhalo element (use the backup list of
2491 // nonhalo elements)
2493
2494 // Get the corresponding bulk element
2496
2497#ifdef PARANOID
2498 // This element should not be marked as halo
2500 {
2501 std::ostringstream error_message;
2502 error_message
2503 << "The bulk element represetation of the left-most nonhalo face\n"
2504 << "element of the current segment (" << is
2505 << ") is marked as halo,\n"
2506 << "but the face element created from it is nonhalo\n";
2507 throw OomphLibError(error_message.str(),
2510 } // if (tmp_left_bulk_ele_pt->is_halo())
2511#endif
2512
2513 // Cast from "FiniteElement*" to "GeneralisedElement*" to be able
2514 // to search in the haloed vector
2515 left_element_pt = tmp_left_bulk_ele_pt;
2516
2517#ifdef PARANOID
2518 // Flag to state that the haloed element was found
2519 bool left_haloed_element_found = false;
2520#endif
2521
2522 // Now get the id for the haloed element to the left, get the
2523 // haloed elements from the processor to the left
2525 this->haloed_element_pt(left_processor);
2526
2527 const unsigned nhaloed_left = left_haloed_element_pt.size();
2528 for (unsigned lhd = 0; lhd < nhaloed_left; lhd++)
2529 {
2530 if (left_element_pt == left_haloed_element_pt[lhd])
2531 {
2533#ifdef PARANOID
2535#endif
2536 break;
2537 }
2538 } // for (lhd < nhaloed_left)
2539
2540#ifdef PARANOID
2542 {
2543 std::ostringstream error_message;
2544 error_message
2545 << "The current bulk element (left) marked as haloed was "
2546 << "not found in the vector of haloed\nelements associated "
2547 << "with processor (" << left_processor << ").\n";
2548 throw OomphLibError(error_message.str(),
2551 }
2552#endif
2553 } // if (tmp_left_bulk_ele_pt->is_halo())
2554 else
2555 {
2556 // If not halo then state the info. to indicate that
2558 // Null this info.
2559 left_halo_element[is] = 0;
2560 // Null this info.
2562 }
2563
2564 // Get access to the right most face element on the segment
2566
2567 // Get the corresponding bulk element and check whether it is
2568 // a halo element or not
2571
2572 // Check if the bulk element is halo
2574 {
2575 // Then store the corresponding info.
2577#ifdef PARANOID
2578 if (right_proc < 0)
2579 {
2580 std::ostringstream error_message;
2581 error_message
2582 << "The current bulk element (right) is marked as halo but "
2583 << "the processor holding\nthe non-halo counterpart is "
2584 << "negative!\n";
2585 throw OomphLibError(error_message.str(),
2586 "TriangleMesh::compute_boundary_segments_"
2587 "connectivity_and_initial_zeta_values()",
2589 }
2590#endif
2591 // The processor "rank" to the right
2592 unsigned right_processor = static_cast<unsigned>(right_proc);
2594
2595 // Now get the id of the halo element to the right
2596 GeneralisedElement* right_element_pt = tmp_right_bulk_ele_pt;
2597
2598 // Get the halo elements with right processor
2600 this->halo_element_pt(right_processor);
2601
2602#ifdef PARANOID
2603 // Flag to state that the halo element was found
2604 bool right_halo_element_found = false;
2605#endif
2606
2607 const unsigned nhalo_right = right_halo_element_pt.size();
2608 for (unsigned rh = 0; rh < nhalo_right; rh++)
2609 {
2610 if (right_element_pt == right_halo_element_pt[rh])
2611 {
2613#ifdef PARANOID
2615#endif
2616 break;
2617 }
2618 } // for (rh < nhalo_right)
2619#ifdef PARANOID
2621 {
2622 std::ostringstream error_message;
2623 error_message
2624 << "The current bulk element (right) marked as halo was not "
2625 << "found in the vector of halo\nelements associated with "
2626 << "the (" << right_processor << ") processor.\n\n";
2627 throw OomphLibError(error_message.str(),
2628 "TriangleMesh::compute_boundary_segments_"
2629 "connectivity_and_initial_zeta_values()",
2631 }
2632#endif
2633
2634 // Get the right-most nonhalo element (use the backup list of
2635 // nonhalo elements)
2637
2638 // Get the corresponding bulk element
2640#ifdef PARANOID
2641 // This element should not be marked as halo
2643 {
2644 std::ostringstream error_message;
2645 error_message
2646 << "The bulk element represetation of the right-most nonhalo face\n"
2647 << "element of the current segment (" << is
2648 << ") is marked as halo,\n"
2649 << "but the face element created from it is nonhalo\n";
2650 throw OomphLibError(error_message.str(),
2651 "TriangleMesh::compute_boundary_segments_"
2652 "connectivity_and_initial_zeta_values()",
2654 } // if (tmp_right_bulk_ele_pt->is_halo())
2655#endif
2656
2657 // Cast from "FiniteElement*" to "GeneralisedElement*" to be able
2658 // to search in the haloed vector
2659 right_element_pt = tmp_right_bulk_ele_pt;
2660
2661#ifdef PARANOID
2662 // Flag to state that the haloed element was found
2663 bool right_haloed_element_found = false;
2664#endif
2665
2666 // Now get the id for the haloed element to the right
2668 this->haloed_element_pt(right_processor);
2669
2670 const unsigned nhaloed_right = right_haloed_element_pt.size();
2671 for (unsigned rhd = 0; rhd < nhaloed_right; rhd++)
2672 {
2673 if (right_element_pt == right_haloed_element_pt[rhd])
2674 {
2676#ifdef PARANOID
2678#endif
2679 break;
2680 }
2681 } // for (rhd < nhaloed_right)
2682
2683#ifdef PARANOID
2685 {
2686 std::ostringstream error_message;
2687 error_message
2688 << "The current bulk element (right) marked as haloed was not "
2689 << "found in the vector of haloed\nelements associated with "
2690 << "the (" << right_processor << ") processor.\n\n";
2691 throw OomphLibError(error_message.str(),
2692 "TriangleMesh::compute_boundary_segments_"
2693 "connectivity_and_initial_zeta_values()",
2695 }
2696#endif
2697
2698 } // if (tmp_right_bulk_ele_pt->is_halo())
2699 else
2700 {
2701 // If not halo then state the info. to indicate that
2703 // Null this info.
2705 // Null this info.
2707 }
2708
2709 } // for (is < nsegments). Used to get the halo info. of the
2710 // segments
2711
2712 // Now we have all the info. to be sent to the root processor and
2713 // compute the correct (global) boundary coordinates for the current
2714 // boundary
2715
2716 // The root processor will be in charge of performing the computing
2717 // of the coordinate values along the boundary, all the other
2718 // processors only send their info. and wait for receiving the new
2719 // starting values for each of its segments
2720
2721 // Choose the root processor
2722 const unsigned root_processor = 0;
2723 // ------------------------------------------------------------------
2724 // Starts the MPI stage
2725
2726 // The root processor receives the number of segments of each
2727 // processor associated to the current boundary
2729 unsigned nsegments_mpi = nsegments;
2731 1,
2734 1,
2737 comm_pt->mpi_comm());
2738
2739 // Package the info. and prepare it to be sent
2740 // For the packaged info. we send 7 data per each segment, the indexes
2741 // are as follow; 0 left proc, 1 right proc, 2 left halo, 3 right
2742 // halo, 4 left haloed, 5 right haloed and 6 for nvertices per
2743 // segment
2744 // The size of the package (unsigned)
2745 const unsigned spu = 7;
2747 for (unsigned is = 0; is < nsegments; is++)
2748 {
2759 }
2760
2761 // How many data will this processor send
2763
2764 // How many data does the root processor will receive from each
2765 // processor
2767 // Total number of data to receive from all processors
2768 unsigned root_nutotal_data_receive = 0;
2769 for (unsigned ip = 0; ip < nproc; ip++)
2770 {
2771 // Compute the number of data the root processor will receive from
2772 // each processor
2774 // Add on the total number of data to receive
2776 }
2777
2778 // Stores and compute the offsets (in root) for the data received
2779 // from each processor
2781 root_uoffsets_receive[0] = 0;
2782 for (unsigned ip = 1; ip < nproc; ip++)
2783 {
2784 // Compute the offset to store the values from each processor
2787 }
2788
2789 // Create at least one entry so we don't get a seg fault below
2791 {
2793 }
2794
2795 // Vector where to receive the info.
2798 if (my_rank != root_processor)
2799 {
2800 // Create at least one entry so we don't get a seg fault below
2802 {
2804 }
2805 } // if (my_rank!=root_processor)
2806
2807 MPI_Gatherv(&flat_packed_unsigned_send_data[0], // Flat package to
2808 // send info. from
2809 // each processor
2810 nudata_to_send, // Total number of data to send from
2811 // each processor
2813 &flat_packed_unsigned_receive_data[0], // Container
2814 // where to
2815 // receive the
2816 // info. from all
2817 // the processors
2818 &root_nudata_to_receive[0], // Number of data to receive
2819 // from each processor
2820 &root_uoffsets_receive[0], // The offset to store the
2821 // info. from each processor
2823 root_processor, // The processor that receives all the
2824 // info.
2825 comm_pt->mpi_comm());
2826
2827 // Clear the flat package to send
2830
2831 // Package the info. and prepare it to be sent
2832 // For the packaged info. we send 1 data per each segment which is
2833 // at the moment the arclength of each segment
2834 // The size of the package
2835 const unsigned spd = 1;
2837 for (unsigned is = 0; is < nsegments; is++)
2838 {
2840 }
2841
2842 // How many data will this processor send
2844 // How many data does the root processor will receive from each
2845 // processor
2847 // Total number of data to receive from all processors
2848 unsigned root_ndtotal_data_receive = 0;
2849 for (unsigned ip = 0; ip < nproc; ip++)
2850 {
2853 }
2854
2855 // Stores and compute the offsets for the data received from each
2856 // processor
2858 root_doffsets_receive[0] = 0;
2859 for (unsigned ip = 1; ip < nproc; ip++)
2860 {
2861 // Compute the offset to store the values from each processor
2864 }
2865
2866 // Create at least one entry so we don't get a seg fault below
2868 {
2870 }
2871
2872 // Vector where to receive the info.
2874 if (my_rank != root_processor)
2875 {
2876 // Create at least one entry so we don't get a seg fault below
2878 {
2880 }
2881 }
2882
2883 MPI_Gatherv(&flat_packed_double_send_data[0], // Flat package to
2884 // send info. from
2885 // each processor
2886 nddata_to_send, // Total number of data to send from
2887 // each processor
2888 MPI_DOUBLE,
2889 &flat_packed_double_receive_data[0], // Container where
2890 // to receive the
2891 // info. from all
2892 // the processors
2893 &root_nddata_to_receive[0], // Number of data to receive
2894 // from each processor
2895 &root_doffsets_receive[0], // The offset to store the
2896 // info. from each processor
2897 MPI_DOUBLE,
2898 root_processor, // The processor that receives all the
2899 // info.
2900 comm_pt->mpi_comm());
2901
2902 // Clear the flat package to send
2905
2906 // The next three containers are only used by the root processor at
2907 // the end of its computations but it is necessary that all the
2908 // processors know them when calling back the info.
2909
2910 // Container that state the initial arclength for each segments
2911 // of each processor
2913
2914 // Container that state the number of vertices before each segment
2915 // in a given processor
2917
2918 // The root processor needs to tell the other processor if it was
2919 // necessary to reverse a segment. Each processor should therefore
2920 // invert the face elements that compose every segment that was
2921 // inverted by the root processor
2923
2924 // Used to store the accumulated arclength, used at the end of
2925 // communications to store the total arclength
2926 double root_accumulated_arclength = 0.0;
2927
2928 // Store the accumulated number of vertices, it means the total number
2929 // of vertices before each segment (counter)
2931
2932 // The root processor is in charge of performing the connections
2933 // of the segments that define the complete boundary
2934 if (my_rank == root_processor)
2935 {
2936 // From the flat packaged received data re-create the data
2937 // structures storing the info. regarding the connectivity of the
2938 // segments, the number of vertices per segment and the local
2939 // arclength of each segment
2940
2941 // Stores the "rank" of the processor to the left of each segment,
2942 // zero if there is no processor to the left which states that the
2943 // segment is the first one on the boundary
2945
2946 // Stores the "rank" of the processor to the right of each segment,
2947 // zero if there is no processor to the right which states that the
2948 // segment is the last one on the boundary
2950
2951 // The id. of the halo element to the left of the segment, note that
2952 // this info. is not necessary if there is no processor to the left
2953 // of the segment or if the processor has no info about the boundary
2955
2956 // The id. of the halo element to the right of the segment, note
2957 // that this info. is not necessary if there is no processor to
2958 // the right of the segment or if the processor has no info about
2959 // the boundary
2961
2962 // The id. of the haloed element to the left of the segment, note
2963 // that this info. is not necessary if there is no processor to
2964 // the left of the segment or if the processor has no info about
2965 // the boundary
2967
2968 // The id. of the haloed element to the right of the segment, note
2969 // that this info. is not necessary if there is no processor to the
2970 // right of the segment or if the processor has no info about the
2971 // boundary
2973
2974 // The number of vertices per segment in each processor
2976
2977 // The arclength of each of the segments in the processors
2979
2980 unsigned ucounter = 0;
2981 unsigned dcounter = 0;
2982 for (unsigned ip = 0; ip < nproc; ip++)
2983 {
2984 // Get the number of segments in the current processor
2986
2993
2994 // Additional info.
2998
2999 // Extract the info. from the BIG package received from all
3000 // processors
3001 for (unsigned is = 0; is < nsegs_iproc; is++)
3002 {
3003 // ------ The flat unsigned package ------
3018
3019 // ------ The flat double package ------
3022 } // for (is < nsegs_iproc)
3023 } // for (ip < nproc)
3024
3025 // Now the root processor has all the info. to find out the
3026 // CONNECTIVITY of the segments in each processor
3027
3028 // Container that stores the info. related with the connectivity
3029 // of the segments of each processor
3032 for (unsigned ip = 0; ip < nproc; ip++)
3033 {
3037 } // for (ip < nprocs)
3038
3039 // In charge of storing the connectivity of the segments, the pair
3040 // indicates the processor and the segment number
3041 std::list<std::pair<unsigned, unsigned>> proc_seg_connectivity;
3042 proc_seg_connectivity.clear();
3043
3044 // Done segments on processor
3045 std::map<std::pair<unsigned, unsigned>, bool> done_segment;
3046
3047 // Take the first segment of the first processor with segments and
3048 // add it to the list of segments
3049 unsigned left_proc = 0;
3050 unsigned right_proc = 0;
3051 unsigned left_seg = 0;
3052 unsigned right_seg = 0;
3053 for (unsigned ip = 0; ip < nproc; ip++)
3054 {
3056 if (nsegs_iproc > 0)
3057 {
3059 right_seg = left_seg = 0;
3060 break; // Break because it is the first processor with at
3061 // least one segment
3062 }
3063 } // for (ip < nproc)
3064
3065 // ... and add it to the list of segments
3066 std::pair<unsigned, unsigned> add_segment =
3067 std::make_pair(left_proc, left_seg);
3068 done_segment[add_segment] = true;
3070
3071 // Flags to indicate when a segment was added to the left or right
3072 // of the current list of segments
3073 bool added_segment_to_the_left = false;
3074 bool added_segment_to_the_right = false;
3075
3076 do // while(added_segment_to_the_left || added_segment_to_the_right)
3077 {
3078 // Read the left-most processor and segment in the list
3079 std::pair<unsigned, unsigned> left_pair = proc_seg_connectivity.front();
3080 left_proc = left_pair.first;
3081 left_seg = left_pair.second;
3082
3083 // Get the processor number to the left of the left-most
3084 // segment in the list
3085 const unsigned new_left_proc =
3087
3088 if (new_left_proc != 0)
3089 {
3090 // Initialise flag
3092 // Get the left halo element id
3093 const unsigned left_halo_id =
3095
3096 // Get the left haloed element id
3097 const unsigned left_haloed_id =
3099
3100 // Go through the segments on the new left processor and look
3101 // for the corresponding left_halo_id in the haloed_ids
3102 const unsigned nsegs_new_left_proc =
3104
3105 for (unsigned ils = 0; ils < nsegs_new_left_proc; ils++)
3106 {
3107 std::pair<unsigned, unsigned> candidate_seg =
3108 std::make_pair(new_left_proc - 1, ils);
3109
3110 // Check that the segment has not been already added
3112 {
3113 // Only consider the segments on new left processor which
3114 // right processor is the current one (left_proc)
3115 const unsigned right_proc_of_new_left_proc =
3117 // Also get the left_proc_of_new_left_proc (in case that it
3118 // be necessary to invert the segment)
3119 const unsigned left_proc_of_new_left_proc =
3121 // Check the not inverted case (to the left and not
3122 // inverted)
3123 if (right_proc_of_new_left_proc != 0 &&
3125 {
3126 // Get the haloed/haloed element id of the current segment
3127 // in the new left processor and compare it to the
3128 // halo/haloed element id of the left_processor
3129 const unsigned right_halo_id =
3131 const unsigned right_haloed_id =
3135 {
3136 // We have a match of the segments (store the segment
3137 // number plus one on the processor to the left)
3139 ils + 1;
3140 // Add the pair to the connectivity list
3143 break;
3144 }
3145 } // if (right_proc_of_new_left_proc-1 == left_proc)
3146
3147 // Check the inverted case (to the left and inverted)
3148 if (left_proc_of_new_left_proc != 0 &&
3150 {
3151 // Get the haloed element id of the current segment
3152 // (inverted version) in the new left processor and
3153 // compare it to the halo element id of the left_processor
3154 const unsigned inv_left_halo_id =
3156 const unsigned inv_left_haloed_id =
3160 {
3161 // We have a match of the segments (store the segment
3162 // number plus one on the processor to the left)
3164 ils + 1;
3165 // Add the pair to the connectivity list
3167
3168 // In addition to the connectivity we need to invert the
3169 // segment (the information)
3170 const unsigned tmp_proc =
3172 const unsigned tmp_halo =
3174 const unsigned tmp_haloed =
3176
3183
3185 tmp_proc;
3188 tmp_haloed;
3189
3190 // ... and mark the segment as inverted in the root
3191 // processor to inform back to the owner processor
3193
3195 break;
3196 }
3197 } // if (left_proc_of_new_left_proc-1 == left_proc)
3198 } // if (!done_segment[candidate_segment])
3199 } // for (ils < nsegs_new_left_proc)
3200
3201#ifdef PARANOID
3203 {
3204 std::ostringstream error_message;
3205 error_message
3206 << "The corresponding processor and segment to the left of "
3207 << "the current left\nmost segment was not found\n";
3208 throw OomphLibError(error_message.str(),
3209 "TriangleMesh::compute_boundary_segments_"
3210 "connectivity_and_initial_zeta_values()",
3212 }
3213#endif
3214 } // if (new_left_proc != 0)
3215 else
3216 {
3217 // No more segments to the left
3219 }
3220
3221 // Read the info. of the right processor and the right segment
3222 std::pair<unsigned, unsigned> right_pair = proc_seg_connectivity.back();
3223 right_proc = right_pair.first;
3224 right_seg = right_pair.second;
3225
3226 // Get the processor number to the right of the right-most
3227 // segment in the list
3228 const unsigned new_right_proc =
3230
3231 if (new_right_proc != 0)
3232 {
3233 // Initialise flag
3235 // Get the right halo element id
3236 const unsigned right_halo_id =
3238
3239 // Get the right halo element id
3240 const unsigned right_haloed_id =
3242
3243 // Go through the segments on the new right processor and look
3244 // for the corresponding right_halo_id in the haloed_ids
3245 const unsigned nsegs_new_right_proc =
3247
3248 for (unsigned irs = 0; irs < nsegs_new_right_proc; irs++)
3249 {
3250 std::pair<unsigned, unsigned> candidate_seg =
3251 std::make_pair(new_right_proc - 1, irs);
3252
3253 // Check that the segment has not been already added
3255 {
3256 // Only consider the segments on new right processor which
3257 // left processor is the current one (right_proc)
3258 const unsigned left_proc_of_new_right_proc =
3260 // Also get the right_proc_of_new_right_proc (in case
3261 // that it be necessary to invert the segment)
3262 const unsigned right_proc_of_new_right_proc =
3264 // Check the not inverted case (to the right and not
3265 // inverted)
3266 if (left_proc_of_new_right_proc != 0 &&
3268 {
3269 // Get the haloed element id of the current segment in the
3270 // new right processor and compare it to the halo element
3271 // id of the right_processor
3272 const unsigned left_halo_id =
3274 const unsigned left_haloed_id =
3276
3279 {
3280 // We have a match of the segments (store the segment
3281 // number plus one on the processor to the right)
3283 irs + 1;
3284 // Add the connectivity information to the list
3287 break;
3288 }
3289 } // if (left_proc_of_new_right_proc-1 == right_proc)
3290
3291 // Check the inverted case (to the right and inverted)
3294 {
3295 // Get the haloed element id of the current segment
3296 // (inverted version) in the new right processor and
3297 // compare it to the halo element id of the
3298 // right_processor
3299 const unsigned inv_right_halo_id =
3301 const unsigned inv_right_haloed_id =
3305 {
3306 // We have a match of the segments (store the segment
3307 // number plus one on the processor to the right)
3309 irs + 1;
3310 // Add the connectivity information to the list
3312 // In addition to the connectivity we need to invert the
3313 // segment
3314 const unsigned tmp_proc =
3316 const unsigned tmp_halo =
3318 const unsigned tmp_haloed =
3320
3327
3329 tmp_proc;
3332 tmp_haloed;
3333
3334 // ... and mark the segment as inverted in the root
3335 // processor to inform back to the owner processor
3337
3339 break;
3340 }
3341 } // if (right_proc_of_new_right_proc-1 == right_proc)
3342 } // if (!done_segment[candidate_segment])
3343 } // for (irs < nsegs_new_left_proc)
3344
3345#ifdef PARANOID
3347 {
3348 std::ostringstream error_message;
3349 error_message
3350 << "The corresponding processor and segment to the right of "
3351 << "the current right\nmost segment was not found\n";
3352 throw OomphLibError(error_message.str(),
3353 "TriangleMesh::compute_boundary_segments_"
3354 "connectivity_and_initial_zeta_values()",
3356 }
3357#endif
3358 } // if (new_right_proc != 0)
3359 else
3360 {
3361 // No more segments to the left
3363 }
3364
3366
3367 // Once we have connected the segments then we can compute the
3368 // initial and final zeta values based on the arclength of each
3369 // individual segment
3370
3371 // Get the total number of segments, which MUST be the same as the
3372 // total number of segments in all processors
3373 const unsigned ntotal_segments = proc_seg_connectivity.size();
3374#ifdef PARANOID
3375 unsigned tmp_total_segments = 0;
3376 for (unsigned ip = 0; ip < nproc; ip++)
3377 {
3379 }
3380
3381 // Check that the total number of segments in all processors is
3382 // the same as the number of segments that form the boundary
3384 {
3385 std::ostringstream error_message;
3386 error_message << "The number of sorted segments (" << ntotal_segments
3387 << ") on "
3388 << "boundary (" << b
3389 << ")\nis different from the total number of "
3390 << "segments (" << tmp_total_segments
3391 << ") in all\nprocessors.\n\n";
3392 throw OomphLibError(error_message.str(),
3395 } // if (ntotal_segments!=tmp_total_segments)
3396#endif
3397
3398 // Now that we know the connectivity of the segments we can
3399 // compute the initial arclength of each segment in the
3400 // processors. Additionally we also get the number of vertices
3401 // before each of the segments. Resize the containers considering
3402 // the number of segments in each processor
3403 for (unsigned ip = 0; ip < nproc; ip++)
3404 {
3408 }
3409
3412
3413 ucounter = 0;
3414 for (std::list<std::pair<unsigned, unsigned>>::iterator it_list =
3415 proc_seg_connectivity.begin();
3417 it_list++)
3418 {
3419 const unsigned iproc = static_cast<unsigned>((*it_list).first);
3420 const unsigned iseg = static_cast<unsigned>((*it_list).second);
3423
3427
3428 // Set the initial zeta value for the segment
3431 // Set the number of vertices before the current segment
3434
3435 // Add the arclength of the segment to the global arclength
3437 // Add the number of vertices to the global number of vertices
3439
3440 // Increase the counter
3441 ucounter++;
3442 } // for (loop over the sorted segments to assigne initial
3443 // arlength and initial number of vertices)
3444
3445 // Increase by one to get the total number of vertices on the
3446 // boundary
3448
3449 // Get the processors with the initial and final segment.
3452 // Also get the corresponding initial and final segment indexes
3453 // (on the initial and final processors)
3454 initial_segment = proc_seg_connectivity.front().second;
3455 final_segment = proc_seg_connectivity.back().second;
3456
3457 } // if (my_rank == root_processor)
3458
3459 // Get the total number of segments
3460 unsigned root_ntotal_segments = 0;
3461 for (unsigned ip = 0; ip < nproc; ip++)
3462 {
3464 }
3465
3466 // Package the info. that will be sent to each processor. For the
3467 // unsigned package we send the number of vertices before each
3468 // segment in each processor and whether it was inverted or not
3469 // Package size
3470 const unsigned rspu = 2;
3473 unsigned ucounter = 0;
3474 // Collect the info. from all the segments in the processors
3475 for (unsigned ip = 0; ip < nproc; ip++)
3476 {
3478 for (unsigned is = 0; is < nsegs_iproc; is++)
3479 {
3484 } // for (is < nsegs_iproc)
3485 } // for (ip < nproc)
3486
3487 // How many data does the root processor will send to each processor
3489 for (unsigned ip = 0; ip < nproc; ip++)
3490 {
3491 // Get the number of data to send to ip processor
3493 }
3494
3495 // Store and compute the offsets for the data sent to each processor
3497 root_uoffsets_send[0] = 0;
3498 for (unsigned ip = 1; ip < nproc; ip++)
3499 {
3500 // Compute the offset to send the values to each processor
3503 }
3504
3505 // Number of data to receive from root
3506 unsigned nutotal_data_receive = nsegments * rspu;
3507
3508 if (my_rank != root_processor)
3509 {
3510 // Create at least one entry so we don't get a seg fault below
3512 {
3514 }
3515 }
3516
3517 // Clear and resize the vector where to receive the info.
3520 // Create at least one entry so we don't get a seg fault below
3522 {
3524 }
3525
3534 comm_pt->mpi_comm());
3535
3536 // Package the info. that will be sent to each processor, for the
3537 // double package we send (one data per segment) the initial
3538 // arclength for each segment
3539 const unsigned rspd = 1;
3542 unsigned dcounter = 0;
3543 // Collect the info. from all the segments in the processors
3544 for (unsigned ip = 0; ip < nproc; ip++)
3545 {
3547 for (unsigned is = 0; is < nsegs_iproc; is++)
3548 {
3551 }
3552 }
3553
3554 // How many data does the root processor will send to each processor
3556 for (unsigned ip = 0; ip < nproc; ip++)
3557 {
3558 // Number of data send to ip processor
3560 }
3561
3562 // Store and compute the offsets for the data sent to each processor
3564 root_doffsets_send[0] = 0;
3565 for (unsigned ip = 1; ip < nproc; ip++)
3566 {
3567 // Compute the offset to send the values to each processor
3570 }
3571
3572 // Number of double data to receive from root
3573 unsigned ndtotal_data_receive = nsegments * rspd;
3574
3575 if (my_rank != root_processor)
3576 {
3577 // Create at least one entry so we don't get a seg fault below
3579 {
3581 }
3582 }
3583
3584 // Clear and resize the vector where to receive the info.
3587 // Create at least one entry so we don't get a seg fault below
3589 {
3591 }
3592
3596 MPI_DOUBLE,
3599 MPI_DOUBLE,
3601 comm_pt->mpi_comm());
3602
3603 // Read if the segments need to be inverted and read the initial
3604 // arclengths
3605 ucounter = 0;
3606 dcounter = 0;
3607
3608 // Read the info. from the flat package and store it in their
3609 // corresponding containers
3610 for (unsigned is = 0; is < nsegments; is++)
3611 {
3612 // The flat unsigned package
3615 // The segment inverted flag
3617 // The flat double package
3620 } // for (is < nsegments)
3621
3622 // Perform two additional communications to get the total number of
3623 // vertices, the processors with the initial and final segments, the
3624 // corresponding initial and final segments ...
3625 const unsigned numore_info = 5;
3627 // Prepare the info ...
3634
3635 // Send the info. to all processors
3640 comm_pt->mpi_comm());
3641
3642 // ... and store the info. in the proper containers
3649
3650 // Do the same for the maximum zeta value
3652 1,
3653 MPI_DOUBLE,
3655 comm_pt->mpi_comm());
3656
3657 // -----------------------------------------------------------------
3658 // Clear the storage to store the data that will be used by the
3659 // setup boundary coordinates method, if we do not perform the
3660 // cleaning then previous data from previous iterations will remain
3661 // there
3662 // -----------------------------------------------------------------
3663 // The info. for the boundary
3664 Boundary_initial_coordinate[b].clear();
3665 Boundary_final_coordinate[b].clear();
3666
3667 Boundary_initial_zeta_coordinate[b].clear();
3668 Boundary_final_zeta_coordinate[b].clear();
3669
3670 // The info. for the segments
3671 Boundary_segment_inverted[b].clear();
3672 Boundary_segment_initial_coordinate[b].clear();
3673 Boundary_segment_final_coordinate[b].clear();
3674
3675 Boundary_segment_initial_zeta[b].clear();
3676 Boundary_segment_final_zeta[b].clear();
3677
3678 Boundary_segment_initial_arclength[b].clear();
3679 Boundary_segment_final_arclength[b].clear();
3680
3681 // Now copy all the info. to the containers to be sent to any other
3682 // mesh (in the adaptation method)
3683 for (unsigned is = 0; is < nsegments; is++)
3684 {
3685 // At this point we can get the initial and final coordinates for
3686 // each segment
3689
3690 // In order to get the first and last coordinates of each segment we
3691 // first need to identify the first and last nonhalo element of each
3692 // segment, and then get the first and last node of the segment
3693
3694 // Get the first nonhalo face element on the segment
3697
3698#ifdef PARANOID
3699 // Check if the face element is nonhalo, it shouldn't, but better
3700 // check
3702 {
3703 std::ostringstream error_message;
3704 error_message << "The first face element in the (" << is
3705 << ")-th segment is halo\n";
3706 throw OomphLibError(error_message.str(),
3707 "TriangleMesh::compute_boundary_segments_"
3708 "connectivity_and_initial_zeta_values()",
3710 } // if (tmp_first_bulk_ele_pt->is_halo())
3711#endif
3712
3713 // Number of nodes
3714 const unsigned nnod = first_seg_ele_pt->nnode();
3715
3716 // Get the first node of the current segment
3719 {
3721 }
3722
3723 // Get the last nonhalo face element on the segment
3725
3726#ifdef PARANOID
3727 // Check if the face element is nonhalo, it shouldn't, but better
3728 // check
3729 if (last_seg_ele_pt->is_halo())
3730 {
3731 std::ostringstream error_message;
3732 error_message << "The last face element in the (" << is
3733 << ")-th segment is halo\n";
3734 throw OomphLibError(error_message.str(),
3735 "TriangleMesh::compute_boundary_segments_"
3736 "connectivity_and_initial_zeta_values()",
3738 } // if (tmp_first_bulk_ele_pt->is_halo())
3739#endif
3740
3741 // Get the last node of the current segment
3744 {
3746 }
3747
3748 // Get the coordinates for the first and last segment's node
3749 for (unsigned i = 0; i < 2; i++)
3750 {
3753 }
3754
3755 // -----------------------------------------------------------------
3756 // Copy the info. if the segment is inverted
3757 Boundary_segment_inverted[b].push_back(segment_inverted[is]);
3758
3759 // Check if the segment is inverted, if that is the case then invert
3760 // the first and last seg. coordinates
3761 if (!segment_inverted[is])
3762 {
3763 // Store the initial and final coordinates that will help to
3764 // identify the segments in the new meshes created from this one
3765 Boundary_segment_initial_coordinate[b].push_back(first_seg_coord);
3766 Boundary_segment_final_coordinate[b].push_back(last_seg_coord);
3767 }
3768 else
3769 {
3770 // Store the initial and final coordinates that will help to
3771 // identify the segments in the new meshes created from this one
3772 // Invert the initial and final coordinates
3773 Boundary_segment_initial_coordinate[b].push_back(last_seg_coord);
3774 Boundary_segment_final_coordinate[b].push_back(first_seg_coord);
3775 }
3776
3777 // Now assign initial and final zeta boundary coordinates for each
3778 // segment
3779 // -----------------------------------------------------------------
3780 // If there is a geom object then
3781 if (boundary_geom_object_pt(b) != 0)
3782 {
3783 // Store the initial and final zeta for the current segments (we
3784 // got this when we assigned arclength to the segments in the
3785 // current processor)
3786 if (segment_inverted[is])
3787 {
3788 Boundary_segment_initial_zeta[b].push_back(final_zeta_segment[is]);
3789 Boundary_segment_final_zeta[b].push_back(initial_zeta_segment[is]);
3790 }
3791 else
3792 {
3793 Boundary_segment_initial_zeta[b].push_back(initial_zeta_segment[is]);
3794 Boundary_segment_final_zeta[b].push_back(final_zeta_segment[is]);
3795 }
3796 } // if (boundary_geom_object_pt(b)!=0)
3797 else
3798 {
3799 // Store the initial arclength and vertices number for the
3800 // current segment
3801 Boundary_segment_initial_arclength[b].push_back(
3803
3804 Boundary_segment_final_arclength[b].push_back(
3806
3807 } // else if (boundary_geom_object_pt(b)!=0)
3808
3809 } // // for (is < nsegments)
3810
3811 // Get the number of segments from the sets of nodes
3812#ifdef PARANOID
3813 if (segment_all_nodes_pt.size() != nsegments)
3814 {
3815 std::ostringstream error_message;
3816 error_message << "The number of segments (" << nsegments
3817 << ") and the number of "
3818 << "set of nodes (" << segment_all_nodes_pt.size()
3819 << ") representing\n"
3820 << "the\nsegments is different!!!\n\n";
3821 throw OomphLibError(error_message.str(),
3822 "TriangleMesh::compute_boundary_segments_"
3823 "connectivity_and_initial_zeta_values()",
3825 }
3826#endif
3827
3828 // The nodes have been assigned arc-length coordinates from one end
3829 // or the other of the connected segment.
3830
3831 // -----------------------------------------------------------------
3832 // If mesh is distributed get the info. regarding the initial and
3833 // final nodes coordinates on the boundary, same as the zeta
3834 // boundary values for those nodes
3835
3836 // Storage for the coordinates of the first and last nodes on the
3837 // boundary
3840
3841 // Storage for the zeta coordinate of the first and last nodes on
3842 // the boundary
3845
3846 // Send three data to all processors, the x[0], x[1] coordinate and
3847 // the zeta coordinate
3848 const unsigned ndtotal_data = 3;
3850
3851 // If the mesh is distributed then check if this processor has the
3852 // initial segment
3854 {
3855 // Stores the firts element of the segment
3857 // Stores the first node of the boundary
3858 Node* first_node_pt = 0;
3859 // Check if the segment is inverted
3861 {
3862 // Get access to the first element on the segment marked as
3863 // initial
3865
3866 // Number of nodes
3867 const unsigned nnod = first_ele_pt->nnode();
3868
3869 // Get the first node of the current segment
3872 {
3874 }
3875 } // if (!segment_inverted[initial_segment])
3876 else
3877 {
3878 // Get access to the first element on the segment marked as
3879 // initial
3881
3882 // Number of nodes
3883 const unsigned nnod = first_ele_pt->nnode();
3884
3885 // Get the first node of the current segment
3888 {
3890 }
3891 } // else if (!segment_inverted[initial_segment])
3892
3893 // Get the coordinates for the first node
3894 for (unsigned i = 0; i < 2; i++)
3895 {
3897 }
3898
3899 // Get the zeta coordinates for the first node
3901 first_node_pt->get_coordinates_on_boundary(b, tmp_zeta);
3902
3903 // If there is a geometric object associated to the boundary then
3904 // further process is necessary
3905 if (this->boundary_geom_object_pt(b) != 0)
3906 {
3907 // tmp_zeta[0] = this->boundary_coordinate_limits(b)[0];
3908 }
3909 else
3910 {
3911 // Check if the initial boundary coordinate is different from
3912 // zero, if that is the case then we need to set it to zero
3913 if (tmp_zeta[0] >= 1.0e-14)
3914 {
3915 tmp_zeta[0] = 0;
3916 }
3917 } // if (this->boundary_geom_object_pt(b)!=0)
3918
3919 // Store the initial zeta value
3921
3922 } // if (my_rank == proc_with_initial_seg)
3923
3924 // All processor receive the info. from the processor that has the
3925 // initial segment
3928 MPI_DOUBLE,
3930 comm_pt->mpi_comm());
3931
3932 // ... and all processor put that info. into the appropriate
3933 // storages
3934 for (unsigned i = 0; i < 2; i++)
3935 {
3937 }
3939
3940 // -----------------------------------------------------------------
3941 // Send three data to all processors, the x[0], x[1] coordinate and
3942 // the zeta coordinate
3944
3945 // If the mesh is distributed then check if this processor has the
3946 // final segment
3948 {
3949 // Get access to the last element on the segment
3951
3952 // Get the last node of the current segment
3953 Node* last_node_pt = 0;
3954
3955 // Check if the segment is inverted
3957 {
3958 // Get access to the last element on the segment marked as
3959 // final
3961
3962 // Number of nodes
3963 const unsigned nnod = last_ele_pt->nnode();
3964
3965 // Get the last node of the current segment
3968 {
3970 }
3971 } // if (!segment_inverted[final_segment])
3972 else
3973 {
3974 // Get access to the first element on the segment marked as
3975 // initial
3977
3978 // Number of nodes
3979 const unsigned nnod = last_ele_pt->nnode();
3980
3981 // Get the first node of the current segment
3984 {
3986 }
3987 } // if (!segment_inverted[final_segment])
3988
3989 // Get the coordinates for the last node
3990 for (unsigned i = 0; i < 2; i++)
3991 {
3993 }
3994
3995 // Get the zeta coordinates for the last node
3997 last_node_pt->get_coordinates_on_boundary(b, tmp_zeta);
3998
3999 // If there is not a geometric object associated to the boundary
4000 // then further process is required
4001 if (this->boundary_geom_object_pt(b) != 0)
4002 {
4003 // Do nothing
4004 } // if (this->boundary_geom_object_pt(b)!=0)
4005 else
4006 {
4007 // Check if the final boundary coordinate is different from
4008 // the boundary arclength, if that is the case then we need
4009 // to set it to the accumulated arclength
4010 if (std::fabs(tmp_zeta[0] - root_accumulated_arclength) >= 1.0e-14)
4011 {
4013 }
4014 } // else if (this->boundary_geom_object_pt(b)!=0)
4015
4016 // Store the final zeta value
4018
4019 } // if (my_rank == proc_with_final_seg)
4020
4021 // All processor receive the info. from the processor that has the
4022 // final segment
4025 MPI_DOUBLE,
4027 comm_pt->mpi_comm());
4028
4029 // All processor receive the info. from the processor that has the
4030 // final segment
4031 for (unsigned i = 0; i < 2; i++)
4032 {
4034 }
4036
4037 // -----------------------------------------------------------------
4038 // Copy the values to the permanent storage
4039 Boundary_initial_coordinate[b] = first_coordinate;
4040 Boundary_final_coordinate[b] = last_coordinate;
4041
4042 Boundary_initial_zeta_coordinate[b] = first_node_zeta_coordinate;
4043 Boundary_final_zeta_coordinate[b] = last_node_zeta_coordinate;
4044
4045 // If we are dealing with an internal boundary then re-assign the
4046 // initial and final zeta values for the segments
4048 {
4049 // Only re-assign zeta values if there are at least one nonhalo
4050 // segment, if all the possible segments are halo then the
4051 // synchronisation method will be in charge of assigning the
4052 // correct boundary coordinates
4053 if (nsegments > 0)
4054 {
4055 // Call the following method to re-construct the segments but
4056 // using only the nonhalo elements, therefore the boundary
4057 // coordinates need to be re-assigned
4058 re_assign_initial_zeta_values_for_internal_boundary(
4060 }
4061
4062 } // if (is_internal_boundary)
4063
4064 // Now identify the boundary segments
4065 if (nsegments > 0)
4066 {
4067 // Identify the boundary segments in the current mesh
4068 // identify_boundary_segments_and_assign_initial_zeta_values(
4069 // b, all_face_ele_pt, is_internal_boundary, face_to_bulk_element_pt);
4070 identify_boundary_segments_and_assign_initial_zeta_values(b, this);
4071 } // if (nsegments > 0)
4072
4073 // Clean all the created face elements
4074 for (unsigned i = 0; i < n_all_face_ele; i++)
4075 {
4076 delete all_face_ele_pt[i];
4077 all_face_ele_pt[i] = 0;
4078 }
4079 }
4080
4081 //======================================================================
4082 /// Re-assign the boundary segments initial zeta (arclength)
4083 /// for those internal boundaries that were splited during the
4084 /// distribution process. Those boundaries that have one face element
4085 /// at each side of the boundary. Here we create the segments only
4086 /// with the nonhalo elements, therefore the boundary coordinates
4087 /// need to be re-assigned to be passed to the new meshes
4088 //======================================================================
4089 template<class ELEMENT>
4092 const unsigned& b,
4093 Vector<std::list<FiniteElement*>>& old_segment_sorted_ele_pt,
4094 std::map<FiniteElement*, bool>& old_is_inverted)
4095 {
4096 // ------------------------------------------------------------------
4097 // First: Get the face elements associated with the current boundary
4098 // Only include nonhalo face elements
4099 // ------------------------------------------------------------------
4100 // Temporary storage for face elements
4102
4103 // Temporary storage for the number of elements adjacent to the
4104 // boundary
4105 unsigned nele = 0;
4106
4107 // Temporary storage for elements adjacent to the boundary that have
4108 // a common edge (related with internal boundaries)
4109 unsigned n_repeated_ele = 0;
4110
4111 const unsigned n_regions = this->nregion();
4112
4113 // Temporary storage for already done nodes
4115
4116 // If there is more than one region then only use boundary
4117 // coordinates from the bulk side (region 0)
4118 if (n_regions > 1)
4119 {
4120 for (unsigned rr = 0; rr < n_regions; rr++)
4121 {
4122 const unsigned region_id =
4123 static_cast<unsigned>(this->Region_attribute[rr]);
4124
4125 // Loop over all elements on boundaries in region i_r
4126 const unsigned nel_in_region =
4127 this->nboundary_element_in_region(b, region_id);
4128
4129 unsigned nel_repetead_in_region = 0;
4130
4131 // Only bother to do anything else, if there are elements
4132 // associated with the boundary and the current region
4133 if (nel_in_region > 0)
4134 {
4135 bool repeated = false;
4136
4137 // Loop over the bulk elements adjacent to boundary b
4138 for (unsigned e = 0; e < nel_in_region; e++)
4139 {
4140 // Get pointer to the bulk element that is adjacent to
4141 // boundary b
4143 this->boundary_element_in_region_pt(b, region_id, e);
4144
4145 // Remember only work with non halo elements
4146 if (bulk_elem_pt->is_halo())
4147 {
4149 continue;
4150 }
4151
4152 // Find the index of the face of element e along boundary b
4153 int face_index =
4154 this->face_index_at_boundary_in_region(b, region_id, e);
4155
4156 // Before adding the new element we need to be sure that the
4157 // edge that this element represent has not been already
4158 // added
4160 new DummyFaceElement<ELEMENT>(bulk_elem_pt, face_index);
4161
4162 const unsigned n_nodes = tmp_ele_pt->nnode();
4163
4164 std::pair<Node*, Node*> tmp_pair = std::make_pair(
4166
4167 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
4169
4170 // Search for repeated nodes
4171 const unsigned repeated_nodes_size = done_nodes_pt.size();
4172 for (unsigned l = 0; l < repeated_nodes_size; l++)
4173 {
4174 if (tmp_pair == done_nodes_pt[l] ||
4176 {
4178 repeated = true;
4179 break;
4180 }
4181 }
4182
4183 // Create new face element
4184 if (!repeated)
4185 {
4186 // Add the pair of nodes (edge) to the node dones
4187 done_nodes_pt.push_back(tmp_pair);
4188 // Add the element to the face elements
4189 face_el_pt.push_back(tmp_ele_pt);
4190 }
4191 else
4192 {
4193 // Clean up
4194 delete tmp_ele_pt;
4195 tmp_ele_pt = 0;
4196 }
4197
4198 // Re-start
4199 repeated = false;
4200
4201 } // for nel
4202
4204
4206
4207 } // if (nel_in_region > 0)
4208 } // for (rr < n_regions)
4209 } // if (n_regions > 1)
4210 // Otherwise it's just the normal boundary functions
4211 else
4212 {
4213 // Loop over all elements on boundaries
4214 nele = this->nboundary_element(b);
4215
4216 // Only bother to do anything else, if there are elements
4217 if (nele > 0)
4218 {
4219 // Check for repeated ones
4220 bool repeated = false;
4221
4222 // Loop over the bulk elements adjacent to boundary b
4223 for (unsigned e = 0; e < nele; e++)
4224 {
4225 // Get pointer to the bulk element that is adjacent to
4226 // boundary b
4227 FiniteElement* bulk_elem_pt = this->boundary_element_pt(b, e);
4228
4229 // Skip the halo elements, they are not included
4230 if (bulk_elem_pt->is_halo())
4231 {
4233 continue;
4234 }
4235
4236 // Find the index of the face of element e along boundary b
4237 int face_index = this->face_index_at_boundary(b, e);
4238
4239 // Before adding the new element we need to be sure that the
4240 // edge that this element represents has not been already
4241 // added (only applies for internal boundaries)
4243 new DummyFaceElement<ELEMENT>(bulk_elem_pt, face_index);
4244
4245 const unsigned n_nodes = tmp_ele_pt->nnode();
4246
4247 std::pair<Node*, Node*> tmp_pair = std::make_pair(
4249
4250 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
4252
4253 // Search for repeated nodes
4254 const unsigned repeated_nodes_size = done_nodes_pt.size();
4255 for (unsigned l = 0; l < repeated_nodes_size; l++)
4256 {
4257 if (tmp_pair == done_nodes_pt[l] ||
4259 {
4260 // Increase the number of repeated elements
4262 // Mark the element as repeated
4263 repeated = true;
4264 break;
4265 }
4266 }
4267
4268 // Create new face element
4269 if (!repeated)
4270 {
4271 // Add the pair of nodes (edge) to the node dones
4272 done_nodes_pt.push_back(tmp_pair);
4273 // Add the element to the face elements
4274 face_el_pt.push_back(tmp_ele_pt);
4275 }
4276 else
4277 {
4278 // Free the repeated bulk element!!
4279 delete tmp_ele_pt;
4280 tmp_ele_pt = 0;
4281 }
4282
4283 // Re-start
4284 repeated = false;
4285
4286 } // for (e < nel)
4287 } // if (nel > 0)
4288
4289 } // else (n_regions > 1)
4290
4291 // Do not consider the repeated elements
4293
4294#ifdef PARANOID
4295 if (nele != face_el_pt.size())
4296 {
4297 std::ostringstream error_message;
4298 error_message
4299 << "The independet counting of face elements (" << nele << ") for "
4300 << "boundary (" << b << ") is different\n"
4301 << "from the real number of face elements in the container ("
4302 << face_el_pt.size() << ")\n";
4303 //<< "Possible memory leak\n"
4304 throw OomphLibError(
4305 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4306 }
4307#endif
4308
4309 // ----------------------------------------------------------------
4310 // Second: Sort the face elements, only consider nonhalo elements
4311 // ----------------------------------------------------------------
4312
4313 // Get the total number of nonhalo face elements
4314 const unsigned nnon_halo_face_elements = face_el_pt.size();
4315
4316 // The vector of list to store the "segments" that compound the
4317 // boundary (segments may appear only in a distributed mesh)
4319
4320 // Number of already sorted face elements
4321 unsigned nsorted_face_elements = 0;
4322
4323 // Keep track of who's done
4324 std::map<FiniteElement*, bool> done_el;
4325
4326 // Keep track of which element is inverted
4327 std::map<FiniteElement*, bool> is_inverted;
4328
4329 // Iterate until all possible segments have been created
4331 {
4332 // The ordered list of face elements (in a distributed mesh a
4333 // collection of contiguous face elements define a segment)
4334 std::list<FiniteElement*> sorted_el_pt;
4335
4336#ifdef PARANOID
4337 // Select an initial element for the segment
4338 bool found_initial_face_element = false;
4339#endif
4340
4342
4343 unsigned iface = 0;
4344 for (iface = 0; iface < nele; iface++)
4345 {
4347 // If not done then take it as initial face element
4348 if (!done_el[ele_face_pt])
4349 {
4350#ifdef PARANOID
4351 // Mark as found the root face element
4353#endif
4354 // Increase the number of sorted face elements
4356 // Increase the counter to mark the position of the next
4357 // element number
4358 iface++;
4359 // Add the face element in the list of sorted face elements
4360 sorted_el_pt.push_back(ele_face_pt);
4361 // Mark as done
4362 done_el[ele_face_pt] = true;
4363 break;
4364 } // if (!done_el[ele_face_pt])
4365 } // for (iface < nele)
4366
4367#ifdef PARANOID
4369 {
4370 std::ostringstream error_message;
4371 error_message
4372 << "Could not find an initial face element for the current segment\n";
4373 throw OomphLibError(
4374 error_message.str(),
4375 "TriangleMesh::re_assign_initial_zeta_values_for_internal_boundary()",
4377 }
4378#endif
4379
4380 // Number of nodes
4381 const unsigned nnod = ele_face_pt->nnode();
4382
4383 // Left and rightmost nodes (the left and right nodes of the
4384 // current face element)
4387
4388 // Continue iterating if a new face element has been added to the
4389 // list
4390 bool face_element_added = false;
4391
4392 // While a new face element has been added to the set of sorted
4393 // face elements then re-iterate
4394 do
4395 {
4396 // Start from the next face element since we have already added
4397 // the previous one as the initial face element (any previous
4398 // face element had to be added on previous iterations)
4399 for (unsigned iiface = iface; iiface < nele; iiface++)
4400 {
4401 // Re-start flag
4402 face_element_added = false;
4403
4404 // Get the candidate element
4406
4407 // Check that the candidate element has not been done and is
4408 // not a halo element
4409 if (!(done_el[ele_face_pt]))
4410 {
4411 // Get the left and right nodes of the current element
4414
4415 // New element fits at the left of segment and is not inverted
4417 {
4419 sorted_el_pt.push_front(ele_face_pt);
4420 is_inverted[ele_face_pt] = false;
4421 face_element_added = true;
4422 }
4423 // New element fits at the left of segment and is inverted
4424 else if (left_node_pt == local_left_node_pt)
4425 {
4427 sorted_el_pt.push_front(ele_face_pt);
4428 is_inverted[ele_face_pt] = true;
4429 face_element_added = true;
4430 }
4431 // New element fits on the right of segment and is not inverted
4433 {
4435 sorted_el_pt.push_back(ele_face_pt);
4436 is_inverted[ele_face_pt] = false;
4437 face_element_added = true;
4438 }
4439 // New element fits on the right of segment and is inverted
4441 {
4443 sorted_el_pt.push_back(ele_face_pt);
4444 is_inverted[ele_face_pt] = true;
4445 face_element_added = true;
4446 }
4447
4449 {
4450 done_el[ele_face_pt] = true;
4452 break;
4453 } // if (face_element_added)
4454
4455 } // if (!(done_el[ele_face_pt]))
4456
4457 } // for (iiface<nnon_halo_face_element)
4458
4459 } while (face_element_added &&
4461
4462 // Store the created segment in the vector of segments
4464
4465 } // while(nsorted_face_elements < nnon_halo_face_elements);
4466
4467 // --------------------------------------------------------------
4468 // Third: We have the face elements sorted, now assign boundary
4469 // coordinates to the nodes in the segments and compute the
4470 // arclength of the segment.
4471 // --------------------------------------------------------------
4472
4473 // The number of segments in this processor
4474 const unsigned nsegments = segment_sorted_ele_pt.size();
4475
4476#ifdef PARANOID
4477 if (nnon_halo_face_elements > 0 && nsegments == 0)
4478 {
4479 std::ostringstream error_message;
4480 error_message
4481 << "The number of segments is zero, but the number of nonhalo\n"
4482 << "elements is: (" << nnon_halo_face_elements << ")\n";
4483 throw OomphLibError(
4484 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4485 } // if (nnon_halo_face_elements > 0 && nsegments == 0)
4486#endif
4487
4488 // Vector of sets that stores the nodes of each segment based on a
4489 // lexicographically order starting from the bottom left node of
4490 // each segment
4492
4493 // Stores the nodes on each segment in the order they appear in the
4494 // face elements
4496
4497 // Associate and arclength to each node on each segment of the
4498 // boundary, the nodes and therefore the arclength come in the same
4499 // order as the face elements
4501
4502 // The arclength of each segment in the current processor
4504
4505 // The number of vertices of each segment
4507
4508 // The initial zeta for the segment
4510
4511 // The final zeta for the segment
4513
4514 // Go through all the segments and compute the LOCAL boundary
4515 // coordinates
4516 for (unsigned is = 0; is < nsegments; is++)
4517 {
4518#ifdef PARANOID
4519 if (segment_sorted_ele_pt[is].size() == 0)
4520 {
4521 std::ostringstream error_message;
4522 error_message << "The (" << is << ")-th segment has no elements\n";
4523 throw OomphLibError(
4524 error_message.str(),
4525 "TriangleMesh::re_assign_initial_zeta_values_for_internal_boundary()",
4527 } // if (segment_sorted_ele_pt[is].size() == 0)
4528#endif
4529
4530 // Get access to the first element on the segment
4532
4533 // Number of nodes
4534 const unsigned nnod = first_ele_pt->nnode();
4535
4536 // Get the first node of the current segment
4539 {
4541 }
4542
4543 // Coordinates of left node
4544 double x_left = first_node_pt->x(0);
4545 double y_left = first_node_pt->x(1);
4546
4547 // Initialise boundary coordinate (local boundary coordinate for
4548 // boundaries with more than one segment)
4549 Vector<double> zeta(1, 0.0);
4550
4551 // If we have associated a GeomObject then it is not necessary
4552 // to compute the arclength, only read the values from the nodes at
4553 // the edges
4554 if (this->boundary_geom_object_pt(b) != 0)
4555 {
4556 first_node_pt->get_coordinates_on_boundary(b, zeta);
4558
4559 // Get access to the last element on the segment
4561
4562 // Get the last node of the current segment
4565 {
4567 }
4568
4569 last_node_pt->get_coordinates_on_boundary(b, zeta);
4571 }
4572
4573 // Sort the nodes in the segment (lexicographically bottom left
4574 // node)
4575 std::set<Node*> local_nodes_pt;
4577
4578 // Associate and arclength to the sorted nodes
4580 sorted_node_arclength.push_back(0.0);
4581
4582 // Sorts the nodes in the segments according their sorting in the
4583 // face elements
4585 sorted_nodes_pt.push_back(first_node_pt);
4586
4587 // Now loop over nodes in order
4588 for (std::list<FiniteElement*>::iterator it =
4590 it != segment_sorted_ele_pt[is].end();
4591 it++)
4592 {
4593 // Get the face element
4595
4596 // Start node and increment
4597 unsigned k_nod = 1;
4598 int nod_diff = 1;
4599 if (is_inverted[el_pt])
4600 {
4601 k_nod = nnod - 2;
4602 nod_diff = -1;
4603 }
4604
4605 // Loop over nodes
4606 for (unsigned j = 1; j < nnod; j++)
4607 {
4609 k_nod += nod_diff;
4610
4611 // Coordinates of right node
4612 double x_right = nod_pt->x(0);
4613 double y_right = nod_pt->x(1);
4614
4615 // Increment boundary coordinate
4616 zeta[0] += sqrt((x_right - x_left) * (x_right - x_left) +
4617 (y_right - y_left) * (y_right - y_left));
4618
4619 // When we have a GeomObject associated to the boundary we already
4620 // know the zeta values for the nodes, there is no need to compute
4621 // the arclength
4622 if (this->boundary_geom_object_pt(b) == 0)
4623 {
4624 // Set boundary coordinate
4625 // nod_pt->set_coordinates_on_boundary(b, zeta);
4626 }
4627
4628 // Increment reference coordinate
4629 x_left = x_right;
4630 y_left = y_right;
4631
4632 // Get lexicographically bottom left node but only
4633 // use vertex nodes as candidates
4634 local_nodes_pt.insert(nod_pt);
4635
4636 // Associate the arclength for the current node
4637 sorted_node_arclength.push_back(zeta[0]);
4638
4639 // Store the node in the sorted nodes storage
4640 sorted_nodes_pt.push_back(nod_pt);
4641
4642 } // for (j < nnod)
4643
4644 } // iterator over the elements in the segment
4645
4646 // Info. to be passed to the other processors
4647 // The initial arclength for the segment that goes after this depends
4648 // on the current segment arclength
4650
4651 // Info. to be passed to the other processors
4652 // The initial vertex number for the segment that goes after this
4653 // depends on the current sement vertices number
4655
4656 // Add the nodes for the corresponding segment in the container
4658
4659 // Add the arclengths to the nodes in the segment
4661
4662 // Add the sorted nodes to the storage
4664
4665 // The attaching of the halo elements at both sides of the segments is
4666 // performed only if segments connectivity needs to be computed
4667
4668 } // for (is < nsegments)
4669
4670 // ------------------------------------------------------------------
4671 // Fourth: Now we have the segments sorted, with arclength and with
4672 // LOCAL boundary coordinates assigned to the nodes. Identify the
4673 // nodes on the segments with the input segments and re-assign all
4674 // the info. related with the identification of segments
4675 // ------------------------------------------------------------------
4676
4677 // Get the number of segments for the old sorted segments
4678 const unsigned old_nsegments = old_segment_sorted_ele_pt.size();
4679
4680 // ------------------------------------------------------------------
4681 // Copy the old info. in temporary storages
4683
4687
4690
4693
4694 // Back-up the information
4695 for (unsigned old_is = 0; old_is < old_nsegments; old_is++)
4696 {
4698 boundary_segment_inverted(b)[old_is];
4699
4702 for (unsigned i = 0; i < 2; i++)
4703 {
4705 boundary_segment_initial_coordinate(b)[old_is][i];
4706
4708 boundary_segment_final_coordinate(b)[old_is][i];
4709 }
4710
4711 // Check if the boundary has an associated GeomObject
4712 if (this->boundary_geom_object_pt(b) != 0)
4713 {
4715 boundary_segment_initial_zeta(b)[old_is];
4716
4718 boundary_segment_final_zeta(b)[old_is];
4719
4720 } // if (this->boundary_geom_object_pt(b)!=0)
4721 else
4722 {
4724 boundary_segment_initial_arclength(b)[old_is];
4725
4727 boundary_segment_final_arclength(b)[old_is];
4728
4729 } // else if (this->boundary_geom_object_pt(b)!=0)
4730
4731 } // for (old_is < old_nsegments)
4732
4733 // ------------------------------------------------------------------
4734 // Now clear the original storages
4735 Boundary_segment_inverted[b].clear();
4736 Boundary_segment_initial_coordinate[b].clear();
4737 Boundary_segment_final_coordinate[b].clear();
4738
4739 Boundary_segment_initial_zeta[b].clear();
4740 Boundary_segment_final_zeta[b].clear();
4741
4742 Boundary_segment_initial_arclength[b].clear();
4743 Boundary_segment_final_arclength[b].clear();
4744 // ------------------------------------------------------------------
4745 // .. and resize the storages for the new number of segments
4746 Boundary_segment_inverted[b].resize(nsegments);
4747 Boundary_segment_initial_coordinate[b].resize(nsegments);
4748 Boundary_segment_final_coordinate[b].resize(nsegments);
4749
4750 // Check if the boundary has an associated GeomObject
4751 if (this->boundary_geom_object_pt(b) != 0)
4752 {
4753 Boundary_segment_initial_zeta[b].resize(nsegments);
4754 Boundary_segment_final_zeta[b].resize(nsegments);
4755 }
4756 else
4757 {
4758 Boundary_segment_initial_arclength[b].resize(nsegments);
4759 Boundary_segment_final_arclength[b].resize(nsegments);
4760 }
4761 // ------------------------------------------------------------------
4762 // map to know if the new segment has been re-assigned the info.
4763 std::map<unsigned, bool> done_segment;
4764
4765 // Count the number of re-assigned segments with the new values
4766 unsigned re_assigned_segments = 0;
4767
4768 // Go through all the old segments (the input segments)
4769 for (unsigned old_is = 0; old_is < old_nsegments; old_is++)
4770 {
4771 // Get the first and last zeta values for the current segment
4772 const double old_initial_arclength =
4774 const double old_final_arclength =
4776 // Get the "is inverted" segment information
4777 const unsigned old_inverted_segment =
4779
4780 // Check if the boundary coordinates in the segment go in
4781 // increasing or decreasing order
4782 bool old_increasing_order = false;
4784 {
4785 old_increasing_order = true;
4786 }
4787
4788 // Now get the first and last node of the current segment
4789 // Get the first element
4792
4793 // Number of nodes
4794 const unsigned nnod = first_old_seg_ele_pt->nnode();
4795
4796 // Get the first node of the current segment
4799 {
4801 }
4802
4803 // Get access to the last element on the segment
4806
4807 // Get the last node of the current segment
4810 {
4812 }
4813 // Check if the segment is inverted, if that is the case then
4814 // also invert the nodes
4816 {
4820 }
4821
4822 // We have the first and last node of the old segment (input
4823 // segment), now identify in which segment, of those with only
4824 // nonhalo face elements, they are
4825 for (unsigned is = 0; is < nsegments; is++)
4826 {
4827 if (!done_segment[is])
4828 {
4829 // Go through the nodes of the current segment and try to find
4830 // the old nodes
4831 bool found_first_old_seg_node = false;
4832 bool found_last_old_seg_node = false;
4833 bool same_order = false;
4834
4835 // Get the first node of the current segment
4839 {
4841 }
4842
4843 // Get the arclength for the first node
4844 const double segment_first_node_zeta =
4846
4847 // Get the node coordinates for the first node
4849 for (unsigned i = 0; i < 2; i++)
4850 {
4852 }
4853
4854 // Get the last node of the current segment
4858 {
4860 }
4861
4862 // Get the arclength for the last node
4864
4865 // Get the node coordinates for the last node
4867 for (unsigned i = 0; i < 2; i++)
4868 {
4870 }
4871
4872 // Temporary storage for the nodes of the current segment
4874 // Get the number of nodes in the segment
4875 const unsigned nsegment_node = segment_node_pt.size();
4876 for (unsigned in = 0; in < nsegment_node; in++)
4877 {
4881 {
4882 // Get the arclength assigned to the node on the old
4883 // segment
4884 const double current_node_zeta =
4886
4887 // Now check if the new segment has the same orientation
4888 // as the old one
4889 if (!found_last_old_seg_node) // has the same orientation
4890 {
4891 // Re-assign the first node coordinates
4892 Boundary_segment_initial_coordinate[b][is] = first_node_coord;
4893
4894 // Check if the boundary has an associated GeomObject
4895 if (this->boundary_geom_object_pt(b) != 0)
4896 {
4897 // Assign the zeta values if the current segment has the
4898 // nodes of the old one
4899
4900 // If we are in the same order then pass the values as
4901 // they are
4902 Boundary_segment_initial_zeta[b][is] =
4904
4905 } // if (this->boundary_geom_object_pt(b)!=0)
4906 else
4907 {
4908 // Get the distance to the first node
4909 const double distance =
4911
4913
4914 // Now check if the zeta values are in increasing order
4916 {
4917 // Substract the distance
4919 }
4920 else
4921 {
4922 // Add the distance
4924 }
4925
4926 // Re-assign the initial arclength for the current segment
4927 Boundary_segment_initial_arclength[b][is] =
4929
4930 } // else if (this->boundary_geom_object_pt(b)!=0)
4931 } // if (!found_last_old_seg_node)
4932 else // has different orientation
4933 {
4934 // Re-assign the first node coordinates
4935 Boundary_segment_initial_coordinate[b][is] = last_node_coord;
4936
4937 // Check if the boundary has an associated GeomObject
4938 if (this->boundary_geom_object_pt(b) != 0)
4939 {
4940 // Assign the zeta values if the current segment has the
4941 // nodes of the old one
4942
4943 // Not the same order, we need to copy the zeta values
4944 // from the other end, the inverted flag is changed at
4945 // the end. Copy the value from the final end
4946 Boundary_segment_initial_zeta[b][is] = final_zeta_segment[is];
4947
4948 } // if (this->boundary_geom_object_pt(b)!=0)
4949 else
4950 {
4951 // Get the distance to the final node
4952 const double distance =
4954
4956
4957 // Now check if the zeta values are in increasing order
4959 {
4960 // Substract the distance
4962 }
4963 else
4964 {
4965 // Add the distance
4967 }
4968
4969 // Re-assign the initial arclength for the current segment
4970 Boundary_segment_initial_arclength[b][is] =
4972
4973 } // else if (this->boundary_geom_object_pt(b)!=0)
4974 } // else if (!found_last_old_seg_node)
4975
4976 // Mark as found the first node
4978 }
4979 // if (!found_first_old_seg_node &&
4980 // first_old_seg_node_pt == current_node_pt)
4981
4982 // If we found first the first node then the segments have
4983 // the same order
4985 {
4986 same_order = true;
4987 }
4988
4991 {
4992 // Get the boundary coordinates assigned to the node on
4993 // the old segment
4994 const double current_node_zeta =
4996
4997 // Now check if the new segment has the same orientation
4998 // as the old one
4999 if (found_first_old_seg_node) // has the same orientation
5000 {
5001 // Re-assign the last node coordinates
5002 Boundary_segment_final_coordinate[b][is] = last_node_coord;
5003
5004 // Check if the boundary has an associated GeomObject
5005 if (this->boundary_geom_object_pt(b) != 0)
5006 {
5007 // Assign the zeta values if the current segment has the
5008 // nodes of the old one
5009
5010 // If we are in the same order then pass the values as
5011 // they are
5012 Boundary_segment_final_zeta[b][is] = final_zeta_segment[is];
5013
5014 } // if (this->boundary_geom_object_pt(b)!=0)
5015 else
5016 {
5017 // Get the distance to the last node
5018 const double distance =
5020
5022
5023 // Now check if the zeta values are in increasing order
5025 {
5026 // Add the distance
5028 }
5029 else
5030 {
5031 // Substract the distance
5033 }
5034
5035 // Re-assign the final arclength for the current segment
5036 Boundary_segment_final_arclength[b][is] = new_final_arclength;
5037
5038 } // else if (this->boundary_geom_object_pt(b)!=0)
5039 } // if (found_first_old_seg_node)
5040 else
5041 {
5042 // Re-assign the last node coordinates
5043 Boundary_segment_final_coordinate[b][is] = first_node_coord;
5044
5045 // Check if the boundary has an associated GeomObject
5046 if (this->boundary_geom_object_pt(b) != 0)
5047 {
5048 // Assign the zeta values if the current segment has the
5049 // nodes of the old one
5050
5051 // Not the same order, we need to copy the zeta values
5052 // from the other end, the inverted flag is changed at
5053 // the end. Copy the value from the initial end
5054 Boundary_segment_final_zeta[b][is] = initial_zeta_segment[is];
5055
5056 } // if (this->boundary_geom_object_pt(b)!=0)
5057 else
5058 {
5059 // Get the distance to the last node
5060 const double distance =
5062
5064
5065 // Now check if the zeta values are in increasing order
5067 {
5068 // Add the distance
5070 }
5071 else
5072 {
5073 // Substract the distance
5075 }
5076
5077 // Re-assign the final arclength for the current segment
5078 Boundary_segment_final_arclength[b][is] = new_final_arclength;
5079
5080 } // else if (this->boundary_geom_object_pt(b)!=0)
5081 } // if (found_first_old_seg_node)
5082
5083 // Mark as found the last node
5085
5086 } // if (!found_last_old_seg_node &&
5087 // last_old_seg_node_pt == current_node_pt)
5088
5089 // If we found the last node first then the segments have
5090 // not the same order
5092 {
5093 same_order = false;
5094 }
5095
5097 {
5098 // Check if necessary to change the information that
5099 // states if a segment is inverted or not
5100 if (same_order)
5101 {
5102 Boundary_segment_inverted[b][is] = old_inverted_segment;
5103 }
5104 else
5105 {
5106 Boundary_segment_inverted[b][is] = !old_inverted_segment;
5107 }
5108
5109 // Mark the segment as done
5110 done_segment[is] = true;
5111
5112 // Increase the number of re-assigned segments
5114
5115 // Break the for that look for the nodes in the segments
5116 break;
5117 }
5118
5119 } // for (in < nsegment_node)
5120
5121#ifdef PARANOID
5124 {
5125 std::stringstream error_message;
5126 error_message
5127 << "Working with boundary (" << b << ").\nOnly the first node or "
5128 << "the last node of the old segment (" << old_is << ") was\n"
5129 << "found. Both, first and last node should have been found in "
5130 << "the same segment!!!.\n"
5131 << "Found first seg node:" << found_first_old_seg_node << "\n"
5132 << "Found last seg node:" << found_last_old_seg_node << "\n\n";
5133 throw OomphLibError(error_message.str(),
5134 "TriangleMesh::re_assign_initial_zeta_values_"
5135 "for_internal_boundary()",
5137 }
5138#endif
5139
5140 } // if (!done_segment[is])
5141 } // for (is < nsegments)
5142 } // for (old_is < old_nsegments)
5143
5144 // For those segments not identified set dummy values, the boundary
5145 // coordinates should be corrected at the synchronisation stage
5146
5147 // loop over the new segments and check if there not identified
5148 // segments
5149 for (unsigned is = 0; is < nsegments; is++)
5150 {
5151 // Was the segment identified
5152 if (!done_segment[is])
5153 {
5154 // Get the first node of the current segment
5156 // Number of nodes
5157 const unsigned nnod = first_seg_ele_pt->nnode();
5158
5161 {
5163 }
5164
5165 // Get the arclength for the first node
5166 const double segment_first_node_zeta =
5168
5169 // Get the node coordinates for the first node
5171 for (unsigned i = 0; i < 2; i++)
5172 {
5174 }
5175
5176 // Get the last node of the current segment
5180 {
5182 }
5183
5184 // Get the arclength for the last node
5186
5187 // Get the node coordinates for the last node
5189 for (unsigned i = 0; i < 2; i++)
5190 {
5192 }
5193
5194 // Re-assign the initial node coordinates
5195 Boundary_segment_initial_coordinate[b][is] = first_node_coord;
5196
5197 // Check if the boundary has an associated GeomObject
5198 if (this->boundary_geom_object_pt(b) != 0)
5199 {
5200 // Assign the zeta values if the current segment has the
5201 // nodes of the old one
5202
5203 // If we are in the same order then pass the values as
5204 // they are
5205 Boundary_segment_initial_zeta[b][is] = initial_zeta_segment[is];
5206
5207 } // if (this->boundary_geom_object_pt(b)!=0)
5208 else
5209 {
5210 // Re-assign the initial arclength for the current segment
5211 Boundary_segment_initial_arclength[b][is] = segment_first_node_zeta;
5212
5213 } // else if (this->boundary_geom_object_pt(b)!=0)
5214
5215 // Re-assign the initial node coordinates
5216 Boundary_segment_final_coordinate[b][is] = last_node_coord;
5217
5218 // Check if the boundary has an associated GeomObject
5219 if (this->boundary_geom_object_pt(b) != 0)
5220 {
5221 // Assign the zeta values if the current segment has the
5222 // nodes of the old one
5223
5224 // If we are in the same order then pass the values as
5225 // they are
5226 Boundary_segment_final_zeta[b][is] = final_zeta_segment[is];
5227
5228 } // if (this->boundary_geom_object_pt(b)!=0)
5229 else
5230 {
5231 // Re-assign the final arclength for the current segment
5232 Boundary_segment_final_arclength[b][is] = segment_final_node_zeta;
5233
5234 } // else if (this->boundary_geom_object_pt(b)!=0)
5235
5236 Boundary_segment_inverted[b][is] = 0;
5237
5238 // Mark the segment as done
5239 done_segment[is] = true;
5240
5241 // Increase the number of re-assigned segments
5243
5244 } // if (!done_segment[is])
5245
5246 } // for (is < nsegments)
5247
5248#ifdef PARANOID
5249 // Compare the number of new segments identified with the old segments
5250 if (re_assigned_segments != nsegments)
5251 {
5252 std::stringstream error_message;
5253 error_message << "Working with boundary (" << b
5254 << ").\nThe number of re-assigned "
5255 << "segments (" << re_assigned_segments
5256 << ") is different from the number\nof segments ("
5257 << nsegments << ")\n\n";
5258 throw OomphLibError(
5259 error_message.str(),
5260 "TriangleMesh::re_assign_initial_zeta_values_for_internal_boundary()",
5262 } // if (re_assigned_segments != nsegments)
5263#endif
5264
5265 // Clean all the created face elements
5266 for (unsigned i = 0; i < nele; i++)
5267 {
5268 delete face_el_pt[i];
5269 face_el_pt[i] = 0;
5270 }
5271 }
5272
5273 /// =====================================================================
5274 /// Select face elements from a given boundary. In case the we are
5275 /// dealing with an internal boundary we use a set of criterias to
5276 /// decide which of the two face elements should be used on represent
5277 /// the internal boundary. We return the face elements, halo or
5278 /// haloed on this processor that form the boundary. The caller method
5279 /// should be in charge of selecting nonhalo elements and deleting the face
5280 /// elements created by this method
5281 /// =====================================================================
5282 template<class ELEMENT>
5285 const unsigned& b,
5287 std::map<FiniteElement*, FiniteElement*>& face_to_bulk_element_pt)
5288 {
5289 // Get the communicator of the mesh
5290 OomphCommunicator* comm_pt = this->communicator_pt();
5291
5292 const unsigned my_rank = comm_pt->my_rank();
5293
5294 // ------------------------------------------------------------------
5295 // 1) Get the face elements associated with the current boundary
5296 // ------------------------------------------------------------------
5297
5298 // Temporary storage for face elements (do not take care of
5299 // repeated face elements)
5301
5302 const unsigned nregions = this->nregion();
5303
5304 // If there is more than one region then only use boundary
5305 // coordinates from the bulk side (region 0)
5306 if (nregions > 1)
5307 {
5308 for (unsigned ir = 0; ir < nregions; ir++)
5309 {
5310 const unsigned region_id =
5311 static_cast<unsigned>(this->Region_attribute[ir]);
5312
5313 // Loop over all elements on boundaries in region -ir-
5314 const unsigned nele_in_region =
5315 this->nboundary_element_in_region(b, region_id);
5316
5317 // Only bother to do anything else, if there are elements
5318 // associated with the boundary and the current region
5319 if (nele_in_region > 0)
5320 {
5321 // Loop over the bulk elements adjacent to boundary b
5322 for (unsigned e = 0; e < nele_in_region; e++)
5323 {
5324 // Get pointer to the bulk element that is adjacent
5325 // to boundary b
5327 this->boundary_element_in_region_pt(b, region_id, e);
5328
5329 // Get the index of the face of element e along
5330 // boundary b
5331 int face_index =
5332 this->face_index_at_boundary_in_region(b, region_id, e);
5333
5334 // Create the face element
5336 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
5337
5338 // Associated the face element with the bulk
5340
5341 // ... and add it to the tmp storage for all the
5342 // face elements, do not take care for repeated
5343 // ones (at the moment)
5345
5346 } // for (e < nele_in_region)
5347
5348 } // if (nele_in_region > 0)
5349
5350 } // for (ir < n_regions)
5351
5352 } // if (n_regions > 1)
5353
5354 // Otherwise it's just the normal boundary functions
5355 else
5356 {
5357 // Loop over all elements on boundaries
5358 const unsigned nbound_ele = this->nboundary_element(b);
5359
5360 // Only bother to do anything else, if there are elements
5361 if (nbound_ele > 0)
5362 {
5363 // Loop over the bulk elements adjacent to boundary b
5364 for (unsigned e = 0; e < nbound_ele; e++)
5365 {
5366 // Get pointer to the bulk element that is adjacent to
5367 // boundary b
5368 FiniteElement* bulk_ele_pt = this->boundary_element_pt(b, e);
5369
5370 // Get the index of the face of element e along
5371 // boundary b
5372 int face_index = this->face_index_at_boundary(b, e);
5373
5374 // Create the face element
5376 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
5377
5378 // Associated the face element with the bulk
5380
5381 // ... and add it to the tmp storage for all the face
5382 // elements, do not care for repeated ones (at the
5383 // moment)
5385
5386 } // (e < nbound_ele)
5387
5388 } // (nbound_ele > 0)
5389
5390 } // else (n_regions > 1)
5391
5392 // map to know which face element has been already done
5393 std::map<FiniteElement*, bool> done_face;
5394
5395 // Set the flag to indicate if we are working with an internal
5396 // boundary
5397 is_internal_boundary = false;
5398
5399 // Free the memory of the elements in this container (only used
5400 // when working with internal boundaries)
5402
5403 // Get the number of face elements in the boundary (including
5404 // repeated)
5405 const unsigned n_tmp_face_ele = tmp_face_ele_pt.size();
5406 for (unsigned ie = 0; ie < n_tmp_face_ele; ie++)
5407 {
5408 // Get the possible main element
5411 {
5412 // Mark the face element as done
5414 // Get the number of nodes for the face element
5415 const unsigned nnodes = main_face_ele_pt->nnode();
5416 // Get the first and last node of the main face element
5419 // Look for the other side face element (we can start from
5420 // the next one, all previous face elements have been
5421 // already identified with its other side face)
5422 for (unsigned iie = ie + 1; iie < n_tmp_face_ele; iie++)
5423 {
5424 // Get the possible dependant element
5427 {
5428 // Get the first and last node of the dependant
5429 // face element
5433 // Check if the nodes at the ends of both face
5434 // elements match (also check the reversed case)
5439 {
5440 // Set the flag to indicate we are working with an
5441 // internal boundary
5442 is_internal_boundary = true;
5443 // Mark the face element as done
5445
5446 // Now choose which face element will be used
5447 // as the main element. We get the processor in
5448 // charge of the element and choose the one
5449 // with the highest processor in charge or the
5450 // bottom-left bulk element in case the both
5451 // faces are on the same processor
5452
5453 // Get the bulk element for each face element
5454 // (the main and the dependant face element)
5459
5460 // Get the processor in charge for each bulk
5461 // element
5466
5467 // If the processor in charge is negative the
5468 // element is not halo, therefore the processor
5469 // in charge is the current one
5471 {
5472 processor_in_charge_main_bulk_ele = static_cast<int>(my_rank);
5473 }
5475 {
5477 static_cast<int>(my_rank);
5478 }
5479
5480 // Flag to know if add the main or dependant
5481 // face element
5482 bool add_main_face_element = true;
5485 {
5486 // Include the dependant element
5487 add_main_face_element = false;
5488 }
5491 {
5492 // When the processor in charge for both
5493 // elements is the same then use the
5494 // bottom-left criteria on the bulk
5495 // elements to choose the main face element
5498 // Get the number of nodes on the bulk
5499 // elements
5500 const unsigned n_bulk_nodes = main_bulk_ele_pt->nnode();
5501 for (unsigned inode = 0; inode < n_bulk_nodes; inode++)
5502 {
5503 for (unsigned idim = 0; idim < 2; idim++)
5504 {
5509 } // (idim < 2)
5510
5511 } // (inode < n_bulk_nodes)
5512
5513 // Get the average of the nodes coordinates
5514 for (unsigned idim = 0; idim < 2; idim++)
5515 {
5518 }
5519
5520 // Once we know the average coordinates for
5521 // each element then we choose the one with
5522 // the bottom-left averaged coordinates
5524 {
5525 add_main_face_element = false;
5526 }
5527 else if (dependant_ele_coordinates[1] ==
5529 {
5530 // The left-most element
5532 {
5533 add_main_face_element = false;
5534 }
5535 }
5536 } // else -- The processor in charge is the
5537 // same for both elements
5538
5540 {
5541 // Add the main face element to the storage
5542 // so we get the halo and haloed nodes from
5543 // it
5544 face_ele_pt.push_back(main_face_ele_pt);
5545 // Mark the dependat face element to free
5546 // its memory
5548 }
5549 else
5550 {
5551 // Add the dependant face element to the
5552 // storage so we get the halo and haloed
5553 // nodes from it
5555 // Mark the main face element to free its
5556 // memory
5558 }
5559
5560 // Break the for to look for the next face
5561 // element
5562 break;
5563
5564 } // if -- matching of nodes from main ele and
5565 // dependant ele
5566
5567 } // if (!done_face[dependant_face_ele_pt])
5568
5569 } // for (iie < n_tmp_face_ele)
5570
5571 } // if (!done_face[main_face_ele_pt])
5572
5573 } // for (ie < n_tmp_face_ele)
5574
5575 // Are there any face element to free its memory
5576 const unsigned n_free_face_ele = free_memory_face_ele_pt.size();
5577 if (n_free_face_ele == 0)
5578 {
5579 // If there is not face elements to free memory that means that
5580 // we are not working with an internal boundary, therefore copy
5581 // all the element from the tmp face elements into the face
5582 // elements container
5583
5584 // Resize the container
5586 // loop over the elements and copy them
5587 for (unsigned i = 0; i < n_tmp_face_ele; i++)
5588 {
5590 } // for (i < n_tmp_face_ele)
5591
5592 } // if (n_free_face_ele == 0)
5593 else
5594 {
5595 // ... otherwise free the memory of the indicated elements
5596 // loop over the elements to free its memory
5597 for (unsigned i = 0; i < n_free_face_ele; i++)
5598 {
5599 delete free_memory_face_ele_pt[i];
5601 } // for (i < n_free_face_ele)
5602 }
5603 }
5604
5605 /// ========================================================================
5606 /// In charge of sinchronize the boundary coordinates for internal
5607 /// boundaries that were split as part of the distribution
5608 /// process. Called after setup_boundary_coordinates() for the
5609 /// original mesh only
5610 /// ========================================================================
5611 template<class ELEMENT>
5613 const unsigned& b)
5614 {
5615 // ------------------------------------------------------------------
5616 // First: Get the face elements associated with the current boundary
5617 // ------------------------------------------------------------------
5618
5619 // Get the communicator of the mesh
5620 OomphCommunicator* comm_pt = this->communicator_pt();
5621
5622 const unsigned nproc = comm_pt->nproc();
5623 const unsigned my_rank = comm_pt->my_rank();
5624
5625 // Temporary storage for face elements (do not take care of repeated
5626 // face elements)
5628
5629 const unsigned nregions = this->nregion();
5630
5631 // map to associate the face element to the bulk element, necessary
5632 // to get the processor in charge for the halo elements
5633 std::map<FiniteElement*, FiniteElement*> face_to_bulk_element_pt;
5634
5635 // If there is more than one region then only use boundary
5636 // coordinates from the bulk side (region 0)
5637 if (nregions > 1)
5638 {
5639 for (unsigned ir = 0; ir < nregions; ir++)
5640 {
5641 const unsigned region_id =
5642 static_cast<unsigned>(this->Region_attribute[ir]);
5643
5644 // Loop over all elements on boundaries in region -ir-
5645 const unsigned nele_in_region =
5646 this->nboundary_element_in_region(b, region_id);
5647
5648 // Only bother to do anything else, if there are elements
5649 // associated with the boundary and the current region
5650 if (nele_in_region > 0)
5651 {
5652 // Loop over the bulk elements adjacent to boundary b
5653 for (unsigned e = 0; e < nele_in_region; e++)
5654 {
5655 // Get pointer to the bulk element that is adjacent to boundary b
5657 this->boundary_element_in_region_pt(b, region_id, e);
5658
5659 // Get the index of the face of element e along boundary b
5660 int face_index =
5661 this->face_index_at_boundary_in_region(b, region_id, e);
5662
5663 // Create the face element
5665 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
5666
5667 // ... and add it to the tmp storage for all the face
5668 // elements, do not take care for repeated ones (at the
5669 // moment)
5671 // Create the map to know if the element is halo
5673
5674 } // for (e < nele_in_region)
5675
5676 } // if (nele_in_region > 0)
5677
5678 } // for (ir < n_regions)
5679
5680 } // if (n_regions > 1)
5681
5682 // Otherwise it's just the normal boundary functions
5683 else
5684 {
5685 // Loop over all elements on boundaries
5686 const unsigned nbound_ele = this->nboundary_element(b);
5687
5688 // Only bother to do anything else, if there are elements
5689 if (nbound_ele > 0)
5690 {
5691 // Loop over the bulk elements adjacent to boundary b
5692 for (unsigned e = 0; e < nbound_ele; e++)
5693 {
5694 // Get pointer to the bulk element that is adjacent to boundary b
5695 FiniteElement* bulk_ele_pt = this->boundary_element_pt(b, e);
5696
5697 // Get the index of the face of element e along boundary b
5698 int face_index = this->face_index_at_boundary(b, e);
5699
5700 // Create the face element
5702 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
5703
5704 // ... and add it to the tmp storage for all the face
5705 // elements, do not care for repeated ones (at the moment)
5707 // Create the map to know if the element is halo
5709
5710 } // (e < nbound_ele)
5711
5712 } // (nbound_ele > 0)
5713
5714 } // else (n_regions > 1)
5715
5716 // Temporary storage for one side face elements. In case we are
5717 // working with an internal boundary here we store only one of the
5718 // face elements that are at each side of the boundary
5720
5721 // map to know which face element has been already done
5722 std::map<FiniteElement*, bool> done_face;
5723
5724 // Flag to indicate if we are working with an internal boundary
5725 bool is_internal_boundary = false;
5726
5727#ifdef PARANOID
5728 // Flag to indicate if we are working with an internal boundary (paranoid)
5729 bool is_internal_boundary_paranoid = false;
5730
5731 // Count the number of other side face elements found in case we are
5732 // working with an internal boundary
5733 unsigned nfound_face_elements = 0;
5734#endif
5735
5736 // Get the number of face elements in the boundary
5737 const unsigned nbound_ele = tmp_face_ele_pt.size();
5738 for (unsigned ie = 0; ie < nbound_ele; ie++)
5739 {
5740 // Get the possible main element
5743 {
5744 // Mark the face element as done
5746 // Get the number of nodes for the face element
5747 const unsigned nnodes = main_face_ele_pt->nnode();
5748 // Get the first and last node of the main face element
5751 // Look for the other side face element
5752 for (unsigned iie = ie + 1; iie < nbound_ele; iie++)
5753 {
5754 // Get the possible dependant element
5757 {
5758 // Get the first and last node of the dependant face element
5762 // Check if the nodes at the ends of both face elements
5763 // match (also check the reversed case)
5768 {
5769#ifdef PARANOID
5770 // Increase the number of found face elements
5772#endif
5773 // Set the flag to indicate we are working with an
5774 // internal boundary
5775 is_internal_boundary = true;
5776 // Mark the face element as done
5778
5779 // Now choose which face element will be used as the main
5780 // element. Use the same criteria as the compute segments
5781 // connectivity method (highest processor in charge or
5782 // bottom-left bulk element)
5783
5784 // Get the bulk element for each face element (the main
5785 // and the dependant face element)
5790
5791 // Get the processor in charge for each bulk element
5796
5797 // If the processor in charge is negative the element is
5798 // not halo, therefore the processor in charge is the
5799 // current one
5801 {
5802 processor_in_charge_main_bulk_ele = static_cast<int>(my_rank);
5803 }
5805 {
5807 static_cast<int>(my_rank);
5808 }
5809
5810 // Flag to know if add the main or dependant face element
5811 bool add_main_face_element = true;
5814 {
5815 // Include the dependant element
5816 add_main_face_element = false;
5817 }
5820 {
5821 // When the processor in charge for both elements is the same
5822 // then use the bottom-left criteria on the bulk elements to
5823 // choose the main face element
5826 // Get the number of nodes on the bulk elements
5827 const unsigned n_bulk_nodes = main_bulk_ele_pt->nnode();
5828 for (unsigned inode = 0; inode < n_bulk_nodes; inode++)
5829 {
5830 for (unsigned idim = 0; idim < 2; idim++)
5831 {
5836 } // (idim < 2)
5837 } // (inode < n_bulk_nodes)
5838
5839 // Get the average of the nodes coordinates
5840 for (unsigned idim = 0; idim < 2; idim++)
5841 {
5844 }
5845
5846 // Once we know the average coordinates for each element
5847 // then we choose the one with the bottom-left averaged
5848 // coordinates
5850 {
5851 add_main_face_element = false;
5852 }
5853 else if (dependant_ele_coordinates[1] ==
5855 {
5856 // The left-most element
5858 {
5859 add_main_face_element = false;
5860 }
5861 }
5862 } // else -- The processor in charge is the same for both
5863 // elements
5864
5866 {
5867 // Add the main face element to the storage so we get
5868 // the halo and haloed nodes from these face element
5869 face_ele_pt.push_back(main_face_ele_pt);
5870 }
5871 else
5872 {
5873 // Add the main face element to the storage so we get
5874 // the halo and haloed nodes from these face element
5876 }
5877
5878 // Break the for to look for the next face element
5879 break;
5880
5881 } // if -- matching of nodes from main ele and dependant ele
5882 } // if (!done_face[dependant_face_ele_pt])
5883 } // for (iie < nbound_ele)
5884 } // if (!done_face[main_face_ele_pt])
5885 } // for (ie < nbound_ele)
5886
5887 // Get the number of face elements
5888 const unsigned nface_ele = face_ele_pt.size();
5889
5890#ifdef PARANOID
5891 // Check if we are working with an internal open curve. First check
5892 // if there are elements, in a distributed approach they may be no
5893 // elements associated to the boundary
5895 {
5897 }
5898
5900 nbound_ele != nface_ele * 2)
5901 {
5902 std::ostringstream error_message;
5903 error_message
5904 << "The info. to perform the synchronisation of the boundary "
5905 << "coordinates was not completely established\n"
5906 << "In this case it was the number of non repeated boundary elements\n"
5907 << "Number of boundary elements: (" << nbound_ele << ")\n"
5908 << "Number of nonrepeated boundary elements: (" << nface_ele << ")\n";
5909 throw OomphLibError(error_message.str(),
5910 "TriangleMesh::synchronize_boundary_coordinates()",
5912 }
5913#endif
5914
5915 // ----------------------------------------------------------------
5916 // Second: Identify the halo face elements
5917 // ----------------------------------------------------------------
5918
5919 // A flag vector to mark those face elements that are considered as
5920 // halo in the current processor
5921 std::vector<bool> is_halo_face_element(nface_ele, false);
5922
5923 // Count the total number of non halo face elements
5924 unsigned nnon_halo_face_elements = 0;
5925
5926 for (unsigned ie = 0; ie < nface_ele; ie++)
5927 {
5929 // Get the bulk element
5931 // Check if the bulk element is halo
5932 if (!tmp_bulk_ele_pt->is_halo())
5933 {
5934 is_halo_face_element[ie] = false;
5936 }
5937 else
5938 {
5939 // Mark the face element as halo
5940 is_halo_face_element[ie] = true;
5941 }
5942 } // for (ie < nface_ele)
5943
5944 // -----------------------------------------------------------------
5945 // Third: Go through the face elements and get the nodes from the
5946 // elements. The boundary coordinate from each node is sent to its
5947 // processor in charge, then that processor will be responsible to
5948 // send the bound coordinate to all the processors that have a halo
5949 // representation of the node
5950 // -----------------------------------------------------------------
5951
5952 // A map to know which nodes are already done
5953 std::map<Node*, bool> done_node;
5954
5955 // The storage for the halo nodes on face elements in this processor
5956 // with other processors
5958
5959 // The storage for the ids of the halo nodes on face elements in
5960 // this processor with other processors
5962
5963 // The storage for the haloed nodes on face elements in this
5964 // processor with other processors
5966
5967 // The storage for the ids of the haloed nodes on face elements in
5968 // this processor with other processors
5970
5971 // A map to know which nodes are face nodes and the processor in
5972 // charge is the current one
5973 std::map<Node*, bool> done_haloed_face_node;
5974
5975 // Go through all the face elements
5976 for (unsigned iface = 0; iface < nface_ele; iface++)
5977 {
5978 // Only work with the non halo face elements
5980 {
5981 // Get the face element
5983 // The number of nodes of the face elements
5984 const unsigned nnodes = ele_face_pt->nnode();
5985 // Go through all the nodes in the face element
5986 for (unsigned in = 0; in < nnodes; in++)
5987 {
5989 // Check if node is done
5990 if (!done_node[face_node_pt])
5991 {
5992 // Mark the node as done
5993 done_node[face_node_pt] = true;
5994 // First check if the node is halo
5995 if (face_node_pt->is_halo())
5996 {
5997 // Get the processor in charge for the current node
5999#ifdef PARANOID
6000 if (int_nonhalo_ID < 0)
6001 {
6002 std::ostringstream error_message;
6003 error_message
6004 << "The node was marked to be halo but the processor in "
6005 << "charge was found to be -1\n\n";
6006 throw OomphLibError(
6007 error_message.str(),
6008 "TriangleMesh::synchronize_boundary_coordinates()",
6010 }
6011#endif
6012 const unsigned ip = static_cast<unsigned>(int_nonhalo_ID);
6013 // Add the node to the structure that holds the halo
6014 // nodes, the current processor will need to send the
6015 // info. to the processor in charge.
6016 face_halo_node_pt[ip].push_back(face_node_pt);
6017 // ... finally look for the halo id with the processor in
6018 // charge
6019#ifdef PARANOID
6020 bool found_halo_node = false;
6021#endif
6022 const unsigned nhalo_iproc = this->nhalo_node(ip);
6023 for (unsigned ihn = 0; ihn < nhalo_iproc; ihn++)
6024 {
6025 Node* compare_face_node_pt = this->halo_node_pt(ip, ihn);
6027 {
6028 // Once found the id of the node with the processor
6029 // store the id in the proper storage
6030 face_halo_node_id[ip].push_back(ihn);
6031#ifdef PARANOID
6032 // Set the flag to mark as found the halo node
6033 found_halo_node = true;
6034#endif
6035 // Break the loop
6036 break;
6037 }
6038 } // for (ih < nhalo_iproc)
6039#ifdef PARANOID
6040 if (!found_halo_node)
6041 {
6042 std::ostringstream error_message;
6043 error_message
6044 << "The halo id of the current node: (" << face_node_pt->x(0)
6045 << ", " << face_node_pt->x(1) << ") with processor (" << ip
6046 << ") was not found!!!\n\n";
6047 throw OomphLibError(
6048 error_message.str(),
6049 "TriangleMesh::synchronize_boundary_coordinates()",
6051 }
6052#endif
6053 } // if (face_node_pt->is_halo())
6054 // If the node is not halo then it could be haloed. If that
6055 // is the case then store the processors at which the node
6056 // is haloed and its id. The info. of these nodes will be
6057 // sent to all the processors with a halo counterpart
6058 else
6059 {
6060 for (unsigned ip = 0; ip < nproc; ip++)
6061 {
6062 // Only work with processors different that the current one
6063 if (ip != my_rank)
6064 {
6065 // If the node is found to be haloed with the "ip"
6066 // processor then save the haloed id in the storage.
6067 // The current processor needs to send info. to the
6068 // other processors to establish the boundary
6069 // coordinates
6070
6071 // Get the number of haloed nodes with processor ip
6072 const unsigned nhaloed_iproc = this->nhaloed_node(ip);
6073 for (unsigned ihdn = 0; ihdn < nhaloed_iproc; ihdn++)
6074 {
6075 Node* compare_face_node_pt = this->haloed_node_pt(ip, ihdn);
6077 {
6078 // Store the node on the haloed node vector for
6079 // the corresponding processor
6081 // Now store the halo id of the node with the
6082 // current processor
6083 face_haloed_node_id[ip].push_back(ihdn);
6084 // Mark the node as haloed with other processors,
6085 // so we know the processor in charge is the
6086 // current one "my_rank".
6088 // Break looking in the current processor, look in
6089 // the next one
6090 break;
6091 } // if (face_node_pt == compare_face_node_pt)
6092 } // for (ihdn < nhaloed_node_iproc)
6093 } // if (ip != my_rank)
6094 } // for (ip < nproc)
6095 } // else (non halo node)
6096 } // if (!done_node[node_face_pt])
6097 } // for (in < nnodes)
6098 } // if (!is_halo_face_element[iface])
6099 } // for (iface < nface_ele)
6100
6101 // -----------------------------------------------------------------
6102 // Fourth: Go through the halo nodes, package and send the
6103 // info. necessary to identify the face nodes in the processor in
6104 // charge. Identify the haloed nodes in the processor in charge and
6105 // establish the boundary coordinates, check if those nodes are
6106 // (already) marked as faced nodes, if that is the case then do not
6107 // establish the boundary coordinates but register them to send back
6108 // the info. to all the processors that have a halo representation
6109 // of the face node
6110 // -----------------------------------------------------------------
6111
6112 // Go through all processors
6113 for (unsigned ip = 0; ip < nproc; ip++)
6114 {
6115 // Only work with processors different than the current one
6116 if (ip != my_rank)
6117 {
6118 const unsigned nhalo_face_nodes = face_halo_node_pt[ip].size();
6119#ifdef PARANOID
6121 {
6122 std::ostringstream error_message;
6123 error_message
6124 << "The number of found halo face nodes (" << nhalo_face_nodes
6125 << ") is different from the number of\nfound halo face ids ("
6126 << face_halo_node_id[ip].size() << ")!!!\n\n";
6127 throw OomphLibError(
6128 error_message.str(),
6129 "TriangleMesh::synchronize_boundary_coordinates()",
6131 }
6132#endif
6133
6134 // Container to send the info. related with the halo nodes to be
6135 // identified in the processors in charge
6138
6139 // Go through the halo face nodes in the "ip" processor
6140 for (unsigned ihfn = 0; ihfn < nhalo_face_nodes; ihfn++)
6141 {
6142 // Get the "ihfn"-th face node with the "ip" processor
6144 // Get the halo id with the "ip" processor
6145 const unsigned halo_id = face_halo_node_id[ip][ihfn];
6146 // Get the boundary coordinate of the node
6148 halo_face_node_pt->get_coordinates_on_boundary(b, zeta);
6149 // Store the info. in the containers
6151 flat_double_send_packed_data.push_back(zeta[0]);
6152 }
6153
6154 // Send the info.
6157
6158 // Processor to which send the info
6159 int send_proc = static_cast<int>(ip);
6160 // Processor from which receive the info
6161 int receive_proc = static_cast<int>(ip);
6162
6163 // Storage to receive the info.
6166
6167 // --------------
6168 // Unsigned data
6171 1,
6173 send_proc,
6174 1,
6175 comm_pt->mpi_comm(),
6176 &request);
6177
6178 unsigned nflat_unsigned_receive = 0;
6180 1,
6183 1,
6184 comm_pt->mpi_comm(),
6185 &status);
6186
6188
6189 if (nflat_unsigned_send != 0)
6190 {
6194 send_proc,
6195 2,
6196 comm_pt->mpi_comm(),
6197 &request);
6198 }
6199
6200 if (nflat_unsigned_receive != 0)
6201 {
6207 2,
6208 comm_pt->mpi_comm(),
6209 &status);
6210 }
6211
6212 if (nflat_unsigned_send != 0)
6213 {
6215 }
6216
6217 // --------------
6218
6219
6220 // Work-around for odd seg fault when calling MPI_Wait below.
6222
6223 // Double data
6226 1,
6228 send_proc,
6229 3,
6230 comm_pt->mpi_comm(),
6231 &my_request);
6232
6233 unsigned nflat_double_receive = 0;
6235 1,
6238 3,
6239 comm_pt->mpi_comm(),
6240 &status);
6241
6242 // this is where it used to die without separate MPI request
6244
6245 if (nflat_double_send != 0)
6246 {
6249 MPI_DOUBLE,
6250 send_proc,
6251 4,
6252 comm_pt->mpi_comm(),
6253 &my_request);
6254 }
6255
6256 if (nflat_double_receive != 0)
6257 {
6261 MPI_DOUBLE,
6263 4,
6264 comm_pt->mpi_comm(),
6265 &status);
6266 }
6267
6268 if (nflat_double_send != 0)
6269 {
6271 }
6272 // --------------
6273
6274#ifdef PARANOID
6276 {
6277 std::ostringstream error_message;
6278 error_message << "The number of unsigned received data ("
6279 << nflat_unsigned_receive << ") is different from the "
6280 << "number\nof double received data ("
6281 << nflat_double_receive << ")!!!\n\n";
6282 throw OomphLibError(
6283 error_message.str(),
6284 "TriangleMesh::synchronize_boundary_coordinates()",
6286 }
6287#endif
6288
6289 // With the received info. establish the boundary coordinates
6290 // for the face nodes that this processor is in charge (haloed
6291 // nodes)
6293 iflat_packed++)
6294 {
6295 // Get the haloed id for the node
6296 const unsigned haloed_id =
6298 // Get the boundary coordinates
6301
6302 // Get the haloed node
6303 Node* haloed_face_node_pt = this->haloed_node_pt(ip, haloed_id);
6304
6305 // If the node has already set the boundary coordinates then
6306 // do not establish it. This is the case for the nodes that
6307 // lie on the boundary, for those nodes not identified on the
6308 // boundary since no elements lie on the boundary but the node
6309 // is on the boundary (a corner of an element lies on the
6310 // boundary) set boundary coordinates and register them to
6311 // send their info. to the processors with a halo counterpart
6312
6313 // If the node is not haloed face in the procesor in charge
6314 // then set the boundary coordinates and register the node to
6315 // send back the boundary coordinates to the processors with a
6316 // halo counterpart
6318 {
6319 // Establish the boundary coordinates
6320 haloed_face_node_pt->set_coordinates_on_boundary(b, zeta);
6321
6322 // Look in all processors where the node could be halo
6323 for (unsigned iiproc = 0; iiproc < nproc; iiproc++)
6324 {
6325 // Only work with processors different than the current one
6326 if (iiproc != my_rank)
6327 {
6328 // Get the number of haloed nodes with processor iiproc
6329 const unsigned nhaloed_node_iiproc = this->nhaloed_node(iiproc);
6330 for (unsigned ihdn = 0; ihdn < nhaloed_node_iiproc; ihdn++)
6331 {
6333 this->haloed_node_pt(iiproc, ihdn);
6335 {
6336 // Store the node on the haloed node vector for the
6337 // corresponding processor
6339 // Now store the halo id of the node with the current
6340 // processor
6341 face_haloed_node_id[iiproc].push_back(ihdn);
6342 // Break searching in the current processor, search in
6343 // the next one
6344 break;
6345 } // if (haloed_face_node_pt==compare_haloed_face_node_pt)
6346 } // for (ihdn < nhaloed_node_iproc)
6347 } // if (iiproc != my_rank)
6348 } // for (iiproc < nproc)
6349 } // if (!done_haloed_face_node[haloed_face_node_pt])
6350 } // for (iflat_packed < nflat_unsigned_receive)
6351 } // if (ip != my_rank)
6352 } // for (ip < nproc)
6353
6354 // -----------------------------------------------------------------
6355 // Fifth: The boundary coordinates have been established in the
6356 // processors in charge of the nodes. Now each processor send back
6357 // the boundary coordinates to all the processors where there is a
6358 // halo representation of the node
6359 // -----------------------------------------------------------------
6360
6361 // Go through all processors
6362 for (unsigned ip = 0; ip < nproc; ip++)
6363 {
6364 // Only work with processors different than the current one
6365 if (ip != my_rank)
6366 {
6367 // Container to send the info. of the haloed nodes to all the
6368 // processors
6371
6372 // Get the total number of haloed face nodes with the "ip"
6373 // processor
6374 const unsigned nhaloed_face_nodes = face_haloed_node_pt[ip].size();
6375 // Go through the haloed face nodes in the "ip" processor
6376 for (unsigned ihdfn = 0; ihdfn < nhaloed_face_nodes; ihdfn++)
6377 {
6378 // Get the "ihdfn"-th face node with the "ip" processor
6380 // Get the haloed id with the "ip" processor
6381 const unsigned haloed_id = face_haloed_node_id[ip][ihdfn];
6382 // Get the boundary coordinate of the node
6384 haloed_face_node_pt->get_coordinates_on_boundary(b, zeta);
6385 // Store the info. in the containers
6387 flat_double_send_packed_data.push_back(zeta[0]);
6388 }
6389
6390 // Send the info.
6393
6394 // Processor to which send the info
6395 int send_proc = static_cast<int>(ip);
6396 // Processor from which receive the info
6397 int receive_proc = static_cast<int>(ip);
6398
6399 // Storage to receive the info.
6402
6403 // --------------
6404 // Unsigned data
6407 1,
6409 send_proc,
6410 1,
6411 comm_pt->mpi_comm(),
6412 &request);
6413
6414 unsigned nflat_unsigned_receive = 0;
6416 1,
6419 1,
6420 comm_pt->mpi_comm(),
6421 &status);
6422
6424
6425 if (nflat_unsigned_send != 0)
6426 {
6430 send_proc,
6431 2,
6432 comm_pt->mpi_comm(),
6433 &request);
6434 }
6435
6436 if (nflat_unsigned_receive != 0)
6437 {
6443 2,
6444 comm_pt->mpi_comm(),
6445 &status);
6446 }
6447
6448 if (nflat_unsigned_send != 0)
6449 {
6451 }
6452
6453 // --------------
6454 // Double data
6457 1,
6459 send_proc,
6460 3,
6461 comm_pt->mpi_comm(),
6462 &request);
6463
6464 unsigned nflat_double_receive = 0;
6466 1,
6469 3,
6470 comm_pt->mpi_comm(),
6471 &status);
6472
6474
6475 if (nflat_double_send != 0)
6476 {
6479 MPI_DOUBLE,
6480 send_proc,
6481 4,
6482 comm_pt->mpi_comm(),
6483 &request);
6484 }
6485
6486 if (nflat_double_receive != 0)
6487 {
6491 MPI_DOUBLE,
6493 4,
6494 comm_pt->mpi_comm(),
6495 &status);
6496 }
6497
6498 if (nflat_double_send != 0)
6499 {
6501 }
6502 // --------------
6503
6504#ifdef PARANOID
6506 {
6507 std::ostringstream error_message;
6508 error_message << "The number of unsigned received data ("
6509 << nflat_unsigned_receive << ") is different from the "
6510 << "number\nof double received data ("
6511 << nflat_double_receive << ")!!!\n\n";
6512 throw OomphLibError(
6513 error_message.str(),
6514 "TriangleMesh::synchronize_boundary_coordinates()",
6516 }
6517#endif
6518
6519 // With the received info. establish the boundary coordinates
6520 // received for the face nodes that this processor is not in
6521 // charge (halo nodes)
6523 iflat_packed++)
6524 {
6525 // Get the halo id for the node
6526 const unsigned halo_id =
6528 // Get the boundary coordinates
6531
6532 // Get the halo node
6533 Node* halo_face_node_pt = this->halo_node_pt(ip, halo_id);
6534
6535 // It could be possible that the node has been already
6536 // established boundary coordinates since it is a halo face
6537 // node. However, for those elements not on the boundary, but
6538 // having a corner node on the boundary this procedure will
6539 // establish boundary coordinates for those nodes
6540
6541 // this->add_boundary_node(b, halo_face_node_pt);
6542
6543 // Establish the boundary coordinates
6544 halo_face_node_pt->set_coordinates_on_boundary(b, zeta);
6545 } // for (iflat_packed < nflat_unsigned_receive)
6546 } // if (ip != my_rank)
6547 } // for (ip < nproc)
6548
6549 // Clean all the created face elements
6550 for (unsigned ie = 0; ie < nbound_ele; ie++)
6551 {
6552 delete tmp_face_ele_pt[ie];
6553 tmp_face_ele_pt[ie] = 0;
6554 }
6555
6556 // Now get a new face mesh representation and fill the data for those
6557 // processors with halo segments
6559 {
6560 re_scale_re_assigned_initial_zeta_values_for_internal_boundary(b);
6561 }
6562 }
6563
6564 //======================================================================
6565 /// Re-assign the boundary segments initial zeta (arclength)
6566 /// for those internal boundaries that were splited during the
6567 /// distribution process (only apply for internal boundaries that
6568 /// have one face element at each side of the boundary)
6569 //======================================================================
6570 template<class ELEMENT>
6573 const unsigned& b)
6574 {
6575 // ------------------------------------------------------------------
6576 // First: Get the face elements associated with the current boundary
6577 // Only include nonhalo face elements
6578 // ------------------------------------------------------------------
6579 // Temporary storage for face elements
6581
6582 // Temporary storage for the number of elements adjacent to the
6583 // boundary
6584 unsigned nele = 0;
6585
6586 // Temporary storage for elements adjacent to the boundary that have
6587 // a common edge (related with internal boundaries)
6588 unsigned n_repeated_ele = 0;
6589
6590 const unsigned n_regions = this->nregion();
6591
6592 // Temporary storage for already done nodes
6594
6595 // If there is more than one region then only use boundary
6596 // coordinates from the bulk side (region 0)
6597 if (n_regions > 1)
6598 {
6599 for (unsigned rr = 0; rr < n_regions; rr++)
6600 {
6601 const unsigned region_id =
6602 static_cast<unsigned>(this->Region_attribute[rr]);
6603
6604 // Loop over all elements on boundaries in region i_r
6605 const unsigned nel_in_region =
6606 this->nboundary_element_in_region(b, region_id);
6607
6608 unsigned nel_repetead_in_region = 0;
6609
6610 // Only bother to do anything else, if there are elements
6611 // associated with the boundary and the current region
6612 if (nel_in_region > 0)
6613 {
6614 bool repeated = false;
6615
6616 // Loop over the bulk elements adjacent to boundary b
6617 for (unsigned e = 0; e < nel_in_region; e++)
6618 {
6619 // Get pointer to the bulk element that is adjacent to
6620 // boundary b
6622 this->boundary_element_in_region_pt(b, region_id, e);
6623
6624 // Remember only to work with nonhalo elements
6625 if (bulk_elem_pt->is_halo())
6626 {
6628 continue;
6629 }
6630
6631 // Find the index of the face of element e along boundary b
6632 int face_index =
6633 this->face_index_at_boundary_in_region(b, region_id, e);
6634
6635 // Before adding the new element we need to be sure that the
6636 // edge that this element represent has not been already
6637 // added
6639 new DummyFaceElement<ELEMENT>(bulk_elem_pt, face_index);
6640
6641 const unsigned n_nodes = tmp_ele_pt->nnode();
6642
6643 std::pair<Node*, Node*> tmp_pair = std::make_pair(
6645
6646 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
6648
6649 // Search for repeated nodes
6650 const unsigned n_done_nodes = done_nodes_pt.size();
6651 for (unsigned l = 0; l < n_done_nodes; l++)
6652 {
6653 if (tmp_pair == done_nodes_pt[l] ||
6655 {
6657 repeated = true;
6658 break;
6659 }
6660 }
6661
6662 // Create new face element
6663 if (!repeated)
6664 {
6665 // Add the pair of nodes (edge) to the node dones
6666 done_nodes_pt.push_back(tmp_pair);
6667 // Add the element to the face elements
6668 face_el_pt.push_back(tmp_ele_pt);
6669 }
6670 else
6671 {
6672 // Clean up
6673 delete tmp_ele_pt;
6674 tmp_ele_pt = 0;
6675 }
6676
6677 // Re-start
6678 repeated = false;
6679
6680 } // for nel
6681
6683
6685
6686 } // if (nel_in_region > 0)
6687 } // for (rr < n_regions)
6688 } // if (n_regions > 1)
6689 // Otherwise it's just the normal boundary functions
6690 else
6691 {
6692 // Loop over all elements on boundaries
6693 nele = this->nboundary_element(b);
6694
6695 // Only bother to do anything else, if there are elements
6696 if (nele > 0)
6697 {
6698 // Check for repeated ones
6699 bool repeated = false;
6700
6701 // Loop over the bulk elements adjacent to boundary b
6702 for (unsigned e = 0; e < nele; e++)
6703 {
6704 // Get pointer to the bulk element that is adjacent to
6705 // boundary b
6706 FiniteElement* bulk_elem_pt = this->boundary_element_pt(b, e);
6707
6708 // Remember only to work with nonhalo elements
6709 if (bulk_elem_pt->is_halo())
6710 {
6712 // Skip the halo element
6713 continue;
6714 }
6715
6716 // Find the index of the face of element e along boundary b
6717 int face_index = this->face_index_at_boundary(b, e);
6718
6719 // Before adding the new element we need to be sure that the
6720 // edge that this element represents has not been already
6721 // added (only applies for internal boundaries)
6723 new DummyFaceElement<ELEMENT>(bulk_elem_pt, face_index);
6724
6725 const unsigned n_nodes = tmp_ele_pt->nnode();
6726
6727 std::pair<Node*, Node*> tmp_pair = std::make_pair(
6729
6730 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
6732
6733 // Search for repeated nodes
6734 const unsigned n_done_nodes = done_nodes_pt.size();
6735 for (unsigned l = 0; l < n_done_nodes; l++)
6736 {
6737 if (tmp_pair == done_nodes_pt[l] ||
6739 {
6740 // Increase the number of repeated elements
6742 // Mark the element as repeated
6743 repeated = true;
6744 break;
6745 }
6746 }
6747
6748 // Create new face element
6749 if (!repeated)
6750 {
6751 // Add the pair of nodes (edge) to the node dones
6752 done_nodes_pt.push_back(tmp_pair);
6753 // Add the element to the face elements
6754 face_el_pt.push_back(tmp_ele_pt);
6755 }
6756 else
6757 {
6758 // Free the repeated bulk element!!
6759 delete tmp_ele_pt;
6760 tmp_ele_pt = 0;
6761 }
6762
6763 // Re-start
6764 repeated = false;
6765
6766 } // for (e < nel)
6767 } // if (nel > 0)
6768
6769 } // else (n_regions > 1)
6770
6771 // Do not consider the repeated elements
6773
6774#ifdef PARANOID
6775 if (nele != face_el_pt.size())
6776 {
6777 std::ostringstream error_message;
6778 error_message
6779 << "The independet counting of face elements (" << nele << ") for "
6780 << "boundary (" << b << ") is different\n"
6781 << "from the real number of face elements in the container ("
6782 << face_el_pt.size() << ")\n";
6783 //<< "Possible memory leak\n"
6784 throw OomphLibError(error_message.str(),
6785 "TriangleMesh::re_scale_re_assigned_initial_zeta_"
6786 "values_for_internal_boundary()",
6788 }
6789#endif
6790
6791 // ----------------------------------------------------------------
6792 // Second: Sort the face elements (to create segments), only
6793 // consider nonhalo elements
6794 // ----------------------------------------------------------------
6795
6796 // Get the total number of nonhalo face elements
6797 const unsigned nnon_halo_face_elements = face_el_pt.size();
6798
6799 // The vector of list to store the "segments" that compound the
6800 // boundary (segments may appear only in a distributed mesh)
6802
6803 // Number of already sorted face elements
6804 unsigned nsorted_face_elements = 0;
6805
6806 // Keep track of who's done
6807 std::map<FiniteElement*, bool> done_el;
6808
6809 // Keep track of which element is inverted
6810 std::map<FiniteElement*, bool> is_inverted;
6811
6812 // Iterate until all possible segments have been created
6814 {
6815 // The ordered list of face elements (in a distributed mesh a
6816 // collection of contiguous face elements define a segment)
6817 std::list<FiniteElement*> sorted_el_pt;
6818
6819#ifdef PARANOID
6820 // Select an initial element for the segment
6821 bool found_initial_face_element = false;
6822#endif
6823
6825
6826 unsigned iface = 0;
6827 for (iface = 0; iface < nele; iface++)
6828 {
6830 // If not done then take it as initial face element
6831 if (!done_el[ele_face_pt])
6832 {
6833#ifdef PARANOID
6835#endif
6837 iface++; // The next element number
6838 sorted_el_pt.push_back(ele_face_pt);
6839 // Mark as done
6840 done_el[ele_face_pt] = true;
6841 break;
6842 }
6843 } // for (iface < nele)
6844
6845#ifdef PARANOID
6847 {
6848 std::ostringstream error_message;
6849 error_message
6850 << "Could not find an initial face element for the current segment\n";
6851 // << "----- Possible memory leak -----\n";
6852 throw OomphLibError(error_message.str(),
6853 "TriangleMesh::re_scale_re_assigned_initial_zeta_"
6854 "values_for_internal_boundary()",
6856 }
6857#endif
6858
6859 // Number of nodes
6860 const unsigned nnod = ele_face_pt->nnode();
6861
6862 // Left and rightmost nodes (the left and right nodes of the
6863 // current face element)
6866
6867 // Continue iterating if a new face element has been added to the
6868 // list
6869 bool face_element_added = false;
6870
6871 // While a new face element has been added to the set of sorted
6872 // face elements then re-iterate
6873 do
6874 {
6875 // Start from the next face element since we have already added
6876 // the previous one as the initial face element (any previous
6877 // face element had to be added on previous iterations)
6878 for (unsigned iiface = iface; iiface < nele; iiface++)
6879 {
6880 // Re-start flag
6881 face_element_added = false;
6882
6883 // Get the candidate element
6885
6886 // Check that the candidate element has not been done
6887 if (!(done_el[ele_face_pt]))
6888 {
6889 // Get the left and right nodes of the current element
6892
6893 // New element fits at the left of segment and is not inverted
6895 {
6897 sorted_el_pt.push_front(ele_face_pt);
6898 is_inverted[ele_face_pt] = false;
6899 face_element_added = true;
6900 }
6901 // New element fits at the left of segment and is inverted
6902 else if (left_node_pt == local_left_node_pt)
6903 {
6905 sorted_el_pt.push_front(ele_face_pt);
6906 is_inverted[ele_face_pt] = true;
6907 face_element_added = true;
6908 }
6909 // New element fits on the right of segment and is not inverted
6911 {
6913 sorted_el_pt.push_back(ele_face_pt);
6914 is_inverted[ele_face_pt] = false;
6915 face_element_added = true;
6916 }
6917 // New element fits on the right of segment and is inverted
6919 {
6921 sorted_el_pt.push_back(ele_face_pt);
6922 is_inverted[ele_face_pt] = true;
6923 face_element_added = true;
6924 }
6925
6927 {
6928 done_el[ele_face_pt] = true;
6930 break;
6931 }
6932
6933 } // if (!(done_el[ele_face_pt]))
6934 } // for (iiface<nnon_halo_face_element)
6935 } while (face_element_added &&
6937
6938 // Store the created segment in the vector of segments
6940
6941 } // while(nsorted_face_elements < nnon_halo_face_elements);
6942
6943 // --------------------------------------------------------------
6944 // Third: We have the face elements sorted, now assign boundary
6945 // coordinates to the nodes in the segments and compute the
6946 // arclength of the segment
6947 // --------------------------------------------------------------
6948
6949 // Vector of sets that stores the nodes of each segment based on a
6950 // lexicographically order starting from the bottom left node of
6951 // each segment
6953
6954 // The number of segments in this processor
6955 const unsigned nsegments = segment_sorted_ele_pt.size();
6956
6957#ifdef PARANOID
6958 if (nnon_halo_face_elements > 0 && nsegments == 0)
6959 {
6960 std::ostringstream error_message;
6961 error_message
6962 << "The number of segments is zero, but the number of nonhalo\n"
6963 << "elements is: (" << nnon_halo_face_elements << ")\n";
6964 throw OomphLibError(error_message.str(),
6965 "TriangleMesh::re_scale_re_assigned_initial_zeta_"
6966 "values_for_internal_boundary()",
6968 } // if (nnon_halo_face_elements > 0 && nsegments == 0)
6969#endif
6970
6971 // The arclength of each segment in the current processor
6973
6974 // The initial zeta for the segment
6976
6977 // The final zeta for the segment
6979
6980 // Go through all the segments and compute the LOCAL boundary
6981 // coordinates
6982 for (unsigned is = 0; is < nsegments; is++)
6983 {
6984#ifdef PARANOID
6985 if (segment_sorted_ele_pt[is].size() == 0)
6986 {
6987 std::ostringstream error_message;
6988 error_message << "The (" << is << ")-th segment has no elements\n";
6989 throw OomphLibError(error_message.str(),
6990 "TriangleMesh::re_scale_re_assigned_initial_zeta_"
6991 "values_for_internal_boundary()",
6993 } // if (segment_sorted_ele_pt[is].size() == 0)
6994#endif
6995
6996 // Get access to the first element on the segment
6998
6999 // Number of nodes
7000 const unsigned nnod = first_ele_pt->nnode();
7001
7002 // Get the first node of the current segment
7005 {
7007 }
7008
7009 // Get access to the last element on the segment
7011
7012 // Get the last node of the current segment
7015 {
7017 }
7018
7019 // Coordinates of left node
7020 double x_left = first_node_pt->x(0);
7021 double y_left = first_node_pt->x(1);
7022
7023 // Initialise boundary coordinate (local boundary coordinate for
7024 // boundaries with more than one segment)
7025 Vector<double> zeta(1, 0.0);
7026
7027 // If we have associated a GeomObject then it is not necessary to
7028 // compute the arclength, only read the values from the nodes at
7029 // the edges
7030 if (this->boundary_geom_object_pt(b) != 0)
7031 {
7032 first_node_pt->get_coordinates_on_boundary(b, zeta);
7034 last_node_pt->get_coordinates_on_boundary(b, zeta);
7036 }
7037
7038 // Lexicographically bottom left node
7039 std::set<Node*> local_nodes_pt;
7041
7042 // Now loop over nodes in order
7043 for (std::list<FiniteElement*>::iterator it =
7045 it != segment_sorted_ele_pt[is].end();
7046 it++)
7047 {
7048 // Get element
7050
7051 // Start node and increment
7052 unsigned k_nod = 1;
7053 int nod_diff = 1;
7054 if (is_inverted[el_pt])
7055 {
7056 k_nod = nnod - 2;
7057 nod_diff = -1;
7058 }
7059
7060 // Loop over nodes
7061 for (unsigned j = 1; j < nnod; j++)
7062 {
7064 k_nod += nod_diff;
7065
7066 // Coordinates of right node
7067 double x_right = nod_pt->x(0);
7068 double y_right = nod_pt->x(1);
7069
7070 // Increment boundary coordinate
7071 zeta[0] += sqrt((x_right - x_left) * (x_right - x_left) +
7072 (y_right - y_left) * (y_right - y_left));
7073
7074 // Increment reference coordinate
7075 x_left = x_right;
7076 y_left = y_right;
7077
7078 // Get lexicographically bottom left node but only
7079 // use vertex nodes as candidates
7080 local_nodes_pt.insert(nod_pt);
7081
7082 } // for (j < nnod)
7083 } // iterator over the elements in the segment
7084
7085 // Store the arclength of the segment
7087
7088 // Add the nodes for the corresponding segment in the container
7090
7091 } // for (is < nsegments)
7092
7093 // ------------------------------------------------------------------
7094 // Fourth: Now we have the segments sorted, with arclength and with
7095 // LOCAL arclength assigned to the nodes. Procced to re-scale the
7096 // coordinates on the nodes based on the arclength
7097 // ------------------------------------------------------------------
7098
7099 // ------------------------------------------------------------------
7100 // Clear the original storages
7101 Boundary_segment_inverted[b].clear();
7102 Boundary_segment_initial_coordinate[b].clear();
7103 Boundary_segment_final_coordinate[b].clear();
7104
7105 Boundary_segment_initial_zeta[b].clear();
7106 Boundary_segment_final_zeta[b].clear();
7107
7108 Boundary_segment_initial_arclength[b].clear();
7109 Boundary_segment_final_arclength[b].clear();
7110
7111 // Get the zeta values for the first and last node in the boundary
7114 first_node_zeta_coordinate = boundary_initial_zeta_coordinate(b);
7115 last_node_zeta_coordinate = boundary_final_zeta_coordinate(b);
7116
7117 // Get the boundary arclength
7118 const double boundary_arclength =
7120
7121 // Go through the segments and get the first and last node for each
7122 // segment
7123 for (unsigned is = 0; is < nsegments; is++)
7124 {
7125 // Get the first face element of the segment
7127
7128 // The number of nodes
7129 const unsigned nnod = first_face_ele_pt->nnode();
7130
7131 // ... and the first node of the segment
7134 {
7136 }
7137
7138 // Get the bound coordinates of the node
7140 first_node_pt->get_coordinates_on_boundary(b, zeta_first);
7141
7142 // Get the last face element of the segment
7144
7145 // ... and the last node of the segment
7148 {
7150 }
7151
7152 // Get the bound coordinates of the node
7154 last_node_pt->get_coordinates_on_boundary(b, zeta_last);
7155
7156 // Now that we have the first and last node of the segment, get
7157 // the coordinates of the nodes
7160 for (unsigned i = 0; i < 2; i++)
7161 {
7164 }
7165
7166 // Re-assign the values to identify the segments on the new mesh
7167 Boundary_segment_inverted[b].push_back(0);
7168 Boundary_segment_initial_coordinate[b].push_back(first_node_coord);
7169 Boundary_segment_final_coordinate[b].push_back(last_node_coord);
7170
7171 // Check if the boudary has an associated GeomObject
7172 if (this->boundary_geom_object_pt(b) != 0)
7173 {
7174 Boundary_segment_initial_zeta[b].push_back(zeta_first[0]);
7175 Boundary_segment_final_zeta[b].push_back(zeta_last[0]);
7176 }
7177 else
7178 {
7179 // Re-assign the values and re-scale them
7180 Boundary_segment_initial_arclength[b].push_back(zeta_first[0] *
7182 Boundary_segment_final_arclength[b].push_back(zeta_last[0] *
7184 }
7185
7186 } // for (is < nsegments)
7187
7188 // Clean all the created face elements
7189 for (unsigned i = 0; i < nele; i++)
7190 {
7191 delete face_el_pt[i];
7192 face_el_pt[i] = 0;
7193 }
7194 }
7195
7196#endif // OOMPH_HAS_MPI
7197
7198#ifdef OOMPH_HAS_TRIANGLE_LIB
7199
7200 //========================================================================
7201 /// Create TriangulateIO object via the .poly file
7202 //========================================================================
7203 template<class ELEMENT>
7205 const std::string& poly_file_name,
7207 bool& use_attributes)
7208 {
7209 // Process poly file
7210 // -----------------
7211 std::ifstream poly_file(poly_file_name.c_str(), std::ios_base::in);
7212 if (!poly_file)
7213 {
7214 throw OomphLibError("Error opening .poly file\n",
7217 }
7218
7219 // Initialize triangulateio structure
7221
7222 // Ignore the first line with structure description
7223 poly_file.ignore(80, '\n');
7224
7225 // Read and store number of nodes
7226 unsigned invertices;
7228 triangulate_io.numberofpoints = invertices;
7229
7230 // Initialisation of the point list
7231 triangulate_io.pointlist =
7232 (double*)malloc(triangulate_io.numberofpoints * 2 * sizeof(double));
7233
7234 // Read and store spatial dimension of nodes
7235 unsigned mesh_dim;
7237
7238 if (mesh_dim == 0)
7239 {
7240 mesh_dim = 2;
7241 }
7242
7243#ifdef PARANOID
7244 if (mesh_dim != 2)
7245 {
7246 throw OomphLibError("The dimension must be 2\n",
7249 }
7250#endif
7251
7252 // Read and check the flag for attributes
7253 unsigned nextras;
7254 poly_file >> nextras;
7255
7256 triangulate_io.numberofpointattributes = 0;
7257 triangulate_io.pointattributelist = (double*)NULL;
7258
7259 // Read and check the flag for boundary markers
7260 unsigned nodemarkers;
7262 triangulate_io.pointmarkerlist = (int*)NULL;
7263
7264#ifdef PARANOID
7265 // Reading the .poly with the oomph.lib we need
7266 // to set the point attribute and markers to 0
7267 if (nextras != 0 || nodemarkers != 0)
7268 {
7269 oomph_info << "===================================================="
7270 << std::endl
7271 << std::endl;
7272 oomph_info << "Reading the .poly file via oomph_lib \n"
7273 << "point's attribute and point's markers \n"
7274 << "are automatically set to 0" << std::endl;
7275 oomph_info << "===================================================="
7276 << std::endl;
7277 }
7278#endif
7279
7280 // Dummy for node number (and attribute or markers if included)
7281 unsigned dummy_value;
7282 unsigned count_point = 0;
7283 std::string test_string;
7284
7285 // Skip line with commentary
7286 getline(poly_file, test_string, '#');
7287 poly_file.ignore(80, '\n');
7288
7289 // Read and store all the nodes coordinates
7290 // (hole's vertices as well)
7291 for (unsigned count = 0; count < invertices; count++)
7292 {
7294 poly_file >> triangulate_io.pointlist[count_point];
7295 poly_file >> triangulate_io.pointlist[count_point + 1];
7296 if (nextras != 0 || nodemarkers != 0)
7297 {
7298 for (unsigned j = 0; j < nextras; j++)
7299 {
7301 }
7302 }
7303 else if (nextras != 0 && nodemarkers != 0)
7304 {
7305 for (unsigned j = 0; j < nextras; j++)
7306 {
7309 }
7310 }
7311 // Read the next line
7312 poly_file.ignore(80, '\n');
7313
7314 // Skip line with commentary for internal box whether found
7315 if (poly_file.get() == '#')
7316 {
7317 poly_file.ignore(80, '\n');
7318 }
7319 // If read the char should be put back in the string
7320
7321 else
7322 {
7323 poly_file.unget();
7324 }
7325 count_point += 2;
7326 }
7327
7328 // The line with the segment's commentary has been skipped
7329 // by the command of the last loop
7330
7331 // Read and store the number of segments
7332 unsigned dummy_seg;
7333 unsigned inelements;
7335
7336 unsigned segment_markers;
7338
7339 // Marker list should be provided by the user to assign
7340 // each segment to a boundary
7341#ifdef PARANOID
7342 if (segment_markers != 1)
7343 {
7344 std::ostringstream error_stream;
7345 error_stream << "The segment marker should be provided \n"
7346 << "In order to assign each segment to a boundary \n "
7347 << std::endl;
7348
7349 throw OomphLibError(
7351 }
7352#endif
7353
7354 triangulate_io.numberofsegments = inelements;
7355 triangulate_io.segmentlist =
7356 (int*)malloc(triangulate_io.numberofsegments * 2 * sizeof(int));
7357 triangulate_io.segmentmarkerlist =
7358 (int*)malloc(triangulate_io.numberofsegments * sizeof(int));
7359
7360 // Read all the segments edges and markers
7361 for (unsigned i = 0; i < 2 * inelements; i += 2)
7362 {
7364 poly_file >> triangulate_io.segmentlist[i];
7365 poly_file >> triangulate_io.segmentlist[i + 1];
7366 if (segment_markers != 0)
7367 {
7368 poly_file >> triangulate_io.segmentmarkerlist[i / 2];
7369 }
7370
7371 // Skip line with commentary
7372 poly_file.ignore(80, '\n');
7373 }
7374
7375 // Read and store the number of holes if given
7376 // Skip line with commentary
7377 if (getline(poly_file, test_string, '#'))
7378 {
7379 poly_file.ignore(80, '\n');
7380
7381 unsigned dummy_hole;
7382 unsigned nhole;
7383 poly_file >> nhole;
7384
7385 triangulate_io.numberofholes = nhole;
7386 triangulate_io.holelist =
7387 (double*)malloc(triangulate_io.numberofholes * 2 * sizeof(double));
7388
7389 // Loop over the holes to get centre coords and store value onto the
7390 // TriangulateIO object
7391 for (unsigned i = 0; i < 2 * nhole; i += 2)
7392 {
7394 poly_file >> triangulate_io.holelist[i];
7395 poly_file >> triangulate_io.holelist[i + 1];
7396 }
7397 }
7398
7399 // Read and store the number of regions if given
7400 // Skip line with commentary
7401 if (getline(poly_file, test_string, '#'))
7402 {
7403 poly_file.ignore(80, '\n');
7404
7405 unsigned dummy_region;
7406 unsigned nregion;
7407 poly_file >> nregion;
7408 std::cerr << "Regions: " << nregion << std::endl;
7409 getchar();
7410
7411 triangulate_io.numberofregions = nregion;
7412 triangulate_io.regionlist =
7413 (double*)malloc(triangulate_io.numberofregions * 4 * sizeof(double));
7414
7415 // Check for using regions
7416 if (nregion > 0)
7417 {
7418 use_attributes = true;
7419 }
7420
7421 // Loop over the regions to get coords and store value onto the
7422 // TriangulateIO object
7423 for (unsigned i = 0; i < nregion; i++)
7424 {
7426 poly_file >> triangulate_io.regionlist[4 * i];
7427 poly_file >> triangulate_io.regionlist[4 * i + 1];
7428 poly_file >> triangulate_io.regionlist[4 * i + 2];
7429 triangulate_io.regionlist[4 * i + 3] = 0.0;
7430 }
7431 }
7432 }
7433
7434#endif
7435
7436#ifdef OOMPH_HAS_TRIANGLE_LIB
7437#ifdef OOMPH_HAS_MPI
7438
7439 //======================================================================
7440 /// Used to dump info. related with distributed triangle meshes
7441 //======================================================================
7442 template<class ELEMENT>
7444 std::ostream& dump_file)
7445 {
7446 // First check that the mesh is distributed
7447 if (this->is_mesh_distributed())
7448 {
7449 // Save the original number of boundaries
7450 const unsigned nboundary = this->nboundary();
7451 dump_file << nboundary << " # number of original boundaries" << std::endl;
7452
7453 // Save the number of shared boundaries
7454 const unsigned nshared_boundaries = this->nshared_boundaries();
7455 dump_file << nshared_boundaries << " # number of shared boundaries"
7456 << std::endl;
7457
7458 // Save the initial and final shared boundaries ids
7459 const unsigned init_shd_bnd_id = this->initial_shared_boundary_id();
7460 dump_file << init_shd_bnd_id << " # initial shared boundaries id"
7461 << std::endl;
7462
7463 const unsigned final_shd_bnd_id = this->final_shared_boundary_id();
7464 dump_file << final_shd_bnd_id << " # final shared boundaries id"
7465 << std::endl;
7466
7467 // Save the number of processors
7468 const unsigned nprocs = this->shared_boundaries_ids().size();
7469 dump_file << nprocs << " # number of processors" << std::endl;
7470
7471 // Now save the processors ids and the shared boundary created
7472 // by them
7473 for (unsigned ip = 0; ip < nprocs; ip++)
7474 {
7475 for (unsigned jp = 0; jp < nprocs; jp++)
7476 {
7477 if (ip != jp)
7478 {
7479 // Get the number of shared boundaries with it these two
7480 // processors
7481 const unsigned nshared_boundaries_iproc_jproc =
7482 this->shared_boundaries_ids(ip, jp).size();
7483
7484 // Save the number of shared boundaries with in these two
7485 // processors
7487 << " # number of shared boundaries with in two "
7488 << "processors" << std::endl;
7489 for (unsigned is = 0; is < nshared_boundaries_iproc_jproc; is++)
7490 {
7491 const unsigned shared_boundary_id =
7492 this->shared_boundaries_ids(ip, jp, is);
7493 dump_file << ip << " " << jp << " " << shared_boundary_id
7494 << " # ip jp shared_boundary of processors ip and jp"
7495 << std::endl;
7496
7497 } // for (is < nshared_boundaries_iproc_jproc)
7498 }
7499 } // for (jp < nprocs)
7500 } // for (ip < nprocs)
7501
7502 // Now save the info. that states which shared boundary overlaps
7503 // an internal boundary
7504
7505 // First check if there are shared boundaries overlapping internal
7506 // boundaries
7508 this->nshared_boundary_overlaps_internal_boundary();
7510 << " # number of shared boundaries that overlap internal "
7511 << "boundaries" << std::endl;
7512
7514 {
7515 for (unsigned isb = init_shd_bnd_id; isb < final_shd_bnd_id; isb++)
7516 {
7517 // Check if the current shared boundary overlaps an internal
7518 // boundary
7519 if (this->shared_boundary_overlaps_internal_boundary(isb))
7520 {
7521 // Which internal boundary is overlapped by the shared
7522 // boundary
7523 const unsigned overlapped_internal_boundary =
7524 shared_boundary_overlapping_internal_boundary(isb);
7525 // Save the shared boundary that overlaps the internal boundary
7527 << " # the shared boundary overlaps the internal "
7528 << "boundary " << std::endl;
7529
7530 } // if (this->shared_boundary_overlaps_internal_boundary(isb))
7531 } // for (isb < final_shd_bnd_id)
7532 } // if (nshared_boundaries_overlap_internal_boundaries > 0)
7533
7534 // Now save the info. related with the initial and final
7535 // boundary coordinates for each original boundary
7536
7537 // Go through all the (original) boundaries to update the initial
7538 // and final boundary coordinates
7539 for (unsigned b = 0; b < nboundary; b++)
7540 {
7541 // Check if the boundary zeta coordinates for this boundary have
7542 // been already assigned, if that is the case then state the
7543 // flag to know that info. should be read
7544 if (Assigned_segments_initial_zeta_values[b])
7545 {
7546 // The boundary coordinates have been computed then state
7547 // the flag and save the info.
7548 dump_file << "1 # assigned boundary coordinates initial zeta values"
7549 << std::endl;
7550
7551 // Save the initial and final boundary coordinates, same as
7552 // the initial and final zeta values for each boundary
7553
7554 // First the vertices coordinates
7556 this->boundary_initial_coordinate(b);
7557
7558 Vector<double> final_coordinates = this->boundary_final_coordinate(b);
7559
7560 dump_file << std::setprecision(14) << initial_coordinates[0] << " "
7562 << " # initial coordinates for the current boundary"
7563 << std::endl;
7564
7565 dump_file << std::setprecision(14) << final_coordinates[0] << " "
7566 << final_coordinates[1]
7567 << " # final coordinates for the current boundary"
7568 << std::endl;
7569
7570 // ... then the zeta values
7571
7572#ifdef PARANOID
7573 // Get the number of zeta coordinates (should be one)
7574 const unsigned zeta_size =
7575 this->boundary_initial_zeta_coordinate(b).size();
7576
7577 if (zeta_size != 1)
7578 {
7579 std::ostringstream error_message;
7580 error_message
7581 << "The dimension for the zeta values container is different\n"
7582 << "from 1, the current implementation only supports\n"
7583 << "one-dimensioned zeta containers\n\n";
7584 throw OomphLibError(
7585 error_message.str(),
7586 "TriangleMesh::dump_distributed_info_for_restart()",
7588 }
7589#endif
7590
7592 this->boundary_initial_zeta_coordinate(b);
7593 Vector<double> zeta_final = this->boundary_final_zeta_coordinate(b);
7594
7595 dump_file << std::setprecision(14) << zeta_initial[0]
7596 << " # initial zeta value for the current boundary"
7597 << std::endl;
7598
7599 dump_file << std::setprecision(14) << zeta_final[0]
7600 << " # final zeta value for the current boundary"
7601 << std::endl;
7602
7603 // Get the number of segments of the current boundary
7604 const unsigned nsegments = this->nboundary_segment(b);
7605 // Save the number of segments of the current boundary
7606 dump_file << b << " " << nsegments
7607 << " # of segments for the current boundary" << std::endl;
7608
7609 // ... and then save that info for each segments
7610 for (unsigned is = 0; is < nsegments; is++)
7611 {
7612 // First the vertices coordinates
7614 this->boundary_segment_initial_coordinate(b)[is];
7616 this->boundary_segment_final_coordinate(b)[is];
7617
7618 dump_file
7619 << std::setprecision(14) << initial_segment_coordinates[0] << " "
7621 << " # initial segment coordinates for the current boundary"
7622 << std::endl;
7623
7624 dump_file << std::setprecision(14) << final_segment_coordinates[0]
7625 << " " << final_segment_coordinates[1]
7626 << " # final segment coordinates for the current boundary"
7627 << std::endl;
7628
7629 // ... then the zeta values
7630
7631 if (this->boundary_geom_object_pt(b) != 0)
7632 {
7633 const double zeta_segment_initial =
7634 this->boundary_segment_initial_zeta(b)[is];
7635 const double zeta_segment_final =
7636 this->boundary_segment_final_zeta(b)[is];
7637
7638 dump_file
7639 << std::setprecision(14) << zeta_segment_initial
7640 << " # initial segment zeta value for the current boundary"
7641 << std::endl;
7642
7643 dump_file
7644 << std::setprecision(14) << zeta_segment_final
7645 << " # final segment zeta value for the current boundary"
7646 << std::endl;
7647 }
7648 else
7649 {
7650 const double arclength_segment_initial =
7651 this->boundary_segment_initial_arclength(b)[is];
7652 const double arclength_segment_final =
7653 this->boundary_segment_final_arclength(b)[is];
7654
7655 dump_file
7656 << std::setprecision(14) << arclength_segment_initial
7657 << " # initial segment arclength for the current boundary"
7658 << std::endl;
7659
7660 dump_file << std::setprecision(14) << arclength_segment_final
7661 << " # final segment arclength for the current boundary"
7662 << std::endl;
7663
7664 } // else if (this->boundary_geom_object_pt(b)!=0)
7665
7666 } // for (is < nsegments)
7667
7668 } // if (Assigned_segments_initial_zeta_values[b])
7669 else
7670 {
7671 // The boundary coordinates have NOT been computed then state
7672 // the flag and save the info.
7673 dump_file << "0 # assigned boundary coordinates initial zeta values"
7674 << std::endl;
7675 }
7676
7677 } // for (b < nboundary)
7678
7679 } // if (this->is_mesh_distributed())
7680 }
7681
7682 //======================================================================
7683 /// Used to read info. related with distributed triangle meshes
7684 //======================================================================
7685 template<class ELEMENT>
7687 std::istream& restart_file)
7688 {
7689 // First check that the mesh is distributed
7690 if (this->is_mesh_distributed())
7691 {
7692 // Read the number of original boundaries
7693 const unsigned n_boundary = read_unsigned_line_helper(restart_file);
7694
7695#ifdef PARANOID
7696 if (n_boundary != this->nboundary())
7697 {
7698 std::ostringstream error_message;
7699 error_message
7700 << "The number of boundaries (" << n_boundary << ") on the "
7701 << "file used for restarting is different\nfrom the number of "
7702 << "boundaries (" << this->nboundary() << ") on the current "
7703 << "mesh!!!\n\n\n";
7704 throw OomphLibError(error_message.str(),
7705 "TriangleMesh::read_distributed_info_for_restart()",
7707 }
7708#endif
7709
7710 // Read the number of shared boundaries
7711 unsigned n_shared_boundaries = read_unsigned_line_helper(restart_file);
7712 // We need to read the data because it comes in the file (add and
7713 // substract to avoid compilation warning)
7716
7717 // Read the initial and final shared boundaries ids
7718 unsigned init_shd_bnd_id = read_unsigned_line_helper(restart_file);
7719 // We need to read the data because it comes in the file (add and
7720 // substract to avoid compilation warning)
7723 // Add and substract to avoid compilation warning
7724 unsigned final_shd_bnd_id = read_unsigned_line_helper(restart_file);
7725 // We need to read the data because it comes in the file (add and
7726 // substract to avoid compilation warning)
7729
7730 // Read the number of processors involved in the generation of
7731 // mesh before restart
7732 const unsigned n_procs = read_unsigned_line_helper(restart_file);
7733
7734#ifdef PARANOID
7735 if (static_cast<int>(n_procs) != this->communicator_pt()->nproc())
7736 {
7737 std::ostringstream error_message;
7738 error_message
7739 << "The number of previously used processors (" << n_procs
7740 << ") (read from the restart file) is different\nfrom the "
7741 << "number of current used processors ("
7742 << this->communicator_pt()->nproc() << ")\n\n";
7743 throw OomphLibError(error_message.str(),
7744 "TriangleMesh::read_distributed_info_for_restart()",
7746 }
7747#endif
7748
7749 // Clear all previuos info. related with shared boundaries
7750 this->shared_boundaries_ids().clear();
7751 this->shared_boundary_from_processors().clear();
7752 this->shared_boundary_overlaps_internal_boundary().clear();
7753
7754 // Create the storage for the shared boundaries ids related with
7755 // the processors
7756 this->shared_boundaries_ids().resize(n_procs);
7757
7758 // Now read the processors ids and the shared boundary created
7759 // by them
7760 for (unsigned ip = 0; ip < n_procs; ip++)
7761 {
7762 // Create the storage for the shared boundaries ids related with
7763 // the processors
7764 this->shared_boundaries_ids(ip).resize(n_procs);
7765 for (unsigned jp = 0; jp < n_procs; jp++)
7766 {
7767 if (ip != jp)
7768 {
7769 // Read the number of shared boundaries with in these two
7770 // processors
7771 const unsigned nshared_boundaries_iproc_jproc =
7772 read_unsigned_line_helper(restart_file);
7773 for (unsigned is = 0; is < nshared_boundaries_iproc_jproc; is++)
7774 {
7775 // Get the processors
7776 unsigned tmp_ip;
7778 unsigned tmp_jp;
7780
7781 // Get the shared boundary id created by these two
7782 // processors
7783 const unsigned shared_boundary_id =
7784 read_unsigned_line_helper(restart_file);
7785
7786 // Update the info. of the processors that give rise to
7787 // the shared boundaries
7788 this->shared_boundaries_ids(ip, jp).push_back(shared_boundary_id);
7789
7790 // Update the structure that states the processors that
7791 // gave rise to the shared boundary
7793 processors[0] = ip;
7794 processors[1] = jp;
7795 this->shared_boundary_from_processors()[shared_boundary_id] =
7796 processors;
7797
7798 } // for (is < nshared_boundaries_iproc_jproc)
7799 }
7800 } // for (jp < n_procs)
7801 } // for (ip < n_procs)
7802
7803 // Now read the info. that states which shared boundary overlaps
7804 // an internal boundary
7805
7806 // First check if there are shared boundaries overlapping internal
7807 // boundaries
7809 read_unsigned_line_helper(restart_file);
7810
7811 for (unsigned isb = 0;
7813 isb++)
7814 {
7815 // Read the shared boundary that overlaps an internal boundary
7818 // ... and read the internal boundary that overlaps
7819 const unsigned overlapped_internal_boundary =
7820 read_unsigned_line_helper(restart_file);
7821
7822 // Re-establish the info. of the shared boundaries overlapped
7823 // by internal boundaries
7824 this->shared_boundary_overlaps_internal_boundary()
7826 } // for (isb < nshared_boundaries_overlap_internal_boundaries)
7827
7828 // Now read the info. related with the initial and final
7829 // boundary coordinates for each original boundary
7830
7831 // Go through all the (original) boundaries to update the initial
7832 // and final boundary coordinates
7833 for (unsigned b = 0; b < n_boundary; b++)
7834 {
7835 // For each boundary check if the boundary coordinates initial
7836 // and final zeta vales were assigned in the restart file
7838 read_unsigned_line_helper(restart_file);
7839
7841 {
7842 // Clear any previous stored info. There should not be
7843 // info. already stored but better clear the info. for the
7844 // boundary
7845 Boundary_initial_coordinate[b].clear();
7846 Boundary_final_coordinate[b].clear();
7847
7848 Boundary_initial_zeta_coordinate[b].clear();
7849 Boundary_final_zeta_coordinate[b].clear();
7850
7851 // The info. for the segments
7852 Boundary_segment_inverted[b].clear();
7853 Boundary_segment_initial_coordinate[b].clear();
7854 Boundary_segment_final_coordinate[b].clear();
7855
7856 Boundary_segment_initial_zeta[b].clear();
7857 Boundary_segment_final_zeta[b].clear();
7858
7859 Boundary_segment_initial_arclength[b].clear();
7860 Boundary_segment_final_arclength[b].clear();
7861
7862 // Read the initial and final boundary coordinates, same as
7863 // the initial and final zeta values for each boundary
7864
7865 // First the vertices coordinates
7867
7868 // Read the initial coordinates
7870
7871 // Ignore rest of line
7872 restart_file.ignore(80, '\n');
7873
7875
7876 // Read the final coordinates
7878
7879 // Ignore rest of line
7880 restart_file.ignore(80, '\n');
7881
7882 // Set the values in the containers
7883
7884 this->boundary_initial_coordinate(b) = initial_coordinates;
7885 this->boundary_final_coordinate(b) = final_coordinates;
7886
7887 // ... now read the zeta values
7890
7891 // Ignore rest of line
7892 restart_file.ignore(80, '\n');
7893
7896
7897 // Ignore rest of line
7898 restart_file.ignore(80, '\n');
7899
7900 // Set the values in the containers
7901 this->boundary_initial_zeta_coordinate(b) = zeta_initial;
7902 this->boundary_final_zeta_coordinate(b) = zeta_final;
7903
7904 // Get the curent boundary id from the restart file
7905 unsigned current_boundary;
7907
7908#ifdef PARANOID
7909 if (current_boundary != b)
7910 {
7911 std::ostringstream error_message;
7912 error_message
7913 << "The current boundary id from the restart file ("
7914 << current_boundary << ") is different from\nthe boundary id "
7915 << b << "currently used to re-establish the initial and\nfinal "
7916 << "segment's zeta values\n\n";
7917 throw OomphLibError(
7918 error_message.str(),
7919 "TriangleMesh::read_distributed_info_for_restart()",
7921 }
7922#endif
7923
7924 // ... and its number of segments
7925 unsigned nsegments;
7926 restart_file >> nsegments;
7927
7928 // Ignore rest of line
7929 restart_file.ignore(80, '\n');
7930
7931 // Now read all the segments info.
7932
7933 // ... and then save that info for each segments
7934 for (unsigned is = 0; is < nsegments; is++)
7935 {
7936 // First the vertices coordinates
7938
7939 // Read the initial coordinates
7942
7943 // Ignore rest of line
7944 restart_file.ignore(80, '\n');
7945
7947
7948 // Read the final coordinates
7951
7952 // Ignore rest of line
7953 restart_file.ignore(80, '\n');
7954
7955 // Set the values in the containers
7956 this->boundary_segment_initial_coordinate(b).push_back(
7958 this->boundary_segment_final_coordinate(b).push_back(
7960
7961 // ... then the zeta values for the segment
7962 if (this->boundary_geom_object_pt(b) != 0)
7963 {
7966
7967 // Ignore rest of line
7968 restart_file.ignore(80, '\n');
7969
7972
7973 // Ignore rest of line
7974 restart_file.ignore(80, '\n');
7975
7976 // Set the values in the containers for the segment
7977 this->boundary_segment_initial_zeta(b).push_back(
7979 this->boundary_segment_final_zeta(b).push_back(
7981 }
7982 else
7983 {
7986
7987 // Ignore rest of line
7988 restart_file.ignore(80, '\n');
7989
7992
7993 // Ignore rest of line
7994 restart_file.ignore(80, '\n');
7995
7996 // Set the values in the containers for the segment
7997 this->boundary_segment_initial_arclength(b).push_back(
7999 this->boundary_segment_final_arclength(b).push_back(
8001 } // else if (this->boundary_geom_object_pt(b)!=0)
8002
8003 } // for (is < nsegments)
8004
8005 } // if (boundary_coordinates_initial_zeta_values_assigned)
8006
8007 } // for (b < n_boundary)
8008
8009 } // if (this->is_mesh_distributed())
8010 }
8011
8012#endif // #ifdef OOMPH_HAS_MPI
8013#endif // #ifdef OOMPH_HAS_TRIANGLE_LIB
8014
8015 //===================================================================
8016 // Output the nodes on the boundaries and their / respective boundary
8017 // coordinates(into separate tecplot / zones)
8018 //===================================================================
8019 template<class ELEMENT>
8021 std::ostream& outfile)
8022 {
8023 // First get all the elements adjacent to the given boundary, then
8024 // the face elements and extract the nodes on the boundaries using
8025 // the face elements. We can not use the data structure
8026 // Boundary_node_pt since the multi_domain functions add nodes there
8027 // without assigning the required boundary coordinate
8028
8029 // Store the nodes in a set so we do not have repeated nodes
8030 std::set<Node*> boundary_nodes_pt;
8031 const unsigned n_boundary_ele = this->nboundary_element(b);
8032 for (unsigned e = 0; e < n_boundary_ele; e++)
8033 {
8034 // Get the boundary bulk element
8035 FiniteElement* bulk_ele_pt = this->boundary_element_pt(b, e);
8036#ifdef OOMPH_HAS_MPI
8037 // Only work with nonhalo elements if the mesh is distributed
8038 if (!bulk_ele_pt->is_halo())
8039 {
8040#endif
8041 // Get the face index
8042 int face_index = this->face_index_at_boundary(b, e);
8043 // Create the face element
8045 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
8046
8047 // Get the number of nodes on the face element
8048 const unsigned n_nodes = face_ele_pt->nnode();
8049 for (unsigned i = 0; i < n_nodes; i++)
8050 {
8051 // Get the nodes in the face elements
8053 // Add the nodes to the set of boundary nodes
8055 } // for (i < n_nodes)
8056
8057 // Free the memory allocated for the face element
8058 delete face_ele_pt;
8059 face_ele_pt = 0;
8060#ifdef OOMPH_HAS_MPI
8061 } // if (!bulk_ele_pt->is_halo())
8062#endif
8063
8064 } // for (e < n_boundary_ele)
8065
8066 outfile << "ZONE T=\"Boundary nodes" << b << "\"\n";
8067 // Set to store the boundary nodes in order
8068 std::set<Vector<double>> set_node_coord;
8069 // Loop over the nodes on the boundary and store them in the set
8070 for (std::set<Node*>::iterator it = boundary_nodes_pt.begin();
8071 it != boundary_nodes_pt.end();
8072 it++)
8073 {
8074 Node* inode_pt = (*it);
8075
8076 // Get the node coordinates
8077 const unsigned n_dim = inode_pt->ndim();
8079
8080 // Get the boundary coordinate
8082 inode_pt->get_coordinates_on_boundary(b, zeta);
8083 node_coord[0] = zeta[0];
8084 for (unsigned j = 0; j < n_dim; j++)
8085 {
8086 node_coord[j + 1] = inode_pt->x(j);
8087 }
8088 set_node_coord.insert(node_coord);
8089 }
8090
8091 for (std::set<Vector<double>>::iterator it = set_node_coord.begin();
8092 it != set_node_coord.end();
8093 it++)
8094 {
8095 // Get the node coordinates
8096 Vector<double> node_coord = (*it);
8097
8098 // Output the node coordinates
8099 const unsigned n_dim = node_coord.size() - 1;
8100 for (unsigned j = 0; j < n_dim; j++)
8101 {
8102 outfile << node_coord[j + 1] << " ";
8103 }
8104 // ... add an extra coordinate to avoid error with tecplot
8105 outfile << "0.0" << std::endl;
8106 }
8107
8108 // ... loop again to plot the bound coordinates
8109 outfile << "ZONE T=\"Boundary coordinates " << b << "\"\n";
8110 for (std::set<Vector<double>>::iterator it = set_node_coord.begin();
8111 it != set_node_coord.end();
8112 it++)
8113 {
8114 // Get the node coordinates
8115 Vector<double> node_coord = (*it);
8116
8117 // Output the node coordinates
8118 const unsigned n_dim = node_coord.size() - 1;
8119 for (unsigned j = 0; j < n_dim; j++)
8120 {
8121 outfile << node_coord[j + 1] << " ";
8122 }
8123
8124 // Output the boundary coordinate
8125 outfile << node_coord[0] << std::endl;
8126 }
8127 }
8128
8129#ifdef OOMPH_HAS_MPI
8130 //====================================================================
8131 // Creates the distributed domain representation. Joins the
8132 // original boundaires, shared boundaries and creates connections among
8133 // them to create the new polygons that represent the distributed
8134 // domain
8135 //====================================================================
8136 template<class ELEMENT>
8140 {
8141 // Get the outer polygons, internal polygons, internal open curves
8142 // and join them with the shared polylines to create the distributed
8143 // domain representation (the new outer, internal polygons, and new
8144 // internal open curves)
8145
8146 // Get the rank of the current processor
8147 const unsigned my_rank = this->communicator_pt()->my_rank();
8148
8149 // *********************************************************************
8150 // Step (2) Get the outer, internal and shared boundaries to create the
8151 // new polygons
8152 // *********************************************************************
8153
8154 // *********************************************************************
8155 // Step (2.1) Get the outer boundaries and check if it is necessary to use
8156 // a new representation (for example when the boundary was splitted in
8157 // the distribution process)
8158 // *********************************************************************
8159
8160 // Storage for new created polylines, non sorted
8162
8163 // Storing for the polylines on the boundaries
8164 // The first index is for a set of connected polylines
8165 // The second index is for a polyline on a set of connected polylines
8167
8168 // Copy the outer boundaries to the vector of polylines
8169 const unsigned nouter = this->Outer_boundary_pt.size();
8170 for (unsigned i = 0; i < nouter; i++)
8171 {
8172 const unsigned npolylines = this->Outer_boundary_pt[i]->npolyline();
8173 for (unsigned p = 0; p < npolylines; p++)
8174 {
8175 // Pointer to the current polyline
8177 this->Outer_boundary_pt[i]->polyline_pt(p);
8178 const unsigned nvertex = tmp_polyline_pt->nvertex();
8179 if (nvertex > 0)
8180 {
8181 // Get the boundary id of the polyline and check if that boundary
8182 // needs a new representation (for example when the boundary was
8183 // splitted in the distribution process)
8184 const unsigned bound_id = tmp_polyline_pt->boundary_id();
8185 if (!boundary_was_splitted(bound_id))
8186 {
8188 } // if (!boundary_was_splitted(bound_id))
8189 else
8190 {
8191 // Get the polylines that will represent this boundary
8193 boundary_subpolylines(bound_id);
8194 const unsigned nsub_poly = tmp_vector_polylines.size();
8195#ifdef PARANOID
8196 if (nsub_poly <= 1)
8197 {
8198 std::ostringstream error_message;
8199 error_message << "The boundary (" << bound_id
8200 << ") was marked to be splitted but\n"
8201 << "there are only (" << nsub_poly
8202 << ") polylines to represent it.\n";
8203 throw OomphLibError(error_message.str(),
8206 }
8207#endif
8208 // Add the new representation of the polylines (sub-polylines)
8209 // to represent this boundary
8210 for (unsigned isub = 0; isub < nsub_poly; isub++)
8211 {
8213#ifdef PARANOID
8214 const unsigned nsvertex = tmp_vector_polylines[isub]->nvertex();
8215 if (nsvertex == 0)
8216 {
8217 std::ostringstream error_message;
8218 error_message
8219 << "The current chunk (" << isub << ") of the polyline with\n"
8220 << "boundary id (" << bound_id << ") has no vertices\n";
8221 throw OomphLibError(error_message.str(),
8224 } // if (nsvertex == 0)
8225#endif // #ifdef PARANOID
8226 } // for (isub < nsub_poly)
8227 } // else if (!boundary_was_splitted(bound_id))
8228 } // if (nvertex > 0)
8229 } // for (p < npolylines)
8230 } // for (i < nouter)
8231
8232 // Get the number of unsorted polylines
8235 {
8236 // Now that we have all the new unsorted polylines it is time to sort them
8237 // so they be all contiguous
8238 sort_polylines_helper(unsorted_outer_polyline_pt, sorted_outer_curves_pt);
8239
8240 } // if (nunsorted_outer_polyline > 0)
8241
8242 // *********************************************************************
8243 // Step (2.2) Get the internal closed boundaries and check if it is
8244 // necessary to use a new representation (for example when the boundary
8245 // was splitted in the distribution process)
8246 // *********************************************************************
8247
8248 // Storage for new created polylines, non sorted
8250
8251 // Storing for the polylines on the boundaries
8252 // The first index is for a set of connected polylines
8253 // The second index is for a polyline on a set of connected polylines
8255
8256 // Copy the internal closed boundaries to the vector of polylines
8257 const unsigned ninternal_closed = this->Internal_polygon_pt.size();
8258 for (unsigned i = 0; i < ninternal_closed; i++)
8259 {
8260 const unsigned npolylines = this->Internal_polygon_pt[i]->npolyline();
8261 for (unsigned p = 0; p < npolylines; p++)
8262 {
8263 // Pointer to the current polyline
8265 this->Internal_polygon_pt[i]->polyline_pt(p);
8266 const unsigned nvertex = tmp_polyline_pt->nvertex();
8267 if (nvertex > 0)
8268 {
8269 // Get the boundary id of the polyline and check if that boundary
8270 // needs a new representation (for example when the boundary was
8271 // splitted in the distribution process)
8272 const unsigned bound_id = tmp_polyline_pt->boundary_id();
8273 if (!boundary_was_splitted(bound_id))
8274 {
8276 } // if (!boundary_was_splitted(bound_id))
8277 else
8278 {
8279 // Get the polylines that will represent this boundary
8281 boundary_subpolylines(bound_id);
8282 const unsigned nsub_poly = tmp_vector_polylines.size();
8283#ifdef PARANOID
8284 if (nsub_poly <= 1)
8285 {
8286 std::ostringstream error_message;
8287 error_message << "The boundary (" << bound_id
8288 << ") was marked to be splitted but\n"
8289 << "there are only (" << nsub_poly
8290 << ") polylines to represent it.\n";
8291 throw OomphLibError(error_message.str(),
8294 }
8295#endif
8296 // Add the new representation of the polylines (sub-polylines)
8297 // to represent this boundary
8298 for (unsigned isub = 0; isub < nsub_poly; isub++)
8299 {
8302#ifdef PARANOID
8303 const unsigned nsvertex = tmp_vector_polylines[isub]->nvertex();
8304 if (nsvertex == 0)
8305 {
8306 std::ostringstream error_message;
8307 error_message
8308 << "The current chunk (" << isub << ") of the polyline with\n"
8309 << "boundary id (" << bound_id << ") has no vertices\n";
8310 throw OomphLibError(error_message.str(),
8313 } // if (nsvertex == 0)
8314#endif // #ifdef PARANOID
8315 } // for (isub < nsub_poly)
8316 } // else if (!boundary_was_splitted(bound_id))
8317 } // if (nvertex > 0)
8318 } // for (p < npolylines)
8319 } // for (i < ninternal_closed)
8320
8321 const unsigned nunsorted_internal_closed_polyline =
8323
8325 {
8326 // Now that we have all the new unsorted polylines it is time to sort them
8327 // so they be all contiguous
8328 sort_polylines_helper(unsorted_internal_closed_polyline_pt,
8330 }
8331
8332 // *********************************************************************
8333 // Step (2.3) Get the internal open boundaries and check if it is
8334 // necessary to use a new representation (for example when the boundary
8335 // was splitted in the distribution process)
8336 // *********************************************************************
8337
8338 // Storage for new created polylines, non sorted
8340
8341 // Storing for the polylines on the boundaries
8342 // The first index is for a set of connected polylines
8343 // The second index is for a polyline on a set of connected polylines
8345
8346 // Copy the internal open boundaries to the vector of polylines
8347 const unsigned ninternal_open = this->Internal_open_curve_pt.size();
8348 for (unsigned i = 0; i < ninternal_open; i++)
8349 {
8350 const unsigned ncurve_section =
8351 this->Internal_open_curve_pt[i]->ncurve_section();
8352 for (unsigned p = 0; p < ncurve_section; p++)
8353 {
8354 // Pointer to the current polyline
8356 this->Internal_open_curve_pt[i]->polyline_pt(p);
8357 const unsigned nvertex = tmp_polyline_pt->nvertex();
8358 if (nvertex > 0)
8359 {
8360 // Get the boundary id of the polyline and check if that boundary
8361 // needs a new representation (for example when the boundary was
8362 // splitted in the distribution process)
8363 const unsigned bound_id = tmp_polyline_pt->boundary_id();
8364 if (!boundary_was_splitted(bound_id))
8365 {
8366 // Only include as internal boundaries those not marked as
8367 // shared boundaries
8368 if (!boundary_marked_as_shared_boundary(bound_id, 0))
8369 {
8371 }
8372 } // if (!boundary_was_splitted(bound_id))
8373 else
8374 {
8375 // Get the polylines that will represent this boundary
8377 boundary_subpolylines(bound_id);
8378 const unsigned nsub_poly = tmp_vector_polylines.size();
8379#ifdef PARANOID
8380 if (nsub_poly <= 1)
8381 {
8382 std::ostringstream error_message;
8383 error_message << "The boundary (" << bound_id
8384 << ") was marked to be splitted but\n"
8385 << "there are only (" << nsub_poly
8386 << ") polylines to represent it.\n";
8387 throw OomphLibError(error_message.str(),
8390 }
8391#endif
8392 // Add the new representation of the polylines (sub-polylines)
8393 // to represent this boundary
8394 for (unsigned isub = 0; isub < nsub_poly; isub++)
8395 {
8396 // Only include as internal boundaries those not marked as
8397 // shared boundaries
8398 if (!boundary_marked_as_shared_boundary(bound_id, isub))
8399 {
8402 }
8403#ifdef PARANOID
8404 const unsigned nsvertex = tmp_vector_polylines[isub]->nvertex();
8405 if (nsvertex == 0)
8406 {
8407 std::ostringstream error_message;
8408 error_message
8409 << "The current chunk (" << isub << ") of the polyline with\n"
8410 << "boundary id (" << bound_id << ") has no vertices\n";
8411 throw OomphLibError(error_message.str(),
8414 } // if (nsvertex == 0)
8415#endif // #ifdef PARANOID
8416 } // for (isub < nsub_poly)
8417 } // else if (!boundary_was_splitted(bound_id))
8418 } // if (nvertex > 0)
8419 } // for (p < npolylines)
8420 } // for (i < ninternal_open)
8421
8422 const unsigned nunsorted_internal_open_polyline =
8424
8426 {
8427 // Now that we have all the new unsorted polylines it is time to sort them
8428 // so they be all contiguous
8429 sort_polylines_helper(unsorted_internal_open_polyline_pt,
8431 }
8432
8433 // ********************************************************************
8434 // Step (2.4) Sort the polylines on the shared boundaries
8435 // ********************************************************************
8436
8437 // Storage for new created polylines, non sorted
8439
8440 // Special storage for the shared polylines that will be also used
8441 // to connect with the internal boundaries
8443
8444 // Storing for the polylines on the shared boundaries
8445 // The first index is for a set of connected polylines
8446 // The second index is for a polyline on a set of connected polylines
8448
8449 // Copy the shared boudaries to the vector of polylines
8450 const unsigned ncurves = nshared_boundary_curves(my_rank);
8451 for (unsigned i = 0; i < ncurves; i++)
8452 {
8453 const unsigned npolylines = nshared_boundary_polyline(my_rank, i);
8454 for (unsigned p = 0; p < npolylines; p++)
8455 {
8456 const unsigned nvertex =
8457 shared_boundary_polyline_pt(my_rank, i, p)->nvertex();
8458 if (nvertex > 0)
8459 {
8461 shared_boundary_polyline_pt(my_rank, i, p);
8462
8463 // First check if there are shared boundaries overlapping
8464 // internal boundaries
8465 if (this->nshared_boundary_overlaps_internal_boundary() > 0)
8466 {
8467 // Get the boundary id of the shared polyline
8468 const unsigned shd_bnd_id = tmp_shared_poly_pt->boundary_id();
8469 // If the shared polyline is marked as internal boundary
8470 // then include it in the special storage to look for
8471 // connection with internal boundaries
8472 if (this->shared_boundary_overlaps_internal_boundary(shd_bnd_id))
8473 {
8476 }
8477 }
8479 }
8480 }
8481 }
8482
8483 // Get the total number of shared polylines
8484 const unsigned nunsorted_shared_polyline =
8486
8488 {
8489 // Now that we have all the new unsorted polylines it is time to
8490 // sort them so they be all contiguous
8491 sort_polylines_helper(unsorted_shared_polyline_pt,
8493 }
8494
8495 // ********************************************************************
8496 // Step (3) Join the boundaries (shared, internal and outer to
8497 // create the new polygons)
8498 // ********************************************************************
8499
8500 // Create the set of curves that will be used to create the new polygons
8501 // Get the total number of curves
8502 const unsigned nouter_curves = sorted_outer_curves_pt.size();
8503 const unsigned ninternal_closed_curves =
8505 const unsigned nshared_curves = sorted_shared_curves_pt.size();
8506 const unsigned ntotal_curves =
8508
8509 // Add all the polylines to a container
8510 unsigned counter = 0;
8512
8513 // Add the shared curves first, this ensure the generation of
8514 // internal polygons defined by the shared boundaries
8515 for (unsigned i = 0; i < nshared_curves; i++, counter++)
8516 {
8518 }
8519
8520 // Add the internal polygons (if any)
8521 for (unsigned i = 0; i < ninternal_closed_curves; i++, counter++)
8522 {
8524 }
8525
8526 // Add the outer polygons
8527 for (unsigned i = 0; i < nouter_curves; i++, counter++)
8528 {
8530 }
8531
8532 // Create the temporary version of the domain by joining the new
8533 // polylines
8534 this->create_tmp_polygons_helper(all_curves_pt, polygons_pt);
8535 // Create the new open curves
8536 this->create_tmp_open_curves_helper(sorted_internal_open_curves_pt,
8539
8540 // ********************************************************************
8541 // Step (4) Create connections among the outer boundaries
8542 // (intersections with themselves)
8543 // ********************************************************************
8544
8545 // After creating the new boundaries representation (polylines)
8546 // establish the connections of the shared boundaries (with
8547 // themselves or with the original boundaries). This avoids the
8548 // multiple definition of vertices in the domain which cause
8549 // problems when calling Triangle
8550
8551 this->create_shared_polylines_connections();
8552
8553 // ------------------------------------------------------------------
8554 // Compute the new holes information. Those from the
8555 // extra_holes_coordinates container, and those from the original
8556 // closed boundaries. Add the holes created by the halo elements
8557 // adjacent to the shared boundaries
8558
8559 // The storage for the new holes, get those from the
8560 // extra_holes_coordinates container and those from the internal
8561 // closed boundaries that are defined as holes
8563
8564 // Copy the holes (those defined by the original internal closed
8565 // boundaries and those in the extra holes container)
8566
8567 // The holes defined by the original internal closed boundaries
8568 const unsigned n_holes = this->Internal_polygon_pt.size();
8569 for (unsigned h = 0; h < n_holes; h++)
8570 {
8572 this->Internal_polygon_pt[h]->internal_point();
8573 // If the closed boundary is a hole, then copy its hole
8574 if (!hole_coordinates.empty())
8575 {
8577 }
8578 } // for (h < n_holes)
8579
8580 // Is this the first time we are going to copy the extra holes
8581 // coordinates
8582 if (First_time_compute_holes_left_by_halo_elements)
8583 {
8584 // The holes in the extra holes container
8585 const unsigned n_extra_holes = Extra_holes_coordinates.size();
8586 for (unsigned h = 0; h < n_extra_holes; h++)
8587 {
8588 Vector<double> hole_coordinates = Extra_holes_coordinates[h];
8590 } // for (h < n_extra_holes)
8591
8592 // Copy the extra holes coordinates
8593 Original_extra_holes_coordinates = Extra_holes_coordinates;
8594
8595 // Set the flag to false
8596 First_time_compute_holes_left_by_halo_elements = false;
8597
8598 } // if (First_time_compute_holes_left_by_halo_elements)
8599 else
8600 {
8601 // Not the first time, then only copy the original extra holes
8602 // coordinates
8603 const unsigned n_original_extra_holes =
8604 Original_extra_holes_coordinates.size();
8605 for (unsigned h = 0; h < n_original_extra_holes; h++)
8606 {
8607 Vector<double> hole_coordinates = Original_extra_holes_coordinates[h];
8609 } // for (h < n_original_extra_holes)
8610 }
8611
8612 // Add the holes created by the halo elements adjacent to the shared
8613 // boundaries
8614 compute_holes_left_by_halo_elements_helper(new_holes_coordinates);
8615
8616 // Update the holes information, only use the coordinate inside the
8617 // poylgons that define the new domain
8618 update_holes_information_helper(polygons_pt, new_holes_coordinates);
8619
8620 // tachidok Clear the storage by now
8621 // new_holes_coordinates.clear();
8622
8623 // Now copy the info. in the extra holes coordinates container
8624 Extra_holes_coordinates = new_holes_coordinates;
8625
8626 // Do not delete halo(ed) info., this will be "deleted"
8627 // automatically by not passing that information to the new adapted
8628 // mesh. Once the transfer of target areas is performed the halo(ed)
8629 // information is no longer required
8630 }
8631
8632 //======================================================================
8633 // Take the polylines from the shared boundaries and the boundaries
8634 // to create polygons
8635 //======================================================================
8636 template<class ELEMENT>
8640 {
8641 // Each vector of polylines (curve) is already sorted, it means that
8642 // all the polylines on the vector polylines_pt[i] point to the same
8643 // direction
8644
8645 // --- Using this fact we should compare the first and last points from
8646 // these arrays of polylines (curves) and compare with the others
8647 // vectors of polylines (curves) end points
8648 // --- Once created a closed curve create a polygon
8649
8650 // The number of curves
8651 const unsigned ncurves = polylines_pt.size();
8652
8653 // The number of non sorted curves
8654 const unsigned nunsorted_curves = ncurves;
8655 // The number of sorted curves
8656 unsigned nsorted_curves = 0;
8657
8658 // Vector to know which ncurve is already done
8659 std::vector<bool> done_curve(ncurves);
8660
8661 do
8662 {
8663 // The list where to add the curves so that they be contiguous
8664 std::list<Vector<TriangleMeshPolyLine*>> list_building_polygon_pt;
8665#ifdef PARANOID
8666 // Flag to indicate that a root curve was found
8667 bool root_curve_found = false;
8668#endif
8669
8670 // The index for the root_curve (we use it in further iterations as the
8671 // starting index so we dont need to search in already done curves)
8672 unsigned root_curve_idx = 0;
8673
8674 // Get the root curve
8675 for (unsigned ic = 0; ic < ncurves; ic++)
8676 {
8677 if (!done_curve[ic])
8678 {
8681#ifdef PARANOID
8682 root_curve_found = true;
8683#endif
8684 done_curve[ic] = true;
8685 // ... break the loop
8686 break;
8687 }
8688 }
8689
8690#ifdef PARANOID
8691 if (!root_curve_found)
8692 {
8693 std::stringstream err;
8694 err << "The root curve to create a polygon from the shared and "
8695 << "original boundaries was not found!!!\n";
8696 throw OomphLibError(err.str(),
8697 "TriangleMesh::create_tmp_polygons_helper()",
8699 }
8700#endif
8701
8702 // Get the root curve
8705
8706 // Add the root curve to the list
8708
8709 // Get the initial and final vertices from the root curve
8712
8713 // We need to get the number of polylines that compose the root curve
8714 const unsigned nroot_curve_polyline = root_curve_pt.size();
8715 // ... and now get the initial and final vertex
8716 root_curve_pt[0]->initial_vertex_coordinate(root_curve_initial_vertex);
8717 root_curve_pt[nroot_curve_polyline - 1]->final_vertex_coordinate(
8719
8720 // First check if it already create a polygon
8721 double diff =
8726 diff = sqrt(diff);
8728 {
8729 // The polyline already create a Polygon, then create it!!!
8730 // Create the curve section representation of the current root curve
8731 Vector<TriangleMeshCurveSection*> curve_section_pt(
8733
8734 // Copy the polylines into its curve section representation
8735 for (unsigned i = 0; i < nroot_curve_polyline; i++)
8736 {
8737 curve_section_pt[i] = root_curve_pt[i];
8738 }
8739
8740 // ... and create the Polygon
8742 new TriangleMeshPolygon(curve_section_pt);
8743
8744 // Mark the polygon for deletion (in the destructor)
8745 this->Free_polygon_pt.insert(new_polygon_pt);
8746
8747 // Add the polygon to the output polygons
8748 polygons_pt.push_back(new_polygon_pt);
8749 } // (diff < ToleranceForVertexMismatchInPolygons::Tolerable_error)
8750 // when the curve creates a Polygon by itself
8751 else
8752 {
8753 // Flag to continue iterating while curves be added to the left
8754 // or right of the list of curves
8755 bool added_curve = false;
8756#ifdef PARANOID
8757 // Flag to know if the "loop" finish because a polygon was
8758 // created or because no more curves can be added to the left or
8759 // right
8760 bool polygon_created = false;
8761#endif
8762 do
8763 {
8764 added_curve = false;
8765 // If the root curve does not create a closed polygon then add curves
8766 // to the left or right until the curves create a closed polygon
8767 for (unsigned ic = root_curve_idx + 1; ic < ncurves; ic++)
8768 {
8769 if (!done_curve[ic])
8770 {
8771 // Get the current curve
8773
8774 // We need to get the number of polylines that compose the
8775 // current curve
8777
8778 // ... and get the initial and final coordinates for the current
8779 // curve
8782
8783 current_curve_pt[0]->initial_vertex_coordinate(
8786 ->final_vertex_coordinate(current_curve_final_vertex);
8787
8788 // ---------------------------------------------------------------
8789 // Start adding curves to the left or right
8790 // ---------------------------------------------------------------
8799 diff = sqrt(diff);
8800 // CURRENT curve to the LEFT of the ROOT curve
8802 {
8803 // Add the current curve to the left
8805 // Mark the curve as done
8806 done_curve[ic] = true;
8807 // Update the initial vertex values
8810 // Increase the number of sorted curves
8812 // Set the flag to indicate that a curve was added to the list
8813 added_curve = true;
8814 break;
8815 }
8816
8825 diff = sqrt(diff);
8826 // CURRENT curve to the LEFT of the ROOT curve but INVERTED
8828 {
8831 // Reverse each polyline and back them up
8832 for (unsigned it = 0; it < ncurrent_curve_polyline; it++)
8833 {
8834 current_curve_pt[it]->reverse();
8836 }
8837 // Now copy them back but in reverse order
8838 unsigned count = 0;
8839 for (int i = ncurrent_curve_polyline - 1; i >= 0; i--, count++)
8840 {
8842 }
8843 // Add the current curve to the left
8845 // Mark the curve as done
8846 done_curve[ic] = true;
8847 // Update the initial vertex values
8850 // Increase the number of sorted curves
8852 // Set the flag to indicate that a curve was added to the list
8853 added_curve = true;
8854 break;
8855 }
8856
8865 diff = sqrt(diff);
8866 // CURRENT curve to the RIGHT of the ROOT curve
8868 {
8869 // Add the current curve to the right
8871 // Mark the curve as done
8872 done_curve[ic] = true;
8873 // Update the initial vertex values
8876 // Increase the number of sorted curves
8878 // Set the flag to indicate that a curve was added to the list
8879 added_curve = true;
8880 break;
8881 }
8882
8883 diff =
8888 diff = sqrt(diff);
8889 // CURRENT curve to the RIGHT of the ROOT curve but INVERTED
8891 {
8894 // Reverse each polyline and back them up
8895 for (unsigned it = 0; it < ncurrent_curve_polyline; it++)
8896 {
8897 current_curve_pt[it]->reverse();
8899 }
8900 // Now copy them back but in reverse order
8901 unsigned count = 0;
8902 for (int i = ncurrent_curve_polyline - 1; i >= 0; i--, count++)
8903 {
8905 }
8906 // Add the current curve to the right
8908 // Mark the curve as done
8909 done_curve[ic] = true;
8910 // Update the initial vertex values
8913 // Increase the number of sorted curves
8915 // Set the flag to indicate that a curve was added to the list
8916 added_curve = true;
8917 break;
8918 }
8919
8920 } // if (!done_curve[ic])
8921
8922 } // for (ic < ncurves)
8923
8924 // After adding a curve check if it is possible to create a polygon
8925 double diff =
8930 diff = sqrt(diff);
8932 {
8933 // If the curves already create a Polygon then go out of the
8934 // loop and create the Polygon
8935 added_curve = false;
8936#ifdef PARANOID
8937 // Set the flag to indicate that a Polygon has been created
8938 polygon_created = true;
8939#endif
8940 } // (diff <
8941 // ToleranceForVertexMismatchInPolygons::Tolerable_error)
8942 // when the curve creates a Polygon by itself
8943
8944 } while (added_curve);
8945
8946#ifdef PARANOID
8947 if (!polygon_created)
8948 {
8949 std::stringstream error_message;
8950 error_message
8951 << "It was no possible to create a TriangleMeshPolygon with "
8952 << "the input set of curves\n"
8953 << "These are the initial and final vertices in the current "
8954 << "sorted list of\nTriangleMeshPolyLines\n\n";
8957 unsigned icurve = 0;
8961 it++, icurve++)
8962 {
8963 const unsigned ncurrent_curve_polyline = (*it).size();
8964 error_message << "TriangleMeshCurve #" << icurve << "\n"
8965 << "-----------------------------------\n";
8966 for (unsigned ip = 0; ip < ncurrent_curve_polyline; ip++)
8967 {
8970 (*it)[ip]->initial_vertex_coordinate(init_vertex);
8971 (*it)[ip]->final_vertex_coordinate(final_vertex);
8972 error_message << "TriangleMeshPolyLine #" << ip << "\n"
8973 << "Initial vertex: (" << init_vertex[0] << ","
8974 << init_vertex[1] << ")\n"
8975 << "Final vertex: (" << final_vertex[0] << ","
8976 << final_vertex[1] << ")\n";
8977 } // for (ip < ncurrent_curve_polyline)
8978 } // for (it != list_building_polygon_pt.end())
8979
8980 throw OomphLibError(error_message.str(),
8981 "TriangleMesh::create_tmp_polygons_helper()",
8983
8984 } // if (!polygon_created)
8985#endif
8986
8987 // Create the polygon after joining the curves
8988 unsigned ntotal_polylines = 0;
8989 // Get the total number of polylines
8993 it++)
8994 {
8995 ntotal_polylines += (*it).size();
8996 }
8997
8998 // Create the curve section representation of the curves on the list
9000
9001 // Copy the polylines into its curve section representation
9002 unsigned counter = 0;
9006 it++)
9007 {
9008 const unsigned ncurrent_curve_polyline = (*it).size();
9009 for (unsigned ip = 0; ip < ncurrent_curve_polyline; ip++, counter++)
9010 {
9011 curve_section_pt[counter] = (*it)[ip];
9012 } // for (ip < ncurrent_curve_polyline)
9013 } // Loop over the list of polylines
9014
9015 // ... and create the Polygon
9017 new TriangleMeshPolygon(curve_section_pt);
9018
9019 // Mark the polygon for deletion (in the destructor)
9020 this->Free_polygon_pt.insert(new_polygon_pt);
9021
9022 // Add the polygon to the output polygons
9023 polygons_pt.push_back(new_polygon_pt);
9024
9025 } // else
9026 // (diff < ToleranceForVertexMismatchInPolygons::Tolerable_error)
9027
9028 } while (nsorted_curves < nunsorted_curves);
9029 }
9030
9031 //======================================================================
9032 // Take the polylines from the original open curves and created
9033 // new temporaly representations of open curves with the bits of
9034 // original curves not overlapped by shared boundaries
9035 //======================================================================
9036 template<class ELEMENT>
9041 {
9042 // Here search for the connections of the open curves remaining as
9043 // open curves with the shared boundaries markes as internal
9045
9046 // Once identified the connections created with the new internal
9047 // boundaries representations add them to the open curves container
9048 for (unsigned i = 0; i < ninternal_open_curves; i++)
9049 {
9050 // Create the curve section representation of the polylines
9051 const unsigned npoly = sorted_open_curves_pt[i].size();
9053 for (unsigned j = 0; j < npoly; j++)
9054 {
9056 }
9057 // ... and create the Open Curve
9060
9061 // Mark the open curve for deletion (in the destructor)
9062 this->Free_open_curve_pt.insert(new_open_curve_pt);
9063
9064 // Add the open curve to the output open curves
9066
9067 } // (i < ninternal_open_curves)
9068 }
9069
9070 //======================================================================
9071 // Check for any possible connections that the array of sorted
9072 // nodes have with original boundary nodes, previous shared polyline
9073 // nodes or with itself polyline nodes. In case that there is a
9074 // connection, get the boundary id to which connects
9075 //======================================================================
9076 template<class ELEMENT>
9078 std::set<FiniteElement*>& element_in_processor_pt,
9079 const int& root_edge_bnd_id,
9080 std::map<std::pair<Node*, Node*>, bool>& overlapped_face,
9081 std::map<unsigned, std::map<Node*, bool>>&
9083 std::list<Node*>& current_polyline_nodes,
9084 std::map<unsigned, std::list<Node*>>& shared_bnd_id_to_sorted_list_node_pt,
9085 const unsigned& node_degree,
9086 Node*& new_node_pt,
9087 const bool called_from_load_balance)
9088 {
9089 // Initialize the flag to return
9090 int flag_to_return = -1;
9091
9092 // --------------------------------------------------------------------
9093 // First try to find a connection with any original boundary (keep
9094 // in mind the case when internal boundaries may be overlapped by
9095 // shared boundaries)
9096 // --------------------------------------------------------------------
9097
9098 // Check if the shared boundary is overlapping an internal boundary
9099 bool overlapping_internal_boundary = false;
9100 // The boundary id overlapped by the current shared boundary
9101 unsigned internal_overlaping_bnd_id = 0;
9102 if (root_edge_bnd_id != -1)
9103 {
9104 // Set the flat to true
9106 // Set the bnd id of the overlapped internal boundary
9107 internal_overlaping_bnd_id = static_cast<unsigned>(root_edge_bnd_id);
9108 } // if (root_edge_bnd_id != -1)
9109
9110 // ---------------------------------------------------------------
9111 // Check if the connection is with an original boundary by checking
9112 // if the new node is a boundary node, and it lives in an element
9113 // that is part of the domain
9114 // ---------------------------------------------------------------
9115 if (new_node_pt->is_on_boundary())
9116 {
9117 // Flag to indicate if the node lives in a non overlapped boundary
9119
9120 // If the node is a boundary node then check in which boundary it
9121 // is
9122 const unsigned noriginal_bnd = this->initial_shared_boundary_id();
9123 for (unsigned bb = 0; bb < noriginal_bnd; bb++)
9124 {
9125 // If the shared boundary overlaps an internal boundary it will
9126 // be indicated by (root_edge_bnd_id != -1), the original
9127 // internal boundary that overlaps is given by the
9128 // root_edge_bnd_id value. We skip that original internal
9129 // boundary because the new node will be obviously ON the
9130 // internal boundary
9132 {
9133 // Is the node on boundary bb?
9134 if (new_node_pt->is_on_boundary(bb))
9135 {
9136 // If overlaping then check that the boundary is different
9137 // from the one that is being overlapped, or if overlapped
9138 // then check that the node is on an edge on the bb
9139 // boundary not overlapped by a shared boundary
9145 {
9146 // Is the node living in a non overlapped boundary
9148 {
9150 }
9151
9152 // Now we need to check that the node lies on a boundary
9153 // that still exist (the elements associated to the
9154 // boundary may have been removed at the mesh distribution
9155 // stage). The node may be still marked as a boundary node
9156 // but the boundary may not have elements associated.
9157
9158 // Get the number of elements in the boundary
9159 const unsigned n_bound_ele = this->nboundary_element(bb);
9160 if (n_bound_ele > 0)
9161 {
9162 // Check that node lies on a nonhalo element, those are
9163 // the elements used to update the domain representation
9164 for (unsigned e = 0; e < n_bound_ele; e++)
9165 {
9166 // Get the boundary bulk element
9167 FiniteElement* bulk_ele_pt = this->boundary_element_pt(bb, e);
9168 // Check if the element will be retained, it means it
9169 // is a nonhalo element
9170 std::set<FiniteElement*>::iterator it =
9172 // If found then check if the node live in the element
9173 if (it != element_in_processor_pt.end())
9174 {
9175 // Found the node in the nonhalo face element
9176 bool found_node = false;
9177 // Get the face index
9178 int face_index = this->face_index_at_boundary(bb, e);
9179 // Create the face element
9181 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
9182 // Get the number of nodes in the face element
9183 const unsigned n_node_face = face_ele_pt->nnode();
9184 // Get the first and last node of the face element
9187 // Create the edge with the pair of nodes
9188 std::pair<Node*, Node*> tmp_edge =
9189 std::make_pair(first_node_pt, last_node_pt);
9190 // Check if the face element edge is overlapped by a
9191 // shared boundary
9192 // Is the face not overlapped?
9194 {
9195 // Look for the node in the current face element
9196 for (unsigned n = 0; n < n_node_face; n++)
9197 {
9198 // Check for every individual node
9200 {
9201 found_node = true;
9202 break;
9203 } // if (face_ele_pt->node_pt(n) == new_node_pt)
9204 } // for (n < n_node_face)
9205 } // if (!overlapped_face[tmp_edge])
9206 // Free the memory of the face element
9207 delete face_ele_pt;
9208 if (found_node)
9209 {
9210 // return the first original boundary id found,
9211 // does not matter if the node lies on more than
9212 // one original boundary (with boundary
9213 // elements). This is the original boundary id
9214 // that will be used to create the connection
9216 return flag_to_return;
9217 } // if (found_node)
9218
9219 } // if (it!=element_in_processor_pt.end())
9220
9221 } // for (e < n_bound_ele)
9222
9223 } // if (n_bound_ele > 0)
9224
9225 } // if (bb != internal_overlaping_bnd_id ||
9226 // ((bb == internal_overlaping_bnd_id) &&
9227 // (on_bnd_edge_not_overlapped_by_shd_bnd)))
9228
9229 } // if (nod_pt->is_on_boundary(bb))
9230
9231 } // if (overlapping_internal_boundary)
9232 else
9233 {
9234 // Is the node on boundary bb?
9235 if (new_node_pt->is_on_boundary(bb))
9236 {
9237 // Now we need to check that the node lies on a boundary
9238 // that still exist (the elements associated to the boundary
9239 // may have been removed at the mesh distribution
9240 // stage). The node may be still marked as a boundary node
9241 // but the boundary may not have elements associated.
9242
9243 // Get the number of elements in the boundary
9244 const unsigned n_bound_ele = this->nboundary_element(bb);
9245 if (n_bound_ele > 0)
9246 {
9247 // Check that node lies on a nonhalo element, those are
9248 // the elements used to update the domain representation
9249 for (unsigned e = 0; e < n_bound_ele; e++)
9250 {
9251 // Get the boundary bulk element
9252 FiniteElement* bulk_ele_pt = this->boundary_element_pt(bb, e);
9253 // Check if the element will be retained, it means it is
9254 // a nonhalo element
9255 std::set<FiniteElement*>::iterator it =
9257 // If found then check if the node live in the element
9258 if (it != element_in_processor_pt.end())
9259 {
9260 // Found the node in the nonhalo face element
9261 bool found_node = false;
9262 // Get the face index
9263 int face_index = this->face_index_at_boundary(bb, e);
9264 // Create the face element
9266 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
9267 // Get the number of nodes in the face element
9268 const unsigned n_node_face = face_ele_pt->nnode();
9269 // Get the first and last node of the face element
9272 // Create the edge with the pair of nodes
9273 std::pair<Node*, Node*> tmp_edge =
9274 std::make_pair(first_node_pt, last_node_pt);
9275 // Check if the face element edge is overlapped by a
9276 // shared boundary
9277 // Is the face not overlapped?
9279 {
9280 // Look for the node in the current face element
9281 for (unsigned n = 0; n < n_node_face; n++)
9282 {
9283 // Check for every individual node
9285 {
9286 found_node = true;
9287 break;
9288 } // if (face_ele_pt->node_pt(n) == new_node_pt)
9289 } // for (n < n_node_face)
9290 } // if (!overlapped_face[tmp_edge])
9291 // Free the memory of the face element
9292 delete face_ele_pt;
9293 if (found_node)
9294 {
9295 // return the first original boundary id found, does
9296 // not matter if the node lies on more than one
9297 // original boundary (with boundary elements). This
9298 // is the original boundary id that will be used to
9299 // create the connection
9301 return flag_to_return;
9302 } // if (found_node)
9303
9304 } // if (it!=element_in_processor_pt.end())
9305
9306 } // for (e < n_bound_ele)
9307
9308 } // if (n_bound_ele > 0)
9309
9310 } // if (nod_pt->is_on_boundary(bb))
9311 } // else if (overlapping_internal_boundary)
9312 } // for (bb < noriginal_bnd)
9313
9314 // We will only reach this stage when the node was found to be
9315 // connected to an original boundary but the element(s) on that
9316 // boundary where the node should live are not part of the domain.
9317 // Think in a corner of a triangle which touches the boundary
9318 // which elements will not be part of the domain
9319
9320 // We need to break the currently forming polyline
9321 // flag_to_return = -3;
9322
9323 // We need to break the currently forming polyline if and only if
9324 // the boundary(ies) in which the node is living is(are) not an
9325 // overlapped boundary
9327 {
9328 // If the boundary(ies) in which the node is living is(are) an
9329 // overlapped boundary then break the break the formation of the
9330 // polyline
9331 flag_to_return = -3;
9332 }
9333 else
9334 {
9335 // The boundary is overlapped, if the node lives in a non
9336 // overlapped boundary then we can break the formation of the
9337 // polyline
9339 {
9340 flag_to_return = -3;
9341 } // if (is_node_living_in_non_overlapped_boundar)y
9342
9343 } // if (!overlapping_internal_boundary)
9344
9345 } // if (new_node_pt->is_on_boundary())
9346
9347 // Return inmediately if the connection is with an original boundary
9348 // whose elements are still part of the domain
9349 if (flag_to_return >= 0)
9350 {
9351 return flag_to_return;
9352 }
9353
9354 // ----------------------------------------------------------------------
9355 // Secondly, if there is not a connection with any original
9356 // boundary, or if there is connection but with an original boundary
9357 // whose elements are not part of the domain, then check for
9358 // connections with previously created shared polylines
9359 // ----------------------------------------------------------------------
9360 // Store all the previous shared polylines to which the current
9361 // found is found to be connected
9363 // Check for all the previous polylines except the current one
9364 for (std::map<unsigned, std::list<Node*>>::iterator it =
9367 it++)
9368 {
9369 // Get the boundary id of the list of nodes that created the
9370 // polyline (the shared boundary id associated with the list of
9371 // nodes)
9372 const unsigned i_bnd_id = (*it).first;
9373 // Get an iterator pointer to the list of nodes of the shared
9374 // polyline
9375 std::list<Node*>::iterator it_list = (*it).second.begin();
9376 // Get the total number of nodes associated to the boundary
9377 const unsigned n_nodes = (*it).second.size();
9378 // Search for connections in the list of nodes
9379 for (unsigned i = 0; i < n_nodes; i++, it_list++)
9380 {
9381 // Is the node already part of any other shared boundary
9382 if ((*it_list) == new_node_pt)
9383 {
9384 // Include the i-th boundary id in the list of candidate
9385 // shared boundaries to connect
9387 // Break the look with the i-th shared boundary, check with
9388 // the others shared boundaries
9389 break;
9390 } // if ((*it_list) == new_node_pt)
9391
9392 } // for (i < nnodes)
9393
9394 } // Loop over the shared boundaries and associated nodes
9395
9396 // Get the number of candidate shared boundaries to connect
9397 const unsigned n_candidate_shared_bnd_to_connect =
9399
9400 // Is there a connection with any previous shared polyline
9402 {
9403 // If called from load balance we do not need to check if the
9404 // shared boundary is part of the processor since it certanily is,
9405 // only the shared boundaries that are pare of the processor are
9406 // used to created connection when creating the new shared
9407 // boundaries in the load balance rutine
9409 {
9411 }
9412
9413 // We need to ensure that the shared boundary to which we are
9414 // connecting is part of the current processor, if none of the
9415 // found shared bundaries is in the current processor then return
9416 // the flag for "connection with boundary not in the current
9417 // processor"
9418
9419 // Store the shared boundaries associated with the current processor
9421
9422 // Get the shared boundaries associated with the current processor
9423 shared_boundaries_in_this_processor(shared_bound_in_this_proc);
9424
9425 // If any of the candidate shared boundaries to connect is in the
9426 // current processor then return that shared boundary id
9427
9428 // The number of shared boundaries in the current processor
9429 const unsigned n_shared_bound_in_this_proc =
9431
9432 // Loop over the candidate shared boundaries to connect
9433 for (unsigned i = 0; i < n_candidate_shared_bnd_to_connect; i++)
9434 {
9435 // Get the i-th candidate shared boundary to connect
9436 const unsigned i_candidate_shared_bnd =
9438
9439 // Loop over the shared boundaries in the current processor
9440 for (unsigned j = 0; j < n_shared_bound_in_this_proc; j++)
9441 {
9442 // Is the candidate boundary a shared boundary in this processor?
9444 {
9445 // Return the candidate shared boundary
9447 return flag_to_return;
9448 } // The candidate shared boundary is a boundary in the
9449 // current processor
9450
9451 } // for (j < n_shared_bound_in_this_proc)
9452
9453 } // for (i < n_candidate_shared_bnd_to_connect)
9454
9455 // If non of the candidate shared boundaries to connect is in the
9456 // current processor the mark that we need to stop the addition of
9457 // vertices at this side of the polyline
9458 flag_to_return = -3;
9459
9460 } // if (n_candidate_shared_bnd_to_connect > 0)
9461
9462 // Return inmediately if the connection is with a previuos shared
9463 // boundary
9464 if (flag_to_return >= 0)
9465 {
9466 return flag_to_return;
9467 }
9468
9469 // ------------------------------------------------------------------
9470 // Finally,check for connections with the same polyline (the shared
9471 // boundary that is being constructed). We are trying to avoid loops
9472 // or connections with the same shared boundary that is why this is
9473 // checked at the end
9474 // ------------------------------------------------------------------
9475 unsigned nrepeated = 0;
9476 for (std::list<Node*>::iterator it_list = current_polyline_nodes.begin();
9478 it_list++)
9479 {
9480 // There must be at least one repeated node (the one that we have
9481 // just added, and it should be the first or last node)
9482 if ((*it_list) == new_node_pt)
9483 {
9484 nrepeated++;
9485 }
9486 }
9487 // If the number of repeated nodes is greater than one then the
9488 // polyline has a connection with itself
9489 if (nrepeated > 1)
9490 {
9491 // Return the flag value to indicate connection with itself, we
9492 // can not return the boundary id of the current polyline since it
9493 // has not been already assigned
9494 flag_to_return = -2;
9495 }
9496
9497 // If there is no connection at all check the degree of the node, if
9498 // it is greater than 2 then return the flag to stop adding nodes
9499 // after this one
9500 if (node_degree > 2)
9501 {
9502 flag_to_return = -3;
9503 }
9504
9505 // Return the flag
9506 return flag_to_return;
9507 }
9508
9509 //======================================================================
9510 // Establish the connections of the polylines previously
9511 // marked as having connections. This connections were created in the
9512 // function TriangleMesh::create_polylines_from_halo_elements_helper().
9513 // In case of doing load balancing the connections were created by the
9514 // function RefineableTriangleMesh::create_new_shared_boundaries()
9515 // ======================================================================
9516 template<class ELEMENT>
9518 {
9519 // Get the rank of the current processor
9520 const unsigned my_rank = this->communicator_pt()->my_rank();
9521
9522 // Get the shared curves associated with this processor
9524 this->Shared_boundary_polyline_pt[my_rank];
9525
9526 // Loop through the shared boundaries on the current processor and
9527 // check if they are marked to create a connection
9528 const unsigned ncurves = shared_curves_pt.size();
9529 for (unsigned icurve = 0; icurve < ncurves; icurve++)
9530 {
9531 // Get the number of polylines in the current shared curve
9532 const unsigned npoly = shared_curves_pt[icurve].size();
9533 for (unsigned ipoly = 0; ipoly < npoly; ipoly++)
9534 {
9535 // Get the polyline representation of the shared boundary
9537
9538 // Get the boundary id of the current polyline
9539 const unsigned bound_id = shd_poly_pt->boundary_id();
9540
9541 // Is the left vertex connected
9542 const bool is_connected_to_the_left =
9543 shd_poly_pt->is_initial_vertex_connected();
9544
9545 // Is the right vertex connected
9546 const bool is_connected_to_the_right =
9547 shd_poly_pt->is_final_vertex_connected();
9548
9549 // -----------------------------------------------------------------
9550 // If there is a connection at one of the ends we need to
9551 // establish that connection
9553 {
9554 // Now get the new left and right vertices of the shared
9555 // polyline
9556 const unsigned n_vertex = shd_poly_pt->nvertex();
9557
9558 // Now get the polylines to where the current shared boundary is
9559 // connected and create the connections
9560
9561 // --------------------------------------------------------------
9562 // Connection to the left
9564 {
9565 // Get the unsigned version of the bound id to connect to
9566 // the left
9567 const unsigned uconnection_to_the_left =
9568 shd_poly_pt->initial_vertex_connected_bnd_id();
9569
9570 // The pointer to the boundary to connect
9572
9573 // Flag to indicate we are trying to connect to an split
9574 // boundary
9576
9577 // Flag to indicate we are trying to connecto to an internal
9578 // boundary that is overlaped by a shared boundary
9580
9581 // Check if the connection is with itself
9583 {
9584 // Set the pointer to the polyline to connect
9586 }
9587 else
9588 {
9589 // Get the initial shared boundary ids
9590 const unsigned initial_shd_bnd_id = initial_shared_boundary_id();
9591 // Check if the boundary to connect is a shared polyline
9593 {
9594 // Get the polyline pointer representing the destination
9595 // boundary
9597 boundary_polyline_pt(uconnection_to_the_left);
9598 } // if (uconnection_to_the_left >= initial_shd_bnd_id)
9599 else
9600 {
9601 // If we are going to connect to an original boundary
9602 // verify if the boundary was splitted during the
9603 // distribution process to consider all the chunks
9604 // (sub-polylines) of the boundary
9605 if (boundary_was_splitted(uconnection_to_the_left))
9606 {
9608 } // if (boundary_was_splitted(uconnection_to_the_left))
9609
9610 // If we are going to connect to an original boundary
9611 // verify if the boundary, or any of its chunks is
9612 // marked to be overlapped by a shared boundary, if that
9613 // is the case we first check for connections in the
9614 // shared boundary that overlaps the internal boundary,
9615 // or the chunks, and then check for connections in the
9616 // original boundary
9618 {
9619 // Get the number of chucks that represent the
9620 // destination boundary
9621 const unsigned n_sub_poly =
9622 nboundary_subpolylines(uconnection_to_the_left);
9623 // Now loop over the chunks of the destination
9624 // boundary and if any of them is marked to be
9625 // overlaped by a shared boundary then set the flag
9626 // and break the loop
9627 for (unsigned ii = 0; ii < n_sub_poly; ii++)
9628 {
9629 if (boundary_marked_as_shared_boundary(
9631 {
9632 // Mark the boundary as being overlaped by a
9633 // shared boundary
9635 // Break, no need to look for more overlapings
9636 break;
9637 } // if (boundary_marked_as_shared_boundary(...))
9638 } // for (ii < n_sub_poly)
9639 } // if (connecting_to_an_split_boundary)
9640 else
9641 {
9642 // If not connecting to an split boundary then check
9643 // if the whole destination boundary is overlaped by
9644 // an internal boundary
9645 if (boundary_marked_as_shared_boundary(
9647 {
9648 // Mark the boundary as being overlaped by a shared
9649 // boundary
9651 } // if (boundary_marked_as_shared_boundary(...))
9652 } // else if (connecting_to_an_split_boundary)
9653
9654 // If we are connecting neither to an split boundary nor
9655 // an overlaped boundary then get the pointer to the
9656 // original boundary
9659 {
9660 // Get the polyline pointer representing the
9661 // destination boundary
9663 boundary_polyline_pt(uconnection_to_the_left);
9664 } // else if (NOT split, NOT overlaped)
9665 } // else if (uconnection_to_the_left >= initial_shd_bnd_id)
9666
9667 } // else if (uconnection_to_the_left == bound_id)
9668
9669#ifdef PARANOID
9670 // If we are not connecting to an original boundary
9671 // (connecting to the same shared boundary or to another
9672 // shared boundary) then the boundary should not be marked
9673 // as split
9675 {
9676 if (boundary_was_splitted(uconnection_to_the_left))
9677 {
9678 std::stringstream error;
9679 error
9680 << "The current shared boundary (" << bound_id << ") was "
9681 << "marked to have a connection\nto the left with the "
9682 << "boundary (" << uconnection_to_the_left << ").\n"
9683 << "The problem is that the destination boundary (possibly\n"
9684 << "another shared boundary) is marked to be split\n"
9685 << "There should not be split shared boundaries\n\n";
9686 throw OomphLibError(
9687 error.str(),
9688 "TriangleMesh::create_shared_polylines_connections()",
9690 }
9691 } // if (!connecting_to_an_split_boundary)
9692#endif
9693
9694 // Now look for the vertex number on the destination
9695 // boundary(ies) -- in case that the boundary was split ---
9696
9697 // Do not check for same orientation, that was previously
9698 // worked by interchanging the connections boundaries (if
9699 // necessary)
9700
9701 // Get the left vertex in the shared boundary
9703 shd_poly_pt->vertex_coordinate(0);
9704
9705 // If the boundary was not split then ...
9707 {
9708 // ... check if the boundary is marked to be overlaped by
9709 // a shared boundary
9711 {
9712 // If that is not the case then we can safely look for
9713 // the vertex number on the destination boundar
9714 unsigned vertex_index = 0;
9715
9716 const bool found_vertex_index =
9717 get_connected_vertex_number_on_destination_polyline(
9719
9720 // If we could not find the vertex index to connect then
9721 // we are in trouble
9722 if (!found_vertex_index)
9723 {
9724 std::stringstream error;
9725 error
9726 << "The current shared boundary (" << bound_id << ") was "
9727 << "marked to have a connection\nto the left with the "
9728 << "boundary (" << uconnection_to_the_left << ").\n"
9729 << "The problem is that the left vertex of the current\n"
9730 << "shared boundary is not in the list of vertices of the\n"
9731 << "boundary to connect.\n\n"
9732 << "This is the left vertex of the current shared "
9733 "boundary\n"
9734 << "Left vertex: (" << shd_bnd_left_vertex[0] << ", "
9735 << shd_bnd_left_vertex[1] << ")\n\n"
9736 << "This is the list of vertices on the destination "
9737 << "boundary\n";
9738 const unsigned n_v = poly_to_connect_pt->nvertex();
9739 for (unsigned i = 0; i < n_v; i++)
9740 {
9742 poly_to_connect_pt->vertex_coordinate(i);
9743 error << "Vertex #" << i << ": (" << cvertex[0] << ", "
9744 << cvertex[1] << ")\n";
9745 }
9746 throw OomphLibError(
9747 error.str(),
9748 "TriangleMesh::create_shared_polylines_connections()",
9750 } // if (!found_vertex_index)
9751
9752 // Create the connection, the left vertex of the current
9753 // shared boundary is connected with the vertex_index-th
9754 // vertex on the destination boundary
9755 shd_poly_pt->connect_initial_vertex_to_polyline(
9757
9758 } // if (!connecting_to_an_overlaped_boundary)
9759 else
9760 {
9761 // If the boundary is marked to be overlaped by a shared
9762 // boundary then get that shared boundary and look for
9763 // the connection in that boundary
9764
9765 // The vertex where to store the index to connect
9766 unsigned vertex_index = 0;
9767 // A flag to indicate if the connection was found
9768 bool found_vertex_index = false;
9769
9770 // Get the shared boundary id that is overlaping the
9771 // internal boundary
9773 get_shared_boundaries_overlapping_internal_boundary(
9775
9776 // Get the number of shared polylines that were found to
9777 // overlap the internal boundary
9778 const unsigned n_shd_bnd_overlap_int_bnd =
9780
9781 // Loop over the shared boundaries that overlap the
9782 // internal boundary and look for the vertex to connect
9783 for (unsigned ss = 0; ss < n_shd_bnd_overlap_int_bnd; ss++)
9784 {
9785 // Get the shared polyline
9786 const unsigned new_connection_to_the_left =
9788
9789 // Get the shared polyline that is overlaping the
9790 // internal boundary
9792 boundary_polyline_pt(new_connection_to_the_left);
9793
9794 if (poly_to_connect_pt != 0)
9795 {
9796 // Look for the vertex number in the destination
9797 // shared polyline
9799 get_connected_vertex_number_on_destination_polyline(
9801 } // if (poly_to_connect_pt!=0)
9802
9803 // If we have found the vertex to connect then
9804 // break the loop
9806 {
9807 break;
9808 } // if (found_vertex_index)
9809
9810 } // for (ss < n_shd_bnd_overlaping_int_bnd)
9811
9812#ifdef PARANOID
9813 // If we could not find the vertex index to connect then
9814 // we are in trouble
9815 if (!found_vertex_index)
9816 {
9817 std::stringstream error;
9818 error
9819 << "The current shared boundary (" << bound_id << ") was "
9820 << "marked to have a connection\nto the left with the "
9821 << "boundary (" << uconnection_to_the_left << ").\n"
9822 << "This last boundary is marked to be overlaped by "
9823 << "shared boundaries\n"
9824 << "The problem is that the left vertex of the current\n"
9825 << "shared boundary is not in the list of vertices of the\n"
9826 << "boundary to connect.\n\n"
9827 << "This is the left vertex of the current shared "
9828 "boundary\n"
9829 << "Left vertex: (" << shd_bnd_left_vertex[0] << ", "
9830 << shd_bnd_left_vertex[1] << ")\n\n"
9831 << "This is the list of vertices on the destination "
9832 << "boundary\n";
9834 get_shared_boundaries_overlapping_internal_boundary(
9836 const unsigned n_shd_bnd_overlap_int_bnd =
9838 for (unsigned ss = 0; ss < n_shd_bnd_overlap_int_bnd; ss++)
9839 {
9840 const unsigned new_connection_to_the_left =
9843 boundary_polyline_pt(new_connection_to_the_left);
9844 if (poly_to_connect_pt != 0)
9845 {
9846 const unsigned shd_bnd_id_overlap =
9847 poly_to_connect_pt->boundary_id();
9848 error << "Shared boundary id(" << shd_bnd_id_overlap
9849 << ")\n";
9850 const unsigned n_v = poly_to_connect_pt->nvertex();
9851 for (unsigned i = 0; i < n_v; i++)
9852 {
9854 poly_to_connect_pt->vertex_coordinate(i);
9855 error << "Vertex #" << i << ": (" << cvertex[0] << ", "
9856 << cvertex[1] << ")\n";
9857 }
9858 } // if (poly_to_connect_pt != 0)
9859 } // for (ss < n_shd_bnd_overlap_int_bnd)
9860
9861 throw OomphLibError(
9862 error.str(),
9863 "TriangleMesh::create_shared_polylines_connections()",
9865
9866 } // if (!found_vertex_index)
9867#endif
9868
9869 // Create the connection, the left vertex of the current
9870 // shared boundary is connected with the vertex_index-th
9871 // vertex on the destination boundary
9872 shd_poly_pt->connect_initial_vertex_to_polyline(
9874
9875 } // else if (!connecting_to_an_overlaped_boundary)
9876
9877 } // if (!connecting_to_an_split_boundary)
9878 else
9879 {
9880 // If the boundary was split then we need to look for the
9881 // vertex in the sub-polylines
9882
9883 // Get the sub-polylines vector
9885 boundary_subpolylines(uconnection_to_the_left);
9886
9887 // Get the number of sub-polylines
9888 const unsigned nsub_poly = tmp_vector_subpolylines.size();
9889#ifdef PARANOID
9890 if (nsub_poly <= 1)
9891 {
9892 std::ostringstream error_message;
9893 error_message
9894 << "The boundary (" << uconnection_to_the_left << ") was "
9895 << "marked to be splitted but\n"
9896 << "there are only (" << nsub_poly << ") polylines to "
9897 << "represent it.\n";
9898 throw OomphLibError(
9899 error_message.str(),
9900 "TriangleMesh::create_shared_polylines_connections()",
9902 } // if (nsub_poly <= 1)
9903#endif
9904 // We need to check if the boundary is marked to be
9905 // overlaped by an internal boundary, if that is the case
9906 // we need to check for each indivual subpolyline, and for
9907 // those overlaped by a shared polyline look for the
9908 // vertex in the shared polyline representation instead of
9909 // the original subpolyline
9910
9911 // ... check if the boundary is marked to be overlaped by
9912 // a shared boundary
9914 {
9915 // We can work without checking the subpolylines
9916 // individually
9917
9918 // The vertex where to store the index to connect
9919 unsigned vertex_index = 0;
9920 // The subpoly number to connect
9921 unsigned sub_poly_to_connect = 0;
9922 // A flag to indicate if the connection was found
9923 bool found_vertex_index = false;
9924
9925 // Look for the vertex number to connect on each of the
9926 // subpolyines
9927 for (unsigned isub = 0; isub < nsub_poly; isub++)
9928 {
9929 // Assign the pointer to the sub-polyline
9931 // Search for the vertex in the current sub-polyline
9933 get_connected_vertex_number_on_destination_polyline(
9935 // If we have found the vertex to connect then break the
9936 // loop
9938 {
9939 // But first save the subpoly number (chunk), that
9940 // will be used to perform the connection
9942 break;
9943 } // if (found_vertex_index)
9944 } // for (isub < nsub_poly)
9945
9946#ifdef PARANOID
9947 // If we could not find the vertex index to connect then
9948 // we are in trouble
9949 if (!found_vertex_index)
9950 {
9951 std::stringstream error;
9952 error
9953 << "The current shared boundary (" << bound_id << ") was "
9954 << "marked to have a connection\nto the left with the "
9955 << "boundary (" << uconnection_to_the_left << ").\n"
9956 << "The problem is that the left vertex of the current\n"
9957 << "shared boundary is not in the list of vertices of any\n"
9958 << "of the sub polylines that represent the boundary to\n"
9959 << "connect.\n\n"
9960 << "This is the left vertex of the current shared "
9961 "boundary\n"
9962 << "Left vertex: (" << shd_bnd_left_vertex[0] << ", "
9963 << shd_bnd_left_vertex[1] << ")\n\n"
9964 << "This is the list of vertices on the destination "
9965 << "boundary\n";
9966 for (unsigned p = 0; p < nsub_poly; p++)
9967 {
9968 error << "Subpolyline #(" << p << ")\n";
9970 const unsigned n_v = poly_to_connect_pt->nvertex();
9971 for (unsigned i = 0; i < n_v; i++)
9972 {
9974 poly_to_connect_pt->vertex_coordinate(i);
9975 error << "Vertex #" << i << ": (" << cvertex[0] << ", "
9976 << cvertex[1] << ")\n";
9977 }
9978 } // for (p < nsub_poly)
9979 throw OomphLibError(
9980 error.str(),
9981 "TriangleMesh::create_shared_polylines_connections()",
9983 } // if (!found_vertex_index)
9984#endif
9985
9986 // Create the connection, the left vertex of the current
9987 // shared boundary is connected with the vertex_index-th
9988 // vertex of sub_poly_to_connect-th subpolyline of the
9989 // destination boundary
9990 shd_poly_pt->connect_initial_vertex_to_polyline(
9992
9993 } // if (!connecting_to_an_overlaped_boundary)
9994 else
9995 {
9996 // We first look on the shared boundaries that overlap
9997 // the internal boundaries and the look for the
9998 // sub-polylines that are not marked as being overlaped
9999 // by shared boundaries
10000
10001 // The vertex where to store the index to connect
10002 unsigned vertex_index = 0;
10003 // The subpoly number to connect
10004 unsigned sub_poly_to_connect = 0;
10005 // A flag to indicate if the connection was found
10006 bool found_vertex_index = false;
10007
10008 // Get the shared boundaries id that are overlaping the
10009 // internal boundary
10011 get_shared_boundaries_overlapping_internal_boundary(
10013
10014 // Get the number of shared polylines that were found to
10015 // overlap the internal boundary
10016 const unsigned n_shd_bnd_overlap_int_bnd =
10018
10019 // Loop over the shared boundaries that overlap the
10020 // internal boundary and look for the vertex to connect
10021 for (unsigned ss = 0; ss < n_shd_bnd_overlap_int_bnd; ss++)
10022 {
10023 // Get the shared polyline
10024 const unsigned new_connection_to_the_left =
10026
10027 // Make sure that the destination polyline is not the
10028 // same as the current shared polyline
10030 {
10031 // Get the shared polyline that is overlaping the
10032 // internal boundary
10034 boundary_polyline_pt(new_connection_to_the_left);
10035
10036 if (poly_to_connect_pt != 0)
10037 {
10038 // Look for the vertex number in the destination
10039 // shared polyline
10041 get_connected_vertex_number_on_destination_polyline(
10044 vertex_index);
10045 } // if (poly_to_connect_pt != 0)
10046
10047 // If we have found the vertex to connect then
10048 // break the loop
10050 {
10051 break;
10052 } // if (found_vertex_index)
10053
10054 } // if (bound_id != new_connection_to_the_left)
10055
10056 } // for (ss < n_shd_bnd_overlaping_int_bnd)
10057
10058 // If we have not yet found the vertex then look for it
10059 // in the sub-polylines that are not overlaped by shared
10060 // boundaries
10061 if (!found_vertex_index)
10062 {
10063 // Look for the vertex number to connect on each of
10064 // the subpolyines
10065 for (unsigned isub = 0; isub < nsub_poly; isub++)
10066 {
10067 // Only work with those sub-polylines that are not
10068 // overlaped by shared boundaries
10069 if (!boundary_marked_as_shared_boundary(
10071 {
10072 // Assign the pointer to the sub-polyline
10074 // Search for the vertex in the current sub-polyline
10076 get_connected_vertex_number_on_destination_polyline(
10079 vertex_index);
10080 // If we have found the vertex to connect then break the
10081 // loop
10083 {
10084 // But first save the subpoly number (chunk), that
10085 // will be used to perform the connection
10087 break;
10088 } // if (found_vertex_index)
10089
10090 } // if (not overlaped by shared boundary)
10091
10092 } // for (isub < nsub_poly)
10093
10094 } // if (!found_vertex_index)
10095
10096#ifdef PARANOID
10097 // If we could not find the vertex index to connect then
10098 // we are in trouble
10099 if (!found_vertex_index)
10100 {
10101 std::stringstream error;
10102 error
10103 << "The current shared boundary (" << bound_id << ") was "
10104 << "marked to have a connection\nto the left with the "
10105 << "boundary (" << uconnection_to_the_left << ").\n"
10106 << "This last boundary is marked to be overlaped by "
10107 << "shared boundaries\n"
10108 << "The problem is that the left vertex of the current\n"
10109 << "shared boundary is not in the list of vertices of "
10110 << "the\nboundary to connect.\n\n"
10111 << "This is the left vertex of the current shared "
10112 << "boundary\n"
10113 << "Left vertex: (" << shd_bnd_left_vertex[0] << ", "
10114 << shd_bnd_left_vertex[1] << ")\n\n"
10115 << "This is the list of vertices on the destination "
10116 << "boundary (only those subpolylines not marked as "
10117 << "overlaped by\nshared boundaries)\n";
10118 for (unsigned p = 0; p < nsub_poly; p++)
10119 {
10120 if (!boundary_marked_as_shared_boundary(
10122 {
10123 error << "Subpolyline #(" << p << ")\n";
10125 const unsigned n_v = poly_to_connect_pt->nvertex();
10126 for (unsigned i = 0; i < n_v; i++)
10127 {
10129 poly_to_connect_pt->vertex_coordinate(i);
10130 error << "Vertex #" << i << ": (" << cvertex[0] << ", "
10131 << cvertex[1] << ")\n";
10132 }
10133 } // Not marked as overlaped
10134 } // for (p < nsub_poly)
10135 error << "\nThis is the list of vertices of the shared "
10136 << "polylines that overlap\nthe internal "
10137 << "boundary\n";
10139 get_shared_boundaries_overlapping_internal_boundary(
10141 const unsigned n_shd_bnd_overlap_int_bnd =
10143 for (unsigned ss = 0; ss < n_shd_bnd_overlap_int_bnd; ss++)
10144 {
10145 const unsigned new_connection_to_the_left =
10148 boundary_polyline_pt(new_connection_to_the_left);
10149 if (poly_to_connect_pt != 0)
10150 {
10151 const unsigned shd_bnd_id_overlap =
10152 poly_to_connect_pt->boundary_id();
10153 error << "Shared boundary id(" << shd_bnd_id_overlap
10154 << ")\n";
10155 const unsigned n_v = poly_to_connect_pt->nvertex();
10156 for (unsigned i = 0; i < n_v; i++)
10157 {
10159 poly_to_connect_pt->vertex_coordinate(i);
10160 error << "Vertex #" << i << ": (" << cvertex[0] << ", "
10161 << cvertex[1] << ")\n";
10162 }
10163 } // if (poly_to_connect_pt != 0)
10164 } // for (ss < n_shd_bnd_overlap_int_bnd)
10165
10166 throw OomphLibError(
10167 error.str(),
10168 "TriangleMesh::create_shared_polylines_connections()",
10170 } // if (!found_vertex_index)
10171#endif
10172
10173 // Create the connection, the left vertex of the current
10174 // shared boundary is connected with the vertex_index-th
10175 // vertex of sub_poly_to_connect-th subpolyline of the
10176 // destination boundary
10177 shd_poly_pt->connect_initial_vertex_to_polyline(
10179
10180 } // else if (!connecting_to_an_overlaped_boundary)
10181
10182 } // else if (!connecting_to_an_split_boundary)
10183
10184 } // if (connection_to_the_left != -1)
10185
10186 // --------------------------------------------------------------
10187 // Connection to the right
10189 {
10190 // Get the unsigned version of the bound id to connect to
10191 // the right
10192 const unsigned uconnection_to_the_right =
10193 shd_poly_pt->final_vertex_connected_bnd_id();
10194
10195 // The pointer to the boundary to connect
10197
10198 // Flag to indicate we are trying to connect to an split
10199 // boundary
10201
10202 // Flag to indicate we are trying to connecto to an internal
10203 // boundary that is overlaped by a shared boundary
10205
10206 // Check if the connection is with itself
10208 {
10209 // Set the pointer to the polyline to connect
10211 }
10212 else
10213 {
10214 // Get the initial shared boundary ids
10215 const unsigned initial_shd_bnd_id = initial_shared_boundary_id();
10216 // Check if the boundary to connect is a shared polyline
10218 {
10219 // Get the polyline pointer representing the destination
10220 // boundary
10222 boundary_polyline_pt(uconnection_to_the_right);
10223 } // if (uconnection_to_the_left >= initial_shd_bnd_id)
10224 else
10225 {
10226 // If we are going to connect to an original boundary
10227 // verify if the boundary was splitted during the
10228 // distribution process to consider all the chunks
10229 // (sub-polylines) of the boundary
10230 if (boundary_was_splitted(uconnection_to_the_right))
10231 {
10233 } // if (boundary_was_splitted(uconnection_to_the_right))
10234
10235 // If we are going to connect to an original boundary
10236 // verify if the boundary, or any of its chunks is
10237 // marked to be overlapped by a shared boundary, if that
10238 // is the case we first check for connections in the
10239 // shared boundary that overlaps the internal boundary,
10240 // or the chunks, and then check for connections in the
10241 // original boundary
10243 {
10244 // Get the number of chucks that represent the
10245 // destination boundary
10246 const unsigned n_sub_poly =
10247 nboundary_subpolylines(uconnection_to_the_right);
10248 // Now loop over the chunks of the destination
10249 // boundary and if any of them is marked to be
10250 // overlaped by a shared boundary then set the flag
10251 // and break the loop
10252 for (unsigned ii = 0; ii < n_sub_poly; ii++)
10253 {
10254 if (boundary_marked_as_shared_boundary(
10256 {
10257 // Mark the boundary as being overlaped by a
10258 // shared boundary
10260 // Break, no need to look for more overlapings
10261 break;
10262 } // if (boundary_marked_as_shared_boundary(...))
10263 } // for (ii < n_sub_poly)
10264 } // if (connecting_to_an_split_boundary)
10265 else
10266 {
10267 // If not connecting to an split boundary then check
10268 // if the whole destination boundary is overlaped by
10269 // an internal boundary
10270 if (boundary_marked_as_shared_boundary(
10272 {
10273 // Mark the boundary as being overlaped by a shared
10274 // boundary
10276 } // if (boundary_marked_as_shared_boundary(...))
10277 } // else if (connecting_to_an_split_boundary)
10278
10279 // If we are connecting neither to an split boundary nor
10280 // an overlaped boundary then get the pointer to the
10281 // original boundary
10284 {
10285 // Get the polyline pointer representing the
10286 // destination boundary
10288 boundary_polyline_pt(uconnection_to_the_right);
10289 } // else if (NOT split, NOT overlaped)
10290 } // else if (uconnection_to_the_right >= initial_shd_bnd_id)
10291
10292 } // else if (uconnection_to_the_right == bound_id)
10293
10294#ifdef PARANOID
10295 // If we are not connecting to an original boundary
10296 // (connecting to the same shared boundary or to another
10297 // shared boundary) then the boundary should not be marked
10298 // as split
10300 {
10301 if (boundary_was_splitted(uconnection_to_the_right))
10302 {
10303 std::stringstream error;
10304 error
10305 << "The current shared boundary (" << bound_id << ") was "
10306 << "marked to have a connection\nto the right with the "
10307 << "boundary (" << uconnection_to_the_right << ").\n"
10308 << "The problem is that the destination boundary (possibly\n"
10309 << "another shared boundary) is marked to be split\n"
10310 << "There should not be split shared boundaries\n\n";
10311 throw OomphLibError(
10312 error.str(),
10313 "TriangleMesh::create_shared_polylines_connections()",
10315 }
10316 } // if (!connecting_to_an_split_boundary)
10317#endif
10318
10319 // Now look for the vertex number on the destination
10320 // boundary(ies) -- in case that the boundary was split ---
10321
10322 // Do not check for same orientation, that was previously
10323 // worked by interchanging the connections boundaries (if
10324 // necessary)
10325
10326 // Get the right vertex in the shared boundary
10328 shd_poly_pt->vertex_coordinate(n_vertex - 1);
10329
10330 // If the boundary was not split then inmediately look for
10331 // the vertex index in the destination boundary
10333 {
10334 // ... check if the boundary is marked to be overlaped by
10335 // a shared boundary
10337 {
10338 // If that is not the case then we can safely look for
10339 // the vertex number on the destination boundar
10340
10341 unsigned vertex_index = 0;
10342 const bool found_vertex_index =
10343 get_connected_vertex_number_on_destination_polyline(
10345
10346 // If we could not find the vertex index to connect then
10347 // we are in trouble
10348 if (!found_vertex_index)
10349 {
10350 std::stringstream error;
10351 error
10352 << "The current shared boundary (" << bound_id << ") was "
10353 << "marked to have a connection\nto the right with the "
10354 << "boundary (" << uconnection_to_the_right << ").\n"
10355 << "The problem is that the right vertex of the current\n"
10356 << "shared boundary is not in the list of vertices of the\n"
10357 << "boundary to connect.\n\n"
10358 << "This is the right vertex of the current shared "
10359 "boundary\n"
10360 << "Right vertex: (" << shd_bnd_right_vertex[0] << ", "
10361 << shd_bnd_right_vertex[1] << ")\n\n"
10362 << "This is the list of vertices on the destination "
10363 "boundary\n";
10364 const unsigned n_v = poly_to_connect_pt->nvertex();
10365 for (unsigned i = 0; i < n_v; i++)
10366 {
10368 poly_to_connect_pt->vertex_coordinate(i);
10369 error << "Vertex #" << i << ": (" << cvertex[0] << ", "
10370 << cvertex[1] << ")\n";
10371 }
10372 throw OomphLibError(
10373 error.str(),
10374 "TriangleMesh::create_shared_polylines_connections()",
10376 } // if (!found_vertex_index)
10377
10378 // Create the connection, the right vertex of the current
10379 // shared boundary is connected with the vertex_index-th
10380 // vertex on the destination boundary
10381 shd_poly_pt->connect_final_vertex_to_polyline(
10383
10384 } // if (!connecting_to_an_overlaped_boundary)
10385 else
10386 {
10387 // If the boundary is marked to be overlaped by a shared
10388 // boundary then get that shared boundary and look for
10389 // the connection in that boundary
10390
10391 // The vertex where to store the index to connect
10392 unsigned vertex_index = 0;
10393 // A flag to indicate if the connection was found
10394 bool found_vertex_index = false;
10395
10396 // Get the shared boundary id that is overlaping the
10397 // internal boundary
10399 get_shared_boundaries_overlapping_internal_boundary(
10401
10402 // Get the number of shared polylines that were found to
10403 // overlap the internal boundary
10404 const unsigned n_shd_bnd_overlap_int_bnd =
10406
10407 // Loop over the shared boundaries that overlap the
10408 // internal boundary and look for the vertex to connect
10409 for (unsigned ss = 0; ss < n_shd_bnd_overlap_int_bnd; ss++)
10410 {
10411 // Get the shared polyline
10412 const unsigned new_connection_to_the_right =
10414
10415 // Get the shared polyline that is overlaping the
10416 // internal boundary
10418 boundary_polyline_pt(new_connection_to_the_right);
10419
10420 if (poly_to_connect_pt != 0)
10421 {
10422 // Look for the vertex number in the destination
10423 // shared polyline
10425 get_connected_vertex_number_on_destination_polyline(
10427 } // if (poly_to_connect_pt!=0)
10428
10429 // If we have found the vertex to connect then
10430 // break the loop
10432 {
10433 break;
10434 } // if (found_vertex_index)
10435
10436 } // for (ss < n_shd_bnd_overlaping_int_bnd)
10437
10438#ifdef PARANOID
10439 // If we could not find the vertex index to connect then
10440 // we are in trouble
10441 if (!found_vertex_index)
10442 {
10443 std::stringstream error;
10444 error
10445 << "The current shared boundary (" << bound_id << ") was "
10446 << "marked to have a connection\nto the right with the "
10447 << "boundary (" << uconnection_to_the_right << ").\n"
10448 << "This last boundary is marked to be overlaped by "
10449 << "shared boundaries\n"
10450 << "The problem is that the right vertex of the current\n"
10451 << "shared boundary is not in the list of vertices of the\n"
10452 << "boundary to connect.\n\n"
10453 << "This is the right vertex of the current shared "
10454 "boundary\n"
10455 << "Right vertex: (" << shd_bnd_right_vertex[0] << ", "
10456 << shd_bnd_right_vertex[1] << ")\n\n"
10457 << "This is the list of vertices on the destination "
10458 << "boundary\n";
10460 get_shared_boundaries_overlapping_internal_boundary(
10462 const unsigned n_shd_bnd_overlap_int_bnd =
10464 for (unsigned ss = 0; ss < n_shd_bnd_overlap_int_bnd; ss++)
10465 {
10466 const unsigned new_connection_to_the_right =
10469 boundary_polyline_pt(new_connection_to_the_right);
10470 if (poly_to_connect_pt != 0)
10471 {
10472 const unsigned shd_bnd_id_overlap =
10473 poly_to_connect_pt->boundary_id();
10474 error << "Shared boundary id(" << shd_bnd_id_overlap
10475 << ")\n";
10476 const unsigned n_v = poly_to_connect_pt->nvertex();
10477 for (unsigned i = 0; i < n_v; i++)
10478 {
10480 poly_to_connect_pt->vertex_coordinate(i);
10481 error << "Vertex #" << i << ": (" << cvertex[0] << ", "
10482 << cvertex[1] << ")\n";
10483 }
10484 } // if (poly_to_connect_pt != 0)
10485 } // for (ss < n_shd_bnd_overlap_int_bnd)
10486
10487 throw OomphLibError(
10488 error.str(),
10489 "TriangleMesh::create_shared_polylines_connections()",
10491
10492 } // if (!found_vertex_index)
10493#endif
10494
10495 // Create the connection, the right vertex of the
10496 // current shared boundary is connected with the
10497 // vertex_index-th vertex on the destination boundary
10498 shd_poly_pt->connect_final_vertex_to_polyline(
10500
10501 } // else if (!connecting_to_an_overlaped_boundary)
10502
10503 } // if (!connecting_to_an_split_boundary)
10504 else
10505 {
10506 // If the boundary was split then we need to look for the
10507 // vertex in the sub-polylines
10508
10509 // Get the sub-polylines vector
10511 boundary_subpolylines(uconnection_to_the_right);
10512
10513 // Get the number of sub-polylines
10514 const unsigned nsub_poly = tmp_vector_subpolylines.size();
10515#ifdef PARANOID
10516 if (nsub_poly <= 1)
10517 {
10518 std::ostringstream error_message;
10519 error_message
10520 << "The boundary (" << uconnection_to_the_right << ") was "
10521 << "marked to be splitted but\n"
10522 << "there are only (" << nsub_poly << ") polylines to "
10523 << "represent it.\n";
10524 throw OomphLibError(
10525 error_message.str(),
10526 "TriangleMesh::create_shared_polylines_connections()",
10528 } // if (nsub_poly <= 1)
10529#endif
10530
10531 // We need to check if the boundary is marked to be
10532 // overlaped by an internal boundary, if that is the case
10533 // we need to check for each indivual subpolyline, and for
10534 // those overlaped by a shared polyline look for the
10535 // vertex in the shared polyline representation instead of
10536 // the original subpolyline
10537
10538 // ... check if the boundary is marked to be overlaped by
10539 // a shared boundary
10541 {
10542 // We can work without checking the subpolylines
10543 // individually
10544
10545 // The vertex where to store the index to connect
10546 unsigned vertex_index = 0;
10547 // The subpoly number to connect
10548 unsigned sub_poly_to_connect = 0;
10549 // A flag to indicate if the connection was found
10550 bool found_vertex_index = false;
10551
10552 // Look for the vertex number to connect on each of the
10553 // subpolyines
10554 for (unsigned isub = 0; isub < nsub_poly; isub++)
10555 {
10556 // Assign the pointer to the sub-polyline
10558 // Search for the vertex in the current sub-polyline
10560 get_connected_vertex_number_on_destination_polyline(
10562 // If we have found the vertex to connect then break the
10563 // loop
10565 {
10566 // But first save the subpoly number (chunk), that
10567 // will be used to perform the connection
10569 break;
10570 } // if (found_vertex_index)
10571 } // for (isub < nsub_poly)
10572
10573#ifdef PARANOID
10574 // If we could not find the vertex index to connect then
10575 // we are in trouble
10576 if (!found_vertex_index)
10577 {
10578 std::stringstream error;
10579 error
10580 << "The current shared boundary (" << bound_id << ") was "
10581 << "marked to have a connection\nto the right with the "
10582 << "boundary (" << uconnection_to_the_right << ").\n"
10583 << "The problem is that the right vertex of the current\n"
10584 << "shared boundary is not in the list of vertices of any\n"
10585 << "of the sub polylines that represent the boundary to\n"
10586 << "connect.\n\n"
10587 << "This is the right vertex of the current shared "
10588 "boundary\n"
10589 << "Right vertex: (" << shd_bnd_right_vertex[0] << ", "
10590 << shd_bnd_right_vertex[1] << ")\n\n"
10591 << "This is the list of vertices on the destination "
10592 << "boundary\n";
10593 for (unsigned p = 0; p < nsub_poly; p++)
10594 {
10595 error << "Subpolyline #(" << p << ")\n";
10597 const unsigned n_v = poly_to_connect_pt->nvertex();
10598 for (unsigned i = 0; i < n_v; i++)
10599 {
10601 poly_to_connect_pt->vertex_coordinate(i);
10602 error << "Vertex #" << i << ": (" << cvertex[0] << ", "
10603 << cvertex[1] << ")\n";
10604 }
10605 } // for (p < nsub_poly)
10606 throw OomphLibError(
10607 error.str(),
10608 "TriangleMesh::create_shared_polylines_connections()",
10610 } // if (!found_vertex_index)
10611#endif
10612
10613 // Create the connection, the right vertex of the current
10614 // shared boundary is connected with the vertex_index-th
10615 // vertex of sub_poly_to_connect-th subpolyline of the
10616 // destination boundary
10617 shd_poly_pt->connect_final_vertex_to_polyline(
10619
10620 } // if (!connecting_to_an_overlaped_boundary)
10621 else
10622 {
10623 // We first look on the shared boundaries that overlap
10624 // the internal boundaries and the look for the
10625 // sub-polylines that are not marked as being overlaped
10626 // by shared boundaries
10627
10628 // The vertex where to store the index to connect
10629 unsigned vertex_index = 0;
10630 // The subpoly number to connect
10631 unsigned sub_poly_to_connect = 0;
10632 // A flag to indicate if the connection was found
10633 bool found_vertex_index = false;
10634
10635 // Get the shared boundaries id that are overlaping the
10636 // internal boundary
10638 get_shared_boundaries_overlapping_internal_boundary(
10640
10641 // Get the number of shared polylines that were found to
10642 // overlap the internal boundary
10643 const unsigned n_shd_bnd_overlap_int_bnd =
10645
10646 // Loop over the shared boundaries that overlap the
10647 // internal boundary and look for the vertex to connect
10648 for (unsigned ss = 0; ss < n_shd_bnd_overlap_int_bnd; ss++)
10649 {
10650 // Get the shared polyline
10651 const unsigned new_connection_to_the_right =
10653
10654 // Make sure that the destination polyline is not the
10655 // same as the current shared polyline
10657 {
10658 // Get the shared polyline that is overlaping the
10659 // internal boundary
10661 boundary_polyline_pt(new_connection_to_the_right);
10662
10663 if (poly_to_connect_pt != 0)
10664 {
10665 // Look for the vertex number in the destination
10666 // shared polyline
10668 get_connected_vertex_number_on_destination_polyline(
10671 vertex_index);
10672 } // if (poly_to_connect_pt != 0)
10673
10674 // If we have found the vertex to connect then
10675 // break the loop
10677 {
10678 break;
10679 } // if (found_vertex_index)
10680
10681 } // if (bound_id != new_connection_to_the_right)
10682
10683 } // for (ss < n_shd_bnd_overlaping_int_bnd)
10684
10685 // If we have not yet found the vertex then look for it
10686 // in the sub-polylines that are not overlaped by shared
10687 // boundaries
10688 if (!found_vertex_index)
10689 {
10690 // Look for the vertex number to connect on each of
10691 // the subpolyines
10692 for (unsigned isub = 0; isub < nsub_poly; isub++)
10693 {
10694 // Only work with those sub-polylines that are not
10695 // overlaped by shared boundaries
10696 if (!boundary_marked_as_shared_boundary(
10698 {
10699 // Assign the pointer to the sub-polyline
10701 // Search for the vertex in the current sub-polyline
10703 get_connected_vertex_number_on_destination_polyline(
10706 vertex_index);
10707 // If we have found the vertex to connect then break the
10708 // loop
10710 {
10711 // But first save the subpoly number (chunk), that
10712 // will be used to perform the connection
10714 break;
10715 } // if (found_vertex_index)
10716
10717 } // if (not overlaped by shared boundary)
10718
10719 } // for (isub < nsub_poly)
10720
10721 } // if (!found_vertex_index)
10722
10723#ifdef PARANOID
10724 // If we could not find the vertex index to connect then
10725 // we are in trouble
10726 if (!found_vertex_index)
10727 {
10728 std::stringstream error;
10729 error
10730 << "The current shared boundary (" << bound_id << ") was "
10731 << "marked to have a connection\nto the right with the "
10732 << "boundary (" << uconnection_to_the_right << ").\n"
10733 << "This last boundary is marked to be overlaped by "
10734 << "shared boundaries\n"
10735 << "The problem is that the right vertex of the current\n"
10736 << "shared boundary is not in the list of vertices of "
10737 << "the\nboundary to connect.\n\n"
10738 << "This is the right vertex of the current shared "
10739 << "boundary\n"
10740 << "Right vertex: (" << shd_bnd_right_vertex[0] << ", "
10741 << shd_bnd_right_vertex[1] << ")\n\n"
10742 << "This is the list of vertices on the destination "
10743 << "boundary (only those subpolylines not marked as "
10744 << "overlaped by\nshared boundaries)\n";
10745 for (unsigned p = 0; p < nsub_poly; p++)
10746 {
10747 if (!boundary_marked_as_shared_boundary(
10749 {
10750 error << "Subpolyline #(" << p << ")\n";
10752 const unsigned n_v = poly_to_connect_pt->nvertex();
10753 for (unsigned i = 0; i < n_v; i++)
10754 {
10756 poly_to_connect_pt->vertex_coordinate(i);
10757 error << "Vertex #" << i << ": (" << cvertex[0] << ", "
10758 << cvertex[1] << ")\n";
10759 }
10760 } // Not marked as overlaped
10761 } // for (p < nsub_poly)
10762 error << "\nThis is the list of vertices of the shared "
10763 << "polylines that overlap\nthe internal "
10764 << "boundary\n";
10766 get_shared_boundaries_overlapping_internal_boundary(
10768 const unsigned n_shd_bnd_overlap_int_bnd =
10770 for (unsigned ss = 0; ss < n_shd_bnd_overlap_int_bnd; ss++)
10771 {
10772 const unsigned new_connection_to_the_right =
10775 boundary_polyline_pt(new_connection_to_the_right);
10776 if (poly_to_connect_pt != 0)
10777 {
10778 const unsigned shd_bnd_id_overlap =
10779 poly_to_connect_pt->boundary_id();
10780 error << "Shared boundary id(" << shd_bnd_id_overlap
10781 << ")\n";
10782 const unsigned n_v = poly_to_connect_pt->nvertex();
10783 for (unsigned i = 0; i < n_v; i++)
10784 {
10786 poly_to_connect_pt->vertex_coordinate(i);
10787 error << "Vertex #" << i << ": (" << cvertex[0] << ", "
10788 << cvertex[1] << ")\n";
10789 }
10790 } // if (poly_to_connect_pt != 0)
10791 } // for (ss < n_shd_bnd_overlap_int_bnd)
10792
10793 throw OomphLibError(
10794 error.str(),
10795 "TriangleMesh::create_shared_polylines_connections()",
10797 } // if (!found_vertex_index)
10798#endif
10799
10800 // Create the connection, the left vertex of the current
10801 // shared boundary is connected with the vertex_index-th
10802 // vertex of sub_poly_to_connect-th subpolyline of the
10803 // destination boundary
10804 shd_poly_pt->connect_final_vertex_to_polyline(
10806
10807 } // else if (!connecting_to_an_overlaped_boundary)
10808
10809 } // else if (!connecting_to_an_split_boundary)
10810
10811 } // if (connection_to_the_right != -1)
10812
10813 } // if (connection_to_the_left != -1 || connection_to_the_right != -1)
10814
10815 } // for (ipoly < npoly)
10816
10817 } // for (icurve < ncurves)
10818 }
10819
10820 //=======================================================================
10821 // Compute the holes left by the halo elements, those adjacent
10822 // to the shared boundaries
10823 //=======================================================================
10824 template<class ELEMENT>
10827 {
10828 // Storage for number of processors and current processor
10829 const unsigned n_proc = this->communicator_pt()->nproc();
10830 const unsigned my_rank = this->communicator_pt()->my_rank();
10831
10832 // Mark those done elements, so we do not repeat any coordinate left
10833 // by repeated halo elements
10834 std::map<FiniteElement*, bool> done_ele;
10835
10836 // Loop over the processors and get the shared boundaries ids that
10837 // the current processor has with the other processors
10838 for (unsigned iproc = 0; iproc < n_proc; iproc++)
10839 {
10840 // There are shared boundaries only with the other processors
10841 if (iproc != my_rank)
10842 {
10843 // Get the number of shared boundaries with the iproc
10844 const unsigned n_shd_bnd_iproc = nshared_boundaries(my_rank, iproc);
10845
10846#ifdef PARANOID
10847 // Get the number of shared boundaries with the iproc, but
10848 // reversing the indexes
10849 const unsigned n_shd_bnd_iproc_rev = nshared_boundaries(iproc, my_rank);
10851 {
10852 std::ostringstream error_stream;
10854 << "The number of shared boundaries of processor (" << my_rank
10855 << ") with processor(" << iproc << "): (" << n_shd_bnd_iproc
10856 << ")\n"
10857 << "is different from the number of shared boundaries of "
10858 << "processor (" << iproc << ")\nwith processor (" << my_rank
10859 << "): (" << n_shd_bnd_iproc << ")\n\n";
10860 throw OomphLibError(error_stream.str(),
10863
10864 } // if (n_shd_bnd_iproc != n_shd_bnd_iproc_rev)
10865#endif
10866
10867 // Loop over the shared boundaries ids
10868 for (unsigned i = 0; i < n_shd_bnd_iproc; i++)
10869 {
10870 // Get the shared boundary id
10871 const unsigned shd_bnd_id = shared_boundaries_ids(my_rank, iproc, i);
10872
10873 // Get the number of shared boundary elements
10874 const unsigned n_shd_bnd_ele = nshared_boundary_element(shd_bnd_id);
10875
10876 // Loop over the shared boundary elements
10877 for (unsigned e = 0; e < n_shd_bnd_ele; e++)
10878 {
10879 // Get the shared boundary element
10880 FiniteElement* ele_pt = shared_boundary_element_pt(shd_bnd_id, e);
10881
10882 // Only work with halo elements
10883 if (ele_pt->is_halo())
10884 {
10885 // If the element has not been visited
10886 if (!done_ele[ele_pt])
10887 {
10888 // Get the number of nodes
10889 const unsigned n_nodes = ele_pt->nnode();
10890
10891 // Compute the centroid of the element
10893 // Loop over the nodes
10894 for (unsigned k = 0; k < n_nodes; k++)
10895 {
10897 // Loop over the dimension
10898 for (unsigned d = 0; d < 2; d++)
10899 {
10900 element_centroid[d] += tmp_node_pt->x(d);
10901 } // for (d < 2)
10902 } // for (k < n_nodes)
10903
10904 // Average the data
10905 for (unsigned d = 0; d < 2; d++)
10906 {
10908 } // for (d < 2)
10909
10910 // Add the centroid to the output holes
10912
10913 } // if (!done_ele[ele_pt])
10914
10915 } // if (ele_pt->is_halo())
10916
10917 } // for1 (e < n_shd_bnd_ele)
10918
10919 } // for (i < n_shd_bnd_iproc)
10920
10921 } // if (iproc != my_rank)
10922
10923 } // for (iproc < n_proc)
10924 }
10925
10926 //======================================================================
10927 // Keeps those vertices that define a hole, those that are
10928 // inside closed internal boundaries in the new polygons that define
10929 // the domain. Delete those outside/inside the outer polygons (this
10930 // is required since Triangle can not deal with vertices that define
10931 // holes outside the new outer polygons of the domain)
10932 //======================================================================
10933 template<class ELEMENT>
10937 {
10938 // General strategy
10939
10940 // 1) Identify the inner closed boundaries
10941
10942 // 2) Separate the vertices in three groups
10943
10944 // --- 2.1) The vertices inside the inner closed boundaries, these
10945 // are not deleted because they define holes
10946
10947 // --- 2.2) The vertices outside the outer boundaries, these are
10948 // deleted only if they are outside the convex hull defined
10949 // by all the polygons
10950
10951 // --- 2.3) Any other vertex is deleted
10952
10953 // Get the number of input holes
10954 const unsigned n_input_holes = output_holes_coordinates.size();
10955
10956 // Only do something if there are holes
10957 if (n_input_holes == 0)
10958 {
10959 return;
10960 }
10961
10962 // Get the number of input polygons
10963 const unsigned n_polygons = polygons_pt.size();
10964
10965 // Store the vertices of all the input polygons
10966 // vertices_polygons[x][ ][ ]: Polygon number
10967 // vertices_polygons[ ][x][ ]: Vertex number
10968 // vertices_polygons[ ][ ][x]: Vertex coordinate
10970
10971 // Loop over all the polygons and get the vertices
10972 for (unsigned p = 0; p < n_polygons; p++)
10973 {
10974 // Get the number of polylines associated to the polygon
10975 const unsigned n_polylines = polygons_pt[p]->npolyline();
10976 // Loop over the polylines and get the vertices
10977 for (unsigned pp = 0; pp < n_polylines; pp++)
10978 {
10979 // Get the polyline
10981 polygons_pt[p]->polyline_pt(pp);
10982 // Get the number of vertices in the polyline
10983 const unsigned n_vertices = tmp_poly_pt->nvertex();
10984 // Loop over the vertices but only add (n_vertices-1) vertices,
10985 // the last vertex of polyline (pp) is the first vertex of
10986 // polyline (pp+1)
10987 for (unsigned v = 0; v < n_vertices - 1; v++)
10988 {
10989 // Get the current vertex
10990 Vector<double> current_vertex = tmp_poly_pt->vertex_coordinate(v);
10992 } // for (v < nvertex)
10993 } // for (p < nouter_polylines)
10994 } // for (p < n_polygons)
10995
10996 // -------------------------------------------------------------------
10997 // 1) Identify the inner closed boundaries
10998 // -------------------------------------------------------------------
10999
11000 // A container that indicates if a given polygon should be
11001 // considered as an outer or as an inner polygon. By default all the
11002 // polygons are considered as outer polygons
11003 std::vector<bool> is_outer_polygon(n_polygons, true);
11004
11005 // We only check for innner polygons if there are more than one
11006 // polygon
11007 if (n_polygons > 1)
11008 {
11009 // Propose an inner polygon, if one of the middle points of its
11010 // edges lies inside any other polygon then the proposed inner
11011 // polygon is marked as an internal polygon
11012
11013 // Pre-compute the middle points of the edges in the polygons
11015
11016 for (unsigned p = 0; p < n_polygons; p++)
11017 {
11018 // Temporary store the vertices of the proposed inner polygon
11020
11021 // Get the number of vertices in the current proposed inner polygon
11022 const unsigned n_vertices = tmp_inner_polygon.size();
11023
11024 // Resize with the number of edges in the polygon
11026
11027 // Loop over the vertices and compute the middle point in the edge
11028 // that joins each pair of contiguous vertices
11029 for (unsigned e = 0; e < n_vertices - 1; e++)
11030 {
11031 // The dimension
11032 const unsigned dim = 2;
11034 for (unsigned d = 0; d < dim; d++)
11035 {
11037 (tmp_inner_polygon[e][d] + tmp_inner_polygon[e + 1][d]) / 2.0;
11038 } // for (d < 2)
11039
11040 } // for (e < n_vertices - 1)
11041
11042 } // for (p < n_polygons)
11043
11044 // Loop over the polygons and for every loop propose a different
11045 // inner polygon
11046 for (unsigned idx_inner = 0; idx_inner < n_polygons; idx_inner++)
11047 {
11048 // Flag to indicate that ONE of the middle edge vertices of the
11049 // proposed inner polygon is inside another polygon, this will
11050 // set the proposed inner polygon as an actual inner polygon
11051 bool is_inner_polygon = false;
11052
11053 // Loop over all the polygons, except the proposed one and check
11054 // if all the middle edges of its edges are inside any other
11055 // polygon
11056 for (unsigned i = 0; i < n_polygons; i++)
11057 {
11058 // Do not check with the polygon itself
11059 if (i != idx_inner)
11060 {
11061 // Get the number of edges of the proposed inner polygon
11062 const unsigned n_edges =
11064 // Loop over the middle points in the edges of the current
11065 // proposed inner polygon
11066 for (unsigned e = 0; e < n_edges; e++)
11067 {
11068 // Get the vertex in the current proposed inner polygon
11071 // Check if the current vertex is inside the current i-th
11072 // polygon
11073 const bool is_point_inside = is_point_inside_polygon_helper(
11075
11076 // If one point is inside then the polygon is inside the
11077 // i-th polygon
11078 if (is_point_inside)
11079 {
11080 // The polygon is an inner polygon
11081 is_inner_polygon = true;
11082 // Break the loop
11083 break;
11084 } // if (is_point_inside)
11085
11086 } // for (e < n_edges)
11087
11088 } // if (i != idx_inner)
11089
11090 // Are all the vertices of the current proposed inner polygon
11091 // inside the i-th polygon
11092 if (is_inner_polygon)
11093 {
11094 // The current proposed inner polygon is an actual inner
11095 // polygon, and is inside the i-th polygon
11096 break;
11097 }
11098
11099 } // for (i < n_polygons)
11100
11101 // Is the current proposed inner polygon an actual inner polygon
11102 if (is_inner_polygon)
11103 {
11104 // The current proposed inner polygon is a real inner polygon
11105 is_outer_polygon[idx_inner] = false;
11106 }
11107 else
11108 {
11109 // The current proposed inner polygon IS NOT a real inner
11110 // polygon
11112 }
11113
11114 } // for (idx_outer < npolygons)
11115
11116 } // if (n_polygons > 1)
11117
11118 // Count the number of outer closed boundaries and inner closed
11119 // boundaries
11120 unsigned n_outer_polygons = 0;
11121 unsigned n_inner_polygons = 0;
11122 // Also get the indexes of the inner polygons
11124 // Loop over the polygons
11125 for (unsigned i = 0; i < n_polygons; i++)
11126 {
11127 if (is_outer_polygon[i])
11128 {
11129 // Increase the counter for outer polygons
11131 }
11132 else
11133 {
11134 // Increase the counter for inner polygons
11136 // Store the index of the inner polygon
11137 index_inner_polygon.push_back(i);
11138 }
11139 } // for (i < n_polygons)
11140
11141 // -------------------------------------------------------------------
11142 // 2) Separate the vertices in three groups
11143
11144 // --- 2.1) The vertices inside the inner closed boundaries, these are
11145 // not deleted because they define holes
11146
11147 // --- 2.2) The vertices outside the outer boundaries, these are
11148 // deleted only if they are outside the convex hull defined
11149 // by all the polygons
11150
11151 // --- 2.3) Any other vertex is deleted
11152 // -------------------------------------------------------------------
11153
11154 // Keep track of the vertices inside the inner closed boundaries (by
11155 // default all vertices not inside the inner polygons)
11156 std::vector<bool> is_inside_an_inner_polygon(n_input_holes, false);
11157
11158 // Keep track of the vertices outside the outer closed boundaries
11159 // (by default all the vertices are outside the outer polygons)
11160 std::vector<bool> is_outside_the_outer_polygons(n_input_holes, true);
11161
11162 // Keep track of the vertices inside the convex hull (by default
11163 // all the vertices are not inside the convex hull)
11164 std::vector<bool> is_inside_the_convex_hull(n_input_holes, false);
11165
11166 // Mark the vertices inside the inner closed boundaries
11169
11170 // -------------------------------------------------------------------
11171 // Loop over the inner polygons and find all the vertices inside
11172 // each one
11173 for (unsigned i = 0; i < n_inner_polygons; i++)
11174 {
11175 // Get the vertex of the inner polygon
11176 const unsigned ii = index_inner_polygon[i];
11177 // Loop over the vertices defining holes, mark and store those
11178 // inside the inner polygon
11179 for (unsigned h = 0; h < n_input_holes; h++)
11180 {
11181 // Check if the vertex has not been already marked as inside
11182 // another polygon
11184 {
11185 // Check if the hole is inside the current inner polygon
11186 const bool is_inside_polygon = is_point_inside_polygon_helper(
11188
11189 // If the vertex is inside the current inner polygon then mark
11190 // it and associate the vertices to the current inner polygon
11192 {
11193 // Set as inside an inner polygon
11195 // Associate the vertex to the current inner polygon
11196 vertex_inside_inner_polygon[i].push_back(
11198 } // if (is_inside_polygon)
11199
11200 } // if (!is_inside_an_inner_polygon[h])
11201
11202 } // for (h < n_input_holes)
11203
11204 } // for (i < n_polygons)
11205
11206 // -------------------------------------------------------------------
11207 // Loop over the vertices defining holes and mark those as outside the
11208 // outer polygons
11209 for (unsigned h = 0; h < n_input_holes; h++)
11210 {
11211 // Check if the vertex has not been already marked as inside
11212 // another polygon
11214 {
11215 // Loop over the polygons and check if the vertex is outside ALL
11216 // the outer polygons
11217 for (unsigned i = 0; i < n_polygons; i++)
11218 {
11219 // Only work with outer polygons
11220 if (is_outer_polygon[i])
11221 {
11222 // Check if the hole is inside the current outer polygon
11223 const bool is_inside_polygon = is_point_inside_polygon_helper(
11225
11226 // If the vertex is inside the current outer polygon then
11227 // mark it and break the loop (it is not outside ALL the
11228 // polygons)
11230 {
11231 // Set as inside an outer polygon
11233 // Break the loop
11234 break;
11235 } // if (is_inside_polygon)
11236
11237 } // if (is_outer_polygon[i])
11238
11239 } // for (i < n_polygons)
11240
11241 } // if (!is_inside_an_inner_polygon[h])
11242 else
11243 {
11244 // If the vertex is inside an inner polygon then it is inside an
11245 // outer polygon
11247 } // else if (!is_inside_an_inner_polygon[h])
11248
11249 } // for (h < n_input_holes)
11250
11251 // -------------------------------------------------------------------
11252 // Compute the convex hull Create the data structure
11253 std::vector<Point> input_vertices_convex_hull;
11254 // Copy ALL the vertices of the polygons
11255 // Loop over the polygons
11256 for (unsigned p = 0; p < n_polygons; p++)
11257 {
11258 // Get the number of vertices
11259 const unsigned n_vertices = vertices_polygons[p].size();
11260 // Loop over the vertices in the polygon
11261 for (unsigned v = 0; v < n_vertices; v++)
11262 {
11263 // Create a new "Point" to store in the input vertices
11264 Point point;
11265 // Assign the values to the "Point"
11266 point.x = vertices_polygons[p][v][0];
11267 point.y = vertices_polygons[p][v][1];
11268 // Add the "Point" to the input vertices
11270 } // for (v < n_vertices)
11271 } // for (p < n_polygons)
11272
11273 // Compute the convex hull
11274 std::vector<Point> output_vertices_convex_hull =
11275 convex_hull(input_vertices_convex_hull);
11276
11277 // Get the number of vertices in the convex hull
11279
11280 // Copy the output to the used data structures
11282 for (unsigned i = 0; i < n_vertices_convex_hull; i++)
11283 {
11284 // Resize the data structure
11285 vertices_convex_hull[i].resize(2);
11286 // Copy the data
11289 } // for (i < n_vertices_convex_hull)
11290
11291 // Loop over the vertices defining holes, work only with those
11292 // outside ALL the outer boundaries and mark those inside the convex
11293 // hull
11294 for (unsigned h = 0; h < n_input_holes; h++)
11295 {
11296 // Only work with those outside ALL the outer polygons
11298 {
11299 // Check if the hole is inside the convex hull
11300 const bool is_inside_convex_hull = is_point_inside_polygon_helper(
11302
11303 // If the vertex is inside the convex hull then mark it
11305 {
11306 // Set as inside the convex hull
11307 is_inside_the_convex_hull[h] = true;
11308 } // if (is_inside_convex_hull)
11309
11310 } // if (is_outside_the_outer_polygons[h])
11311 else
11312 {
11313 // Any vertex inside any outer polygon is inside the convex hull
11314 is_inside_the_convex_hull[h] = true;
11315 } // else if (is_outside_the_outer_polygons[h])
11316
11317 } // for (h < n_input_holes)
11318
11319 // Store the output holes, only (those inside an inner polygon) OR
11320 // (those outside ALL the polygons AND inside the convex hull)
11322 for (unsigned h = 0; h < n_input_holes; h++)
11323 {
11324 // Check if the hole should be kept
11325 if ((is_inside_an_inner_polygon[h]) ||
11327 {
11328 // Copy the hole information
11329 hole_kept.push_back(output_holes_coordinates[h]);
11330 } // if (keep_hole[h])
11331 } // for (h < n_input_holes)
11332
11333 // Clear the previous storage
11335 // Set the output holes
11337 }
11338
11339 //======================================================================
11340 // Sorts the polylines so they be contiguous and then we can
11341 // create a closed or open curve from them
11342 //======================================================================
11343 template<class ELEMENT>
11347 {
11349 unsigned n_sorted_polylines = 0;
11350 unsigned curves_index = 0;
11351
11352 // Map to know which polyline has been already sorted
11353 std::map<TriangleMeshPolyLine*, bool> done_polyline;
11354
11355 do
11356 {
11357 // Create the list that stores the polylines and allows to introduce
11358 // polylines to the left and to the right
11359 std::list<TriangleMeshPolyLine*> sorted_polyline_list_pt;
11360 bool changes = false;
11361
11362 // Create pointers to the left and right "side" of the sorted list of
11363 // new created TriangleMeshPolyLines
11366
11367 // 1) Take the first non done polyline on the unsorted list of polylines
11368 unsigned pp = 0;
11369 bool found_root_polyline = false;
11371 {
11373 {
11374 found_root_polyline = true;
11375 }
11376 else
11377 {
11378 pp++;
11379 }
11380 }
11381
11382 // Check if there are polylines to be sorted
11384 {
11385 // 2) Mark the polyline as done
11387 done_polyline[left_pt] = true;
11388 // Increment the number of sorted polylines
11390
11391 // 3) Add this polyline to the sorted list and use it as root
11392 // to sort the other polylines
11394
11395 do
11396 {
11397 changes = false;
11398
11401
11402 left_pt->initial_vertex_coordinate(left_vertex);
11403 right_pt->final_vertex_coordinate(right_vertex);
11404
11405 for (unsigned i = pp + 1; i < n_unsorted_polylines; i++)
11406 {
11410 {
11413 current_polyline_pt->initial_vertex_coordinate(initial_vertex);
11414 current_polyline_pt->final_vertex_coordinate(final_vertex);
11415
11416 // Compare if the current polyline should go to the left or
11417 // to the right on the sorted polyline list
11418
11419 // Go to the left
11421 {
11423 sorted_polyline_list_pt.push_front(left_pt);
11424 done_polyline[left_pt] = true;
11426
11427 // We have added one more polyline, go for another round
11428 changes = true;
11429 }
11430 // Go to the right
11431 else if (right_vertex == initial_vertex)
11432 {
11435 done_polyline[right_pt] = true;
11437
11438 // We have added one more polyline, go for another round
11439 changes = true;
11440 }
11441 // Go to the left but it is reversed
11442 else if (left_vertex == initial_vertex)
11443 {
11444 current_polyline_pt->reverse();
11446 sorted_polyline_list_pt.push_front(left_pt);
11447 done_polyline[left_pt] = true;
11449
11450 // We have added one more polyline, go for another round
11451 changes = true;
11452 }
11453 // Go to the right but it is reversed
11454 else if (right_vertex == final_vertex)
11455 {
11456 current_polyline_pt->reverse();
11459 done_polyline[right_pt] = true;
11461
11462 // We have added one more polyline, go for another round
11463 changes = true;
11464 }
11465 } // if (!done_polyline[current_polyline_pt])
11466 if (changes)
11467 {
11468 break;
11469 }
11470 } // for (i < n_unsorted_polylines)
11471 } while (changes);
11472
11473 } // if (pp < n_unsorted_polylines)
11474 else
11475 {
11476 // All the polylines are now on the sorted list of polylines
11477#ifdef PARANOID
11478 // This case comes when it was not possible to find a root polyline
11479 // since all of them are marked as done but the number of sorted and
11480 // unsorted polylines is not the same
11482 {
11483 std::stringstream err;
11484 err << "It was not possible to find a root polyline to sort the "
11485 << "others around it.\nThe number of unsorted and sorted "
11486 << "polylines is different, it means that\nnot all the "
11487 << "polylines have been sorted.\n"
11488 << "Found root polyline: (" << found_root_polyline << ")\n"
11489 << "Sorted polylines: (" << n_sorted_polylines << ")\n"
11490 << "Unsorted polylines: (" << n_unsorted_polylines << ")\n";
11491 throw OomphLibError(err.str(),
11492 "TriangleMesh::sort_polylines_helper()",
11494 }
11495#endif
11496 }
11497
11498 // Create the storage for the new sorted polylines and copy them on the
11499 // vector structure for sorted polylines
11501
11502 // Create the temporal vector that stores the sorted polylines
11505 unsigned counter = 0;
11506
11507 std::list<TriangleMeshPolyLine*>::iterator it_polyline;
11508 for (it_polyline = sorted_polyline_list_pt.begin();
11510 it_polyline++)
11511 {
11513 counter++;
11514 }
11515
11517
11518 ++curves_index;
11519
11521
11522#ifdef PARANOID
11523 // Verify that the number of polylines on the sorted list is the same
11524 // as the number of polylines on the unsorted list
11526 {
11527 std::stringstream err;
11528 err << "The number of polylines on the unsorted and sorted vectors"
11529 << " is different,\n"
11530 << "it means that not all the polylines have been sorted.\n"
11531 << "Sorted polylines: " << n_sorted_polylines
11532 << "\nUnsorted polylines: " << n_unsorted_polylines;
11533 throw OomphLibError(err.str(),
11534 "TriangleMesh::sort_polylines_helper()",
11536 }
11537#endif
11538 }
11539
11540 //======================================================================
11541 // Creates the shared boundaries
11542 //======================================================================
11543 template<class ELEMENT>
11545 OomphCommunicator* comm_pt,
11549 std::map<Data*, std::set<unsigned>>& processors_associated_with_data,
11551 {
11552 // Storage for number of processors and current processor
11553 const unsigned nproc = comm_pt->nproc();
11554 const unsigned my_rank = comm_pt->my_rank();
11555
11556 // Storage for all the halo elements on all processors
11557 // halo_element[iproc][jproc][ele_number]
11558 // Stores the "ele_number"-th halo element of processor "iproc" with
11559 // processor "jproc"
11561 // Create complete storage for the halo_element_pt container
11562 for (unsigned iproc = 0; iproc < nproc; iproc++)
11563 {
11564 halo_element_pt[iproc].resize(nproc);
11565 }
11566
11567 // Store the global index of the element, used to check for possible
11568 // misclassification of halo elements in the above container
11569 // (halo_element_pt)
11570 std::map<GeneralisedElement*, unsigned> element_to_global_index;
11571
11572 // Get the halo elements on all processors
11573 this->get_halo_elements_on_all_procs(nproc,
11579 halo_element_pt);
11580
11581 // Resize the shared polylines container
11582 flush_shared_boundary_polyline_pt();
11583 Shared_boundary_polyline_pt.resize(nproc);
11584
11585 // Create a set that store only the elements that will be kept in
11586 // the processor as nonhalo element, those whose element_domains is
11587 // equal to my_rank. This set is used when creating the shared
11588 // polylines and identify the connections to the original boundaries
11589 std::set<FiniteElement*> element_in_processor_pt;
11590 const unsigned n_ele = backed_up_f_el_pt.size();
11591 for (unsigned e = 0; e < n_ele; e++)
11592 {
11593 if (element_domain[e] == my_rank)
11594 {
11596 } // if (element_domain[e] == my_rank)
11597 } // for (e < n_elex)
11598
11599 // Look for elements edges that may lie on internal boundaries
11600 // If that is the case then relate the face with the boundary on
11601 // which it lies
11602 std::map<std::pair<Node*, Node*>, unsigned> elements_edges_on_boundary;
11603 this->get_element_edges_on_boundary(elements_edges_on_boundary);
11604
11605 // Now we have all the halo elements on all processors. Use the
11606 // edges shared by the halo elements to create the shared boundaries.
11607 this->create_polylines_from_halo_elements_helper(
11611 halo_element_pt,
11613 Shared_boundary_polyline_pt);
11614 }
11615
11616 //======================================================================
11617 /// Creates the halo elements on all processors
11618 /// Gets the halo elements on all processors, these elements are then used
11619 /// on the function that computes the shared boundaries among the processors
11620 //======================================================================
11621 template<class ELEMENT>
11623 const unsigned& nproc,
11626 std::map<Data*, std::set<unsigned>>& processors_associated_with_data,
11628 std::map<GeneralisedElement*, unsigned>& element_to_global_index,
11630 {
11631 const unsigned n_ele = backed_up_el_pt.size();
11632
11633 // Loop over all the processors
11634 for (unsigned iproc = 0; iproc < nproc; iproc++)
11635 {
11636 // Boolean to know which elements has been already added to the
11637 // halo scheme on "iproc" processor
11639
11640 // Loop over all backed up elements
11641 for (unsigned e = 0; e < n_ele; e++)
11642 {
11643 // Get element and its domain
11645 unsigned el_domain = element_domain[e];
11646
11647 // If element is NOT located on "iproc" processor then check if it is
11648 // halo with "el_domain" processor
11649 if (el_domain != iproc)
11650 {
11651 // If this current mesh has been told to keep all elements as halos,
11652 // OR the element itself knows that it must be kept then
11653 // keep it
11654 if ((this->Keep_all_elements_as_halos) ||
11656 {
11658 {
11659 // Add as halo element whose non-halo counterpart is
11660 // located on processor "el_domain"
11662 {
11664 already_added[el_domain][el_pt] = true;
11666 }
11667 }
11668 }
11669 // Otherwise: Is one of the nodes associated with other processor?
11670 else
11671 {
11672 // Can only have nodes if this is a finite element
11673 FiniteElement* finite_el_pt = dynamic_cast<FiniteElement*>(el_pt);
11674 if (finite_el_pt != 0)
11675 {
11676 unsigned n_node = finite_el_pt->nnode();
11677 for (unsigned n = 0; n < n_node; n++)
11678 {
11680
11681 // Keep element?
11682 std::set<unsigned>::iterator it =
11685 {
11686 // Add as root halo element whose non-halo counterpart is
11687 // located on processor "el_domain"
11689 {
11691 already_added[el_domain][el_pt] = true;
11693 }
11694 // Now break out of loop over nodes
11695 break;
11696 } // if (it!=processors_associated_with_data[nod_pt].end())
11697 } // for (n < n_node)
11698 } // if (finite_el_pt!=0)
11699 } // else (this->Keep_all_elements_as_halos)
11700 } // if (el_domain!=iproc)
11701 } // for (e < nele)
11702 } // for (iproc < nproc)
11703 }
11704
11705 //====================================================================
11706 // Get the element edges (pair of nodes, edges) that lie
11707 // on a boundary (used to mark shared boundaries that lie on
11708 // internal boundaries)
11709 //====================================================================
11710 template<class ELEMENT>
11712 std::map<std::pair<Node*, Node*>, unsigned>& element_edges_on_boundary)
11713 {
11714 // The number of original boundaries
11715 const unsigned nbound = this->nboundary();
11716 // Loop over the boundaries
11717 for (unsigned b = 0; b < nbound; b++)
11718 {
11719 // Keep track of the pair of nodes done
11720 std::map<std::pair<Node*, Node*>, bool> edge_done;
11721 // Get the number of elements on the boundary
11722 const unsigned nbound_ele = this->nboundary_element(b);
11723 for (unsigned e = 0; e < nbound_ele; e++)
11724 {
11725 // Get the boundary bulk element
11726 FiniteElement* bulk_ele_pt = this->boundary_element_pt(b, e);
11727 // Get the face index
11728 int face_index = this->face_index_at_boundary(b, e);
11729 // Create the face element
11731 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
11732 // Get the number of nodes on the face element
11733 const unsigned nnodes = face_ele_pt->nnode();
11734 // Get the first and last node
11737
11738 // Create the pair to store the nodes
11739 std::pair<Node*, Node*> edge =
11740 std::make_pair(first_node_pt, last_node_pt);
11741
11742 // Has the edge been included
11743 if (!edge_done[edge])
11744 {
11745 // Mark the edge as done
11746 edge_done[edge] = true;
11747
11748 // Create the reversed version and mark it as done too
11749 std::pair<Node*, Node*> inv_edge =
11750 std::make_pair(last_node_pt, first_node_pt);
11751
11752 // Mark the reversed edge as done
11753 edge_done[inv_edge] = true;
11754
11755 // Mark the edge to belong to boundary b
11757 } // if (!edge_done[edge])
11758
11759 // Free the memory allocated for the face element
11760 delete face_ele_pt;
11761 face_ele_pt = 0;
11762
11763 } // for (e < nbound_ele)
11764
11765 } // for (b < nbound)
11766 }
11767
11768 // ======================================================================
11769 // Creates polylines from the intersection of halo elements on
11770 // all processors. The new polylines define the shared boundaries in
11771 // the domain This method computes the polylines on ALL processors,
11772 // that is why the three dimensions in the structure
11773 // output_polylines_pt[iproc][ncurve][npolyline]
11774 // ======================================================================
11775 template<class ELEMENT>
11778 std::map<GeneralisedElement*, unsigned>& element_to_global_index,
11779 std::set<FiniteElement*>& element_in_processor_pt,
11781 std::map<std::pair<Node*, Node*>, unsigned>& elements_edges_on_boundary,
11783 {
11784 const unsigned nproc = this->communicator_pt()->nproc();
11785 const unsigned my_rank = this->communicator_pt()->my_rank();
11786
11787 // ---------------------------------------------------------------
11788 // Get the edges shared between each pair of processors
11789 // ---------------------------------------------------------------
11790
11791 // Storage for the edges (pair of nodes) shared between a pair of
11792 // processors
11794
11795 // Each edge is associated to two elements, a haloi (halo element
11796 // in processors i) and a haloj (halo element in processors j)
11798
11799 // Each edge is associated to two elements, a haloi and a haloj,
11800 // the edge was created from a given face from each element, the
11801 // haloi face is stored at [0], the haloj face is stored at [1]
11803
11804 // Store the possible internal boundary id associated to each edge
11805 // (-1 if there is no association). Some edges may overlap an
11806 // internal boundary (and only internal boundaries)
11807 Vector<Vector<Vector<int>>> edge_boundary(nproc);
11808
11809 // Mark those edges (pair of nodes overlapped by a shared boundary)
11810 std::map<std::pair<Node*, Node*>, bool> overlapped_edge;
11811
11812 // Resize the containers, they store info. for each pair of
11813 // processors
11814
11815 // First resize the global container
11816 Shared_boundaries_ids.resize(nproc);
11817 for (unsigned j = 0; j < nproc; j++)
11818 {
11819 edges[j].resize(nproc);
11820 edge_element_pt[j].resize(nproc);
11821 edge_element_face[j].resize(nproc);
11822 edge_boundary[j].resize(nproc);
11823
11824 // Resize the global container for shared boundaries ids
11825 Shared_boundaries_ids[j].resize(nproc);
11826
11827 } // for (j < nproc)
11828
11829 // Take the halo elements of processor "iproc" and compare their
11830 // edges with halo elements of other processors (except itself)
11831 for (unsigned iproc = 0; iproc < nproc; iproc++)
11832 {
11833 // Take the halo elements of processor iproc and compare with
11834 // other processors
11835 // Start from the iproc + 1,
11836 // 1) To avoid comparing with itself,
11837 // 2) To avoid generation of repeated boundaries
11838 for (unsigned jproc = iproc + 1; jproc < nproc; jproc++)
11839 {
11840 // **************************************************************
11841 // FIRST PART
11842 // 1) Get the halo elements of processor "iproc" with processor
11843 // "jproc"
11844 // 2) Get the halo elements of processor "jproc" with processor
11845 // "iproc"
11846 // 3) Compare their edges and those that match are the ones that
11847 // define the shared boundaries
11848 // **************************************************************
11849
11850 // Storage for halo elements
11853
11854 // Get the halo elements of "iproc" with "jproc"
11856
11857 // If there are halo elements then there are shared boundaries
11858 const unsigned nhalo_elements_iproc_with_jproc =
11860 // DEBP(nhalo_elements_iproc_with_jproc);
11862 {
11863 // Get the halo elements of "jproc" with "iproc"
11865
11866 // If there are halo elements then there are shared
11867 // boundaries
11868 const unsigned nhalo_elements_jproc_with_iproc =
11870// DEBP(nhalo_elements_jproc_with_iproc);
11871#ifdef PARANOID
11873 {
11874 // If there are halo elements of iproc with jproc there
11875 // MUST be halo elements on the other way round, not
11876 // necessary the same but at least one
11877 std::stringstream err;
11878 err << "There are no halo elements from processor (" << jproc
11879 << ") "
11880 << "with processor (" << iproc << ").\n"
11881 << "This is strange since there are halo elements from "
11882 << "processor (" << iproc << ") with processor (" << jproc
11883 << ").\n"
11884 << "Number of halo elements from (" << iproc << ") to ("
11885 << jproc << ") : (" << nhalo_elements_iproc_with_jproc << ")\n"
11886 << "Number of halo elements from (" << jproc << ") to ("
11887 << iproc << ") : (" << nhalo_elements_jproc_with_iproc << ")\n";
11888 throw OomphLibError(
11889 err.str(),
11890 "TriangleMesh::create_polylines_from_halo_elements_helper()",
11892 }
11893#endif
11894 // The edges are defined as pair of nodes
11896 unsigned halo_edges_counter_iproc = 0;
11898 unsigned halo_edges_counter_jproc = 0;
11899
11900 // Map to associate the edge with the element used to create it
11901 std::map<std::pair<Node*, Node*>, FiniteElement*>
11903
11904 // Map to associated the edge with the face number of the
11905 // element that created it
11906 std::map<std::pair<std::pair<Node*, Node*>, FiniteElement*>, int>
11908
11909 // Map to associate the edge with the element used to create it
11910 std::map<std::pair<Node*, Node*>, FiniteElement*>
11912
11913 // Map to associated the edge with the face number of the
11914 // element that created it
11915 std::map<std::pair<std::pair<Node*, Node*>, FiniteElement*>, int>
11917
11918 // **************************************************************
11919 // 1.1) Store the edges of the "iproc" halo elements
11920 // **************************************************************
11921 // Go throught halo elements on "iproc" processor
11922 for (unsigned ih = 0; ih < nhalo_elements_iproc_with_jproc; ih++)
11923 {
11924#ifdef PARANOID
11925 unsigned e =
11927 // Only work with halo elements inside the "jproc" processor
11928 if (element_domain[e] != jproc)
11929 {
11930 // There was a problem on the ihalo-jhalo classification
11931 std::stringstream err;
11932 err << "There was a problem on the ihalo-jhalo classification.\n"
11933 << "One of the elements, (the one with the (" << e << ")-th "
11934 << "index ) is not on the (" << jproc << ")-th processor\n"
11935 << "but it was stored as a halo element of processor ("
11936 << iproc << ") with processor (" << jproc << ").\n";
11937 throw OomphLibError(
11938 err.str(),
11939 "TriangleMesh::create_polylines_from_halo_elements_helper()",
11941 }
11942#endif
11943
11946
11947 if (el_pt == 0)
11948 {
11949 std::stringstream err;
11950 err << "The halo element (" << ih
11951 << ") could not be casted to the "
11952 << "FiniteElement type.\n";
11953 throw OomphLibError(
11954 err.str(),
11955 "TriangleMesh::create_polylines_from_halo_elements_helper()",
11957 }
11958
11959#ifdef PARANOID
11960 // Number of nodes on this element
11961 const unsigned n_nodes = el_pt->nnode();
11962
11963 // The number of nodes on every element should be at least
11964 // three since we are going to work with the cornes nodes,
11965 // the ones with index 0, 1 and 2
11966 if (n_nodes < 3)
11967 {
11968 std::stringstream err;
11969 err << "The number of nodes of the " << ih
11970 << "-th halo element is"
11971 << " (" << n_nodes << ").\nWe can not work with triangle "
11972 << "elements with less than three nodes\n";
11973 throw OomphLibError(
11974 err.str(),
11975 "TriangleMesh::create_polylines_from_halo_elements_helper()",
11977 }
11978#endif
11979
11980 // Get the corner nodes, the first three nodes
11984
11985 // Store the edges
11989
11993
11997
11998 // Store the info. of the element used to create these edges
11999 std::pair<Node*, Node*> edge1 =
12000 std::make_pair(first_node_pt, second_node_pt);
12002
12003 std::pair<Node*, Node*> edge2 =
12004 std::make_pair(second_node_pt, third_node_pt);
12006
12007 std::pair<Node*, Node*> edge3 =
12008 std::make_pair(third_node_pt, first_node_pt);
12010
12011 // Store the face index of the edge in the element
12012 std::pair<std::pair<Node*, Node*>, FiniteElement*> edge_ele1 =
12013 std::make_pair(edge1, el_pt);
12015
12016 std::pair<std::pair<Node*, Node*>, FiniteElement*> edge_ele2 =
12017 std::make_pair(edge2, el_pt);
12019
12020 std::pair<std::pair<Node*, Node*>, FiniteElement*> edge_ele3 =
12021 std::make_pair(edge3, el_pt);
12023
12024 } // for (ih < nhalo_elements_iproc_with_jproc)
12025
12026 // **************************************************************
12027 // 1.2) Store the edges of the "jproc" halo elements
12028 // **************************************************************
12029 // Go throught halo elements on "jproc" processor
12030 for (unsigned jh = 0; jh < nhalo_elements_jproc_with_iproc; jh++)
12031 {
12032#ifdef PARANOID
12033 unsigned e =
12035 // Only work with halo elements inside the "jproc" processor
12036 if (element_domain[e] != iproc)
12037 {
12038 // There was a problem on the jhalo-ihalo classification
12039 std::stringstream err;
12040 err << "There was a problem on the jhalo-ihalo classification.\n"
12041 << "One of the elements, (the one with the (" << e << ")-th "
12042 << "index ) is not on the (" << iproc << ")-th processor\n"
12043 << "but it was stored as a halo element of processor ("
12044 << jproc << ") with processor (" << iproc << ").\n";
12045 throw OomphLibError(
12046 err.str(),
12047 "TriangleMesh::create_polylines_from_halo_elements_helper()",
12049 }
12050#endif
12051
12054 if (el_pt == 0)
12055 {
12056 std::stringstream err;
12057 err << "The halo element (" << jh
12058 << ") could not be casted to the "
12059 << "FiniteElement type.\n";
12060 throw OomphLibError(
12061 err.str(),
12062 "TriangleMesh::create_polylines_from_halo_elements_helper()",
12064 }
12065
12066#ifdef PARANOID
12067 // Number of nodes on this element
12068 const unsigned n_nodes = el_pt->nnode();
12069
12070 // The number of nodes on every element should be at least
12071 // three since we are going to work with the cornes nodes,
12072 // the ones with index 0, 1 and 2
12073 if (n_nodes < 3)
12074 {
12075 std::stringstream err;
12076 err << "The number of nodes of the " << jh
12077 << "-th halo element is"
12078 << " (" << n_nodes << ").\nWe can not work with triangle "
12079 << "elements with less than three nodes\n";
12080 throw OomphLibError(
12081 err.str(),
12082 "TriangleMesh::create_polylines_from_halo_elements_helper()",
12084 }
12085#endif
12086
12087 // Get the nodes pointers
12091
12092 // Store the edges
12096
12100
12104
12105 // Store the info. of the element used to create these edges
12106 std::pair<Node*, Node*> edge1 =
12107 std::make_pair(first_node_pt, second_node_pt);
12109
12110 std::pair<Node*, Node*> edge2 =
12111 std::make_pair(second_node_pt, third_node_pt);
12113
12114 std::pair<Node*, Node*> edge3 =
12115 std::make_pair(third_node_pt, first_node_pt);
12117
12118 // Store the face index of the edge in the element
12119 std::pair<std::pair<Node*, Node*>, FiniteElement*> edge_ele1 =
12120 std::make_pair(edge1, el_pt);
12122
12123 std::pair<std::pair<Node*, Node*>, FiniteElement*> edge_ele2 =
12124 std::make_pair(edge2, el_pt);
12126
12127 std::pair<std::pair<Node*, Node*>, FiniteElement*> edge_ele3 =
12128 std::make_pair(edge3, el_pt);
12130
12131 } // for (jh < nhalo_elements_jproc_with_iproc)
12132
12133 // ***************************************************************
12134 // SECOND PART
12135 // 1) We already have the information of the edges on the iproc
12136 // halo and jproc halo elements
12137 // 2) Identify the shared edges to create the shared boundaries
12138 // (Only store the information but do not create the polyline)
12139 // ***************************************************************
12140
12141 // Get the number of edges from each processor
12142 unsigned nhalo_iedges = halo_edges_iproc.size();
12143 unsigned nhalo_jedges = halo_edges_jproc.size();
12144
12145 // Start comparing the edges to check which of those are
12146 // shared between the "ihalo_edge" and the "jhalo_edge"
12147 for (unsigned ihe = 0; ihe < nhalo_iedges; ihe += 2)
12148 {
12149 // Get the ihe-th edge (pair of nodes)
12153
12154 // Create the pair that defines the edge
12155 std::pair<Node*, Node*> tmp_edge =
12156 std::make_pair(ihalo_edge[0], ihalo_edge[1]);
12157
12158 // Check if the edge lies on a boundary (default values is
12159 // -1 for no association with an internal boundary)
12160 int edge_boundary_id = -1;
12161 {
12162 std::map<std::pair<Node*, Node*>, unsigned>::iterator it;
12164 // If the edges lie on a boundary then get the boundary id
12165 // on which the edges lie
12166 if (it != elements_edges_on_boundary.end())
12167 {
12168 // Assign the internal boundary id associated with the
12169 // edge
12170 edge_boundary_id = (*it).second;
12171 }
12172 else
12173 {
12174 // Look for the reversed version of the edge (the nodes
12175 // inverted)
12176 std::pair<Node*, Node*> rtmp_edge =
12177 std::make_pair(ihalo_edge[1], ihalo_edge[0]);
12179 if (it != elements_edges_on_boundary.end())
12180 {
12181 // Assign the internal boundary id associated with the
12182 // edge
12183 edge_boundary_id = (*it).second;
12184 }
12185 }
12186 }
12187
12188 // Go through the jhalo_edge and compare with the
12189 // ihalo_edge
12190 for (unsigned jhe = 0; jhe < nhalo_jedges; jhe += 2)
12191 {
12192 // Get the jhe-th edge (pair of nodes)
12196
12197 // Comparing pointer of nodes
12198 if (ihalo_edge[0] == jhalo_edge[0] &&
12199 ihalo_edge[1] == jhalo_edge[1])
12200 {
12201 // Create the edge (both nodes that make the edge)
12202 std::pair<Node*, Node*> new_edge =
12203 std::make_pair(ihalo_edge[0], ihalo_edge[1]);
12204
12205 // Get the elements involved in the creation of the
12206 // edge to check that there are elements associated to
12207 // the edge
12212
12213 // Verify that there is an element associated with it
12214 if (haloi_ele_pt == 0 || haloj_ele_pt == 0)
12215 {
12216 std::stringstream err;
12217 err << "There is no associated elements with the new "
12218 << "shared boundary. This is an storing problem,\n"
12219 << "possibly related with a memory leak problem!!!\n"
12220 << "The nodes that compound the edge are these:\n"
12221 << "On processor (" << iproc << "):\n"
12222 << "(" << ihalo_edge[0]->x(0) << ", "
12223 << ihalo_edge[0]->x(1) << ") and (" << ihalo_edge[1]->x(0)
12224 << ", " << ihalo_edge[1]->x(1) << ")\n\n"
12225 << "On processor (" << jproc << "):\n"
12226 << "(" << jhalo_edge[0]->x(0) << ", "
12227 << jhalo_edge[0]->x(1) << ") and (" << jhalo_edge[1]->x(0)
12228 << ", " << jhalo_edge[1]->x(1) << ")\n\n"
12229 << "The nodes coordinates should be the same!!!\n";
12230 throw OomphLibError(err.str(),
12231 "TriangleMesh::create_polylines_from_"
12232 "halo_elements_helper()",
12234 }
12235
12236 // Store the edge
12237 edges[iproc][jproc].push_back(new_edge);
12238
12239 // Is the edge overlapped by a shared boundary
12240 if (edge_boundary_id >= 0)
12241 {
12242 // Mark the edge as overlapped
12243 overlapped_edge[new_edge] = true;
12244
12245 // Also mark the reversed edge
12246 std::pair<Node*, Node*> rev_new_edge =
12247 std::make_pair(ihalo_edge[1], ihalo_edge[0]);
12248
12249 // Mark the edge as overlapped
12251
12252 } // if (edge_boundary_id >= 0)
12253
12254 // Store the internal boundary id (default -1)
12255 // associated to the edge
12256 edge_boundary[iproc][jproc].push_back(edge_boundary_id);
12257
12258 // Store the two elements associated with the edge
12260 tmp_elements_pt.push_back(haloi_ele_pt);
12261 tmp_elements_pt.push_back(haloj_ele_pt);
12262
12263 // Associate the edge with the elements that gave rise to it
12265
12266 // Get the face index on each element that gave rise to
12267 // the edge
12268
12269 // .. first create the pair (edge, finite_element)
12270 std::pair<std::pair<Node*, Node*>, FiniteElement*>
12272
12273 std::pair<std::pair<Node*, Node*>, FiniteElement*>
12275
12276 // Set default values to later check if values were
12277 // read from the map structure
12278 int face_index_haloi_ele = -1;
12281 int face_index_haloj_ele = -1;
12284 // Verify that there is an element associated with it
12285 if (face_index_haloi_ele == -1 || face_index_haloj_ele == -1)
12286 {
12287 std::stringstream err;
12288 err << "There is no associated face indexes to the"
12289 << "elements that gave\nrise to the shared edge\n"
12290 << "The nodes that compound the edge are these:\n"
12291 << "On processor (" << iproc << "):\n"
12292 << "(" << ihalo_edge[0]->x(0) << ", "
12293 << ihalo_edge[0]->x(1) << ") and (" << ihalo_edge[1]->x(0)
12294 << ", " << ihalo_edge[1]->x(1) << ")\n\n"
12295 << "On processor (" << jproc << "):\n"
12296 << "(" << jhalo_edge[0]->x(0) << ", "
12297 << jhalo_edge[0]->x(1) << ") and (" << jhalo_edge[1]->x(0)
12298 << ", " << jhalo_edge[1]->x(1) << ")\n\n"
12299 << "The nodes coordinates should be the same!!!\n";
12300 throw OomphLibError(err.str(),
12301 "TriangleMesh::create_polylines_from_"
12302 "halo_elements_helper()",
12304 } // if (face_index_haloi_ele == -1 ||
12305 // face_index_haloj_ele == -1)
12306
12307 // Get the face indexes from the map structure
12311 // Store the face indexes
12312 edge_element_face[iproc][jproc].push_back(
12314
12315 break; // break for (jhe < nhalo_jedges) since edge
12316 // found
12317
12318 } // if (ihalo_edge[0] == jhalo_edge[0] &&
12319 // ihalo_edge[1] == jhalo_edge[1])
12320 // Comparing nodes pointers
12321 else if (ihalo_edge[0] == jhalo_edge[1] &&
12322 ihalo_edge[1] == jhalo_edge[0])
12323 {
12324 // Create the edge (both nodes that make the edge)
12325 std::pair<Node*, Node*> new_edge =
12326 std::make_pair(ihalo_edge[0], ihalo_edge[1]);
12327
12328 // Get the elements involved in the creation of the
12329 // edge
12332
12334 // Create the edge (reversed, that is how it was
12335 // originally stored)
12336 std::pair<Node*, Node*> new_edge_reversed =
12337 std::make_pair(jhalo_edge[0], jhalo_edge[1]);
12339
12340 // Verify that there is an element associated with it
12341 if (haloi_ele_pt == 0 || haloj_ele_pt == 0)
12342 {
12343 std::stringstream err;
12344 err << "There is no associated elements with the new "
12345 << "shared boundary (reversed version). This is an "
12346 << "storing problem, possibly related with a memory "
12347 << "leak problem!!!\n"
12348 << "The nodes that compound the edge are these:\n"
12349 << "On processor (" << iproc << "):\n"
12350 << "(" << ihalo_edge[0]->x(0) << ", "
12351 << ihalo_edge[0]->x(1) << ") and (" << ihalo_edge[1]->x(0)
12352 << ", " << ihalo_edge[1]->x(1) << ")\n\n"
12353 << "On processor (" << jproc << "):\n"
12354 << "(" << jhalo_edge[0]->x(0) << ", "
12355 << jhalo_edge[0]->x(1) << ") and (" << jhalo_edge[1]->x(0)
12356 << ", " << jhalo_edge[1]->x(1) << ")\n\n"
12357 << "The nodes coordinates should be the same!!!\n";
12358 throw OomphLibError(err.str(),
12359 "TriangleMesh::create_polylines_from_"
12360 "halo_elements_helper()",
12362 }
12363
12364 // Store the edge
12365 edges[iproc][jproc].push_back(new_edge);
12366
12367 // Is the edge overlapped by a shared boundary
12368 if (edge_boundary_id >= 0)
12369 {
12370 // Mark the edge as overlapped
12371 overlapped_edge[new_edge] = true;
12372
12373 // Also mark the reversed edge
12374 std::pair<Node*, Node*> rev_new_edge =
12375 std::make_pair(ihalo_edge[1], ihalo_edge[0]);
12376
12377 // Mark the edge as overlapped
12379 } // if (edge_boundary_id >= 0)
12380
12381 // Store the internal boundary id (default -1)
12382 // associated to the edge
12383 edge_boundary[iproc][jproc].push_back(edge_boundary_id);
12384
12385 // Store the two elements associated with the edge
12387 tmp_elements_pt.push_back(haloi_ele_pt);
12388 tmp_elements_pt.push_back(haloj_ele_pt);
12389
12390 // Associate the edge with the elements that gave rise to it
12392
12393 // Get the face index on each element that gave rise to
12394 // the edge
12395
12396 // .. first create the pair (edge, finite_element)
12397 std::pair<std::pair<Node*, Node*>, FiniteElement*>
12399
12400 std::pair<std::pair<Node*, Node*>, FiniteElement*>
12403
12404 // Set default values to later check if values were
12405 // read from the map structure
12406 int face_index_haloi_ele = -1;
12409 int face_index_haloj_ele = -1;
12412 // Verify that there is an element associated with it
12413 if (face_index_haloi_ele == -1 || face_index_haloj_ele == -1)
12414 {
12415 std::stringstream err;
12416 err << "There is no associated face indexes to the"
12417 << "elements that gave\nrise to the shared edge\n"
12418 << "The nodes that compound the edge are these:\n"
12419 << "On processor (" << iproc << "):\n"
12420 << "(" << ihalo_edge[0]->x(0) << ", "
12421 << ihalo_edge[0]->x(1) << ") and (" << ihalo_edge[1]->x(0)
12422 << ", " << ihalo_edge[1]->x(1) << ")\n\n"
12423 << "On processor (" << jproc << "):\n"
12424 << "(" << jhalo_edge[0]->x(0) << ", "
12425 << jhalo_edge[0]->x(1) << ") and (" << jhalo_edge[1]->x(0)
12426 << ", " << jhalo_edge[1]->x(1) << ")\n\n"
12427 << "The nodes coordinates should be the same!!!\n";
12428 throw OomphLibError(err.str(),
12429 "TriangleMesh::create_polylines_from_"
12430 "halo_elements_helper()",
12432 } // if (face_index_haloi_ele == -1 ||
12433 // face_index_haloj_ele == -1)
12434
12435 // Get the face indexes from the map structure
12439 // Store the face indexes
12440 edge_element_face[iproc][jproc].push_back(
12442
12443 break; // break for (jhe < nhalo_jedges) since edge found
12444
12445 } // else if (ihalo_edge[0] == jhalo_edge[1] &&
12446 // ihalo_edge[1] == jhalo_edge[0])
12447
12448 } // for (jhe < nhaloj_edges)
12449
12450 } // for (ihe < nhaloi_edges)
12451
12452 } // if (nhalo_elements_iproc_with_jproc > 0)
12453
12454 } // for (jproc < nproc)
12455
12456 } // for (iproc < nproc)
12457
12458 // ------------------------------------------------------------------
12459 // Compute the degree of each node in the shared edges
12460 // ------------------------------------------------------------------
12461
12462 // Visit all the shared edges between each pair of processors,
12463 // visit the nodes of each edge and compute the degree of each node
12464
12465 // Store the degree (valency) of each node
12466 std::map<Node*, unsigned> global_shared_node_degree;
12467
12468#ifdef PARANOID
12469 // Map to check if an edge has been already visited
12470 std::map<std::pair<Node*, Node*>, bool> edge_done;
12471#endif // #ifdef PARANOID
12472 // Map to check if a node has been already visited
12473 std::map<Node*, bool> node_done;
12474
12475 // Loop over the processors and get the shared edged between each
12476 // pair of processors
12477 for (unsigned iproc = 0; iproc < nproc; iproc++)
12478 {
12479 // Start from iproc + 1 to avoid checking with itself (there is
12480 // no shared edges between the same processor), and to avoid
12481 // double counting the edges and nodes (the shared edges between
12482 // processor (iproc, jproc) are the same as those between
12483 // processor jproc, iproc)
12484 for (unsigned jproc = iproc + 1; jproc < nproc; jproc++)
12485 {
12486 // Get the number of edges shared between the pair of processors
12487 const unsigned nshd_edges = edges[iproc][jproc].size();
12488#ifdef PARANOID
12489 // There must be the same number of information on each of the
12490 // containers
12491
12492 // Get the number of edge elements
12493 const unsigned nedge_element = edge_element_pt[iproc][jproc].size();
12495 {
12496 std::stringstream error_message;
12497 error_message
12498 << "The number of shared edges between processor iproc and jproc\n"
12499 << "is different form the number of edge elements between the\n"
12500 << "pair of processors\n"
12501 << "iproc: (" << iproc << ")\n"
12502 << "jproc: (" << jproc << ")\n"
12503 << "# of shared edges: (" << nshd_edges << ")\n"
12504 << "# of edge elements: (" << nedge_element << ")\n\n";
12505 throw OomphLibError(
12506 error_message.str(),
12507 "TriangleMesh::create_polylines_from_halo_elements_helper()",
12509 }
12510
12511 // Get the number of edge element faces
12512 const unsigned nedge_element_face =
12515 {
12516 std::stringstream error_message;
12517 error_message
12518 << "The number of shared edges between processor iproc and jproc\n"
12519 << "is different form the number of edge element faces between "
12520 "the\n"
12521 << "pair of processors\n"
12522 << "iproc: (" << iproc << ")\n"
12523 << "jproc: (" << jproc << ")\n"
12524 << "# of shared edges: (" << nshd_edges << ")\n"
12525 << "# of edge element faces: (" << nedge_element_face << ")\n\n";
12526 throw OomphLibError(
12527 error_message.str(),
12528 "TriangleMesh::create_polylines_from_halo_elements_helper()",
12530 }
12531
12532 // Get the number of edge boundaries
12533 const unsigned nedge_boundary = edge_boundary[iproc][jproc].size();
12535 {
12536 std::stringstream error_message;
12537 error_message
12538 << "The number of shared edges between processor iproc and jproc\n"
12539 << "is different form the number of edge boundaries ids between "
12540 "the\n"
12541 << "pair of processors\n"
12542 << "iproc: (" << iproc << ")\n"
12543 << "jproc: (" << jproc << ")\n"
12544 << "# of shared edges: (" << nshd_edges << ")\n"
12545 << "# of edge boundaries ids: (" << nedge_boundary << ")\n\n";
12546 throw OomphLibError(
12547 error_message.str(),
12548 "TriangleMesh::create_polylines_from_halo_elements_helper()",
12550 }
12551
12552#endif // #ifdef PARANOID
12553
12554 // Loop over the shared edges between (iproc, jproc) processors
12555 for (unsigned se = 0; se < nshd_edges; se++)
12556 {
12557 // Get the edge
12558 std::pair<Node*, Node*> edge = edges[iproc][jproc][se];
12559#ifdef PARANOID
12560 // Check that the edge has not been previously visited
12561 if (edge_done[edge])
12562 {
12563 std::stringstream error_message;
12564 error_message
12565 << "The shared edge between processor iproc and processor\n"
12566 << "jproc has been already visited, this is weird since the\n"
12567 << "edge should not be shared by other pair of processors\n"
12568 << "iproc: (" << iproc << ")\n"
12569 << "jproc: (" << jproc << ")\n"
12570 << "First node of edge: (" << edge.first->x(0) << ", "
12571 << edge.first->x(1) << ")\n"
12572 << "Second node of edge: (" << edge.second->x(0) << ", "
12573 << edge.second->x(1) << ")\n"
12574 << "Associated edge boundary id: ("
12575 << edge_boundary[iproc][jproc][se] << ")\n\n";
12576 throw OomphLibError(
12577 error_message.str(),
12578 "TriangleMesh::create_polylines_from_halo_elements_helper()",
12580 }
12581
12582 // Mark the edge as done
12583 edge_done[edge] = true;
12584 // Create the reversed version and include it too
12585 std::pair<Node*, Node*> rev_edge =
12586 std::make_pair(edge.second, edge.first);
12587 // Mark reversed edge as done
12588 edge_done[rev_edge] = true;
12589#endif // #ifdef PARANOID
12590
12591 // Get each of the nodes that conform the edge
12592 Node* left_node_pt = edge.first;
12593 Node* right_node_pt = edge.second;
12594
12595 // Check if the left node has been already done
12596 if (!node_done[left_node_pt])
12597 {
12598 // Set the degree of the node to once since this is the
12599 // first time it has been found
12601
12602 } // if (!done_node[left_node_pt])
12603 else
12604 {
12605 // Increase the degree of the node
12607 }
12608
12609 // Check if the right node has been already done
12611 {
12612 // Set the degree of the node to once since this is the
12613 // first time it has been found
12615 } // if (!done_node[right_node_pt])
12616 else
12617 {
12618 // Increase the degree of the node
12620 }
12621
12622 } // for (se < nshd_edges)
12623
12624 } // for (jproc < nproc)
12625
12626 } // for (iproc < nproc)
12627
12628 // -----------------------------------------------------------------
12629 // Identify those nodes living on edges of original boundaries not
12630 // overlapped by a shared boundary
12631
12632 // Mark the nodes on original boundaries not overlapped by shared
12633 // boundaries
12634 std::map<unsigned, std::map<Node*, bool>>
12636
12637 // Loop over the edges of the original boundaries
12638 for (std::map<std::pair<Node*, Node*>, unsigned>::iterator it_map =
12641 it_map++)
12642 {
12643 // Get the edge
12644 std::pair<Node*, Node*> edge_pair = (*it_map).first;
12645
12646 // Is the edge overlaped by a shared boundary
12648 {
12649 // Mark the nodes of the edge as being on an edge not overlaped
12650 // by a shared boundary on the boundary the edge is
12651 unsigned b = (*it_map).second;
12652
12653 // Get the left node
12654 Node* left_node_pt = edge_pair.first;
12656
12657 // Get the right node
12658 Node* right_node_pt = edge_pair.second;
12660
12661 } // if (!overlapped_edge[edge_pair])
12662
12663 } // Loop over edges to mark those nodes on overlaped edge by
12664 // shared boundaries
12665
12666 // ------------------------------------------------------------------
12667 // Now create the shared polylines but including the degree of the
12668 // nodes as a nw stop condition for adding more edges to the side
12669 // or a root edge
12670 // ------------------------------------------------------------------
12671
12672 // Storage for new created polylines with "each processor", non
12673 // sorted (shared polylines of the current processor only)
12675
12676 // Map that associates the shared boundary id with the list of
12677 // nodes that create it (shared boundary of the current processor
12678 // only)
12679 std::map<unsigned, std::list<Node*>> shared_bnd_id_to_sorted_list_node_pt;
12680
12681 // Get maximum user boundary id and set the initial shared boundary
12682 // id
12683 unsigned shared_boundary_id_start = this->nboundary();
12684 Initial_shared_boundary_id = shared_boundary_id_start;
12685
12686 // Aqui
12687
12688 // Loop over the processors and get the shared edged between each
12689 // pair of processors
12690 for (unsigned iproc = 0; iproc < nproc; iproc++)
12691 {
12692 // Start from iproc + 1 to avoid checking with itself (there is
12693 // no shared edges between the same processor), and to avoid
12694 // double counting the edges and nodes (the shared edges between
12695 // processor (iproc, jproc) are the same as those between
12696 // processor jproc, iproc)
12697 for (unsigned jproc = iproc + 1; jproc < nproc; jproc++)
12698 {
12699 // *************************************************************
12700 // THIRD PART
12701 // 1) Sort the edges (make them contiguous) so that they can
12702 // be used as the vertex coordinates that define a shared
12703 // boundary (polyline)
12704 // *************************************************************
12705 unsigned npolylines_counter = 0;
12706 const unsigned nedges = edges[iproc][jproc].size();
12707
12708 // -----------------------------------------------------------
12709 // Compute all the SHARED POLYLINES
12710 // -----------------------------------------------------------
12711 // The number of sorted edges
12712 unsigned nsorted_edges = 0;
12713
12714 // Keep track of the already done edges
12715 std::map<std::pair<Node*, Node*>, bool> edge_done;
12716
12717 // Loop over all the edges to create all the polylines with
12718 // the current processors involved
12719 while (nsorted_edges < nedges)
12720 {
12721 // Temporaly storage for the elements associated to the
12722 // sorted edges
12723 std::list<FiniteElement*> tmp_boundary_element_pt;
12724 // Temporly storage for the face indexes on the element
12725 // that created the given edge
12726 std::list<int> tmp_face_index_element;
12727 // Get an initial pair of nodes to create an edge
12728 std::pair<Node*, Node*> edge;
12729#ifdef PARANOID
12730 bool found_initial_edge = false;
12731#endif
12732 int root_edge_bound_id = -1;
12733 unsigned iedge = 0;
12734 for (iedge = 0; iedge < nedges; iedge++)
12735 {
12736 edge = edges[iproc][jproc][iedge];
12737 // If not done then take it as initial edge
12738 if (!edge_done[edge])
12739 {
12740 // Get the boundary id that the edge may be overlapping
12741 root_edge_bound_id = edge_boundary[iproc][jproc][iedge];
12742#ifdef PARANOID
12743 found_initial_edge = true;
12744#endif
12745 nsorted_edges++;
12746 iedge++;
12747 break;
12748 } // if (!edge_done[edge])
12749 } // for (iedge < nedges)
12750
12751#ifdef PARANOID
12752 if (!found_initial_edge)
12753 {
12754 std::ostringstream error_message;
12755 error_message
12756 << "All the edge are already done, but the number of done\n"
12757 << "edges (" << nsorted_edges
12758 << ") is still less than the total\n"
12759 << "number of edges (" << nedges << ").\n";
12760 // << "----- Possible memory leak -----\n";
12761 throw OomphLibError(
12762 error_message.str(),
12763 "TriangleMesh::create_polylines_from_halo_elements_helper()",
12765 }
12766#endif
12767
12768 // Storing for the sorting nodes extracted from the
12769 // edges. The sorted nodes are used to create a polyline
12770 std::list<Node*> sorted_nodes;
12771 sorted_nodes.clear();
12772
12773 // The initial and final nodes of the list
12774 Node* first_node_pt = edge.first;
12775 Node* last_node_pt = edge.second;
12776
12777 // Push back on the list the new edge (nodes)
12778 sorted_nodes.push_back(first_node_pt);
12779 sorted_nodes.push_back(last_node_pt);
12780
12781 // Get the elements associated to the edge and store them
12782 // in the temporaly boundary elements storage
12783 tmp_boundary_element_pt.push_back(
12784 edge_element_pt[iproc][jproc][iedge - 1][0]);
12785 tmp_boundary_element_pt.push_back(
12786 edge_element_pt[iproc][jproc][iedge - 1][1]);
12787
12788 // ... then get the face index of the element from where
12789 // the edge came from
12790 tmp_face_index_element.push_back(
12792 tmp_face_index_element.push_back(
12794
12795 // Mark edge as done
12796 edge_done[edge] = true;
12797
12798 // Continue iterating if a new node (that creates a new
12799 // edge) is added to the list, we have just added two nodes
12800 // (the first and last of the root edge)
12801 bool node_added = true;
12802
12803 // Flags to indicate at which end the node was added (left
12804 // or right)
12805 bool node_added_to_the_left = true;
12806 bool node_added_to_the_right = true;
12807
12808 // The nodes that create a shared boundary are obtained by
12809 // connecting the edges shared by the halo and haloed
12810 // elements. These edges are connected to left or right of
12811 // the shared boundary. Every time a new edge is added to
12812 // the left (or right), the most left (or right) node is
12813 // searched in the list of nodes of previous shared
12814 // boundaries, if the node is found then it is said to be
12815 // shared with another boundary and a connection to that
12816 // boundary needs to be specified. We stop adding edges
12817 // (and nodes) to the side where that nodes was found to be
12818 // shared. Note that the intersection (shared node) may be
12819 // with the same shared boundary
12820
12821 // Flag to indicate a node was found to be shared with
12822 // another boundary at the left end (most left node) of the
12823 // shared boundary
12824 bool connection_to_the_left = false;
12825
12826 // Flag to indicate a node was found to be shared with
12827 // another boundary at the right end (most right node) of
12828 // the shared boundary
12829 bool connection_to_the_right = false;
12830
12831 // Flag to stop the adding of edges (and nodes) to the
12832 // current shared boundary
12834
12835 // Store the boundary ids of the polylines to connect (only
12836 // used when the polyline was found to have a connection)
12837 // -1: Indicates no connection
12838 // -2: Indicates connection with itself
12839 // Any other value: Boundary id to connect
12842
12843 // Get the degree of the first node
12844 const unsigned first_node_degree =
12846
12847 // Check if the nodes of the root edge have connections
12848 // ... to the left
12849 bound_id_connection_to_the_left = check_connections_of_polyline_nodes(
12858
12859 // If there is a connection then set the
12860 // corresponding flag
12861 // (-1): No connection
12862 // (-2): Connection with itself
12863 // (-3): No connection, stop adding nodes
12864 // (other value): Boundary id
12866 {
12868 } // if (bound_id_connection_to_the_left != -1)
12869
12870 // Get the degree of the last node
12871 const unsigned last_node_degree =
12873
12874 // Check if the nodes of the root edge have connections
12875 // ... to the right
12877 check_connections_of_polyline_nodes(
12885 last_node_pt);
12886
12887 // If there is a connection then set the
12888 // corresponding flag
12889 // (-1): No connection
12890 // (-2): Connection with itself
12891 // (other value): Boundary id
12893 {
12895 } // if (bound_id_connection_to_the_right != -1)
12896
12897 // If the current shared boundary has connections at both
12898 // ends then stop the adding of nodes
12900 {
12902 }
12903
12904 // Continue searching for more edges if
12905 // 1) A new node was added at the left or right of the list
12906 // 2) There are more edges to possible add
12907 // 3) The added node is not part of any other previous
12908 // shared polyline
12909 while (node_added && (nsorted_edges < nedges) &&
12911 {
12912 // Start from the next edge since we have already added
12913 // the previous one as the initial edge (any previous
12914 // edge had to be added to previous polylines)
12915 for (unsigned iiedge = iedge; iiedge < nedges; iiedge++)
12916 {
12917 // Reset the flags for added nodes, to the left and right
12918 node_added = false;
12919 node_added_to_the_left = false;
12921 // Get the current edge
12923 const int edge_bound_id = edge_boundary[iproc][jproc][iiedge];
12924
12925 // We need to ensure to connect with edges that share
12926 // the same bound id or with those that has no boundary
12927 // id associated (the default -1 value), may apply
12928 // exclusively to internal boundaries
12930 {
12931 // Get each individual node
12932 Node* left_node_pt = edge.first;
12933 Node* right_node_pt = edge.second;
12934
12935 // Pointer to the new added node
12937
12938 // Is the node to be added to the left?
12940 {
12941 // Push front the new node
12942 sorted_nodes.push_front(right_node_pt);
12943 // Update the new added node and the first node
12945 // Set the node added flag to true
12946 node_added = true;
12947 // Indicate the node was added to the left
12949 }
12950 // Is the node to be added to the right?
12951 else if (left_node_pt == last_node_pt &&
12953 {
12954 // Push back the new node
12955 sorted_nodes.push_back(right_node_pt);
12956 // Update the new added node and the last node
12958 // Set the node added flag to true
12959 node_added = true;
12960 // Indicate the node was added to the right
12962 }
12963 // Is the node to be added to the left?
12964 else if (right_node_pt == first_node_pt &&
12966 {
12967 // Push front the new node
12968 sorted_nodes.push_front(left_node_pt);
12969 // Update the new added node and the first node
12971 // Set the node added flag to true
12972 node_added = true;
12973 // Indicate the node was added to the left
12975 }
12976 // Is the node to be added to the right?
12977 else if (right_node_pt == last_node_pt &&
12979 {
12980 // Push back the new node
12981 sorted_nodes.push_back(left_node_pt);
12982 // Update the new added node and the last node
12984 // Set the node added flag to true
12985 node_added = true;
12986 // Indicate the node was added to the right
12988 }
12989
12990 // If we added a new node then we need to check if
12991 // that node has been already added in other shared
12992 // boundaries (which may define a connection)
12993 if (node_added)
12994 {
12995 // Mark as done only if one of its nodes has been
12996 // added to the list
12997 edge_done[edge] = true;
12998 nsorted_edges++;
12999
13000 // Get the degree of the added node
13001 const unsigned added_node_degree =
13003
13005 {
13006 // Add the bulk elements
13007 tmp_boundary_element_pt.push_front(
13009 tmp_boundary_element_pt.push_front(
13011 // Add the face elements
13012 tmp_face_index_element.push_front(
13014 tmp_face_index_element.push_front(
13016 }
13017
13019 {
13020 // Add the bulk elements
13021 tmp_boundary_element_pt.push_back(
13023 tmp_boundary_element_pt.push_back(
13025 // Add the face elements
13026 tmp_face_index_element.push_back(
13028 tmp_face_index_element.push_back(
13030 }
13031
13032 // Based on which side the node was added, look for
13033 // connections on that side
13034
13035 // Verify for connections to the left (we need to
13036 // check for the connection variable too, since
13037 // after a connection has been done we no longer
13038 // need to verify for this condition)
13040 {
13041 // Check for connection
13043 check_connections_of_polyline_nodes(
13052
13053 // If there is a connection then set the
13054 // corresponding flag
13055 // (-1): No connection
13056 // (-2): Connection with itself
13057 // (other value): Boundary id
13059 {
13061 } // if (bound_id_connection_to_the_left != -1)
13062
13063 } // if (node_added_to_the_left &&
13064 // !connection_to_the_left)
13065
13066 // Verify for connections to the right (we need to
13067 // check for the connection variable too, since
13068 // after a connection has been done we no longer
13069 // need to verify for this condition)
13071 {
13072 // Check for connection
13074 check_connections_of_polyline_nodes(
13083
13084 // If there is a connection then set the
13085 // corresponding flag
13086 // (-1): No connection
13087 // (-2): Connection with itself
13088 // (other value): Boundary id
13090 {
13092 } // if (bound_id_connection_to_the_right != -1)
13093
13094 } // if (node_added_to_the_right &&
13095 // !connection_to_the_right)
13096
13097 // If the current shared boundary has connections
13098 // at both ends then stop the adding of nodes
13100 {
13102 }
13103
13104 // Break the for and re-start to look more edges to
13105 // the left or right
13106 break;
13107
13108 } // if (node_added)
13109
13110 } // if (!edge_done[edge])
13111 } // for (iiedge < nedges)
13112
13113 } // while(node_added && (nsorted_edges < nedges)
13114 // && !current_polyline_has_connections_at_both_ends)
13115
13116 // ------------------------------------------------------------
13117 // If the sorted nodes of the shared polyline create a loop
13118 // it is necessary to break it by creating as many
13119 // polylines as required
13120
13121 // Change the list to a vector representation of the
13122 // boundary elements and the face indexes
13123
13124 // Get the number of boundary elements
13125 const unsigned n_bnd_ele = tmp_boundary_element_pt.size();
13126
13127 // Storage for the boundary elements and face indexes
13130 // Helper counter
13131 unsigned help_counter = 0;
13132 // Fill the data structures
13133 for (std::list<FiniteElement*>::iterator it_bnd_ele =
13136 it_bnd_ele++)
13137 {
13138 tmp_bnd_ele_pt[help_counter++] = (*it_bnd_ele);
13139 }
13140
13141 // Restart counter
13142 help_counter = 0;
13143 for (std::list<int>::iterator it_face_idx =
13144 tmp_face_index_element.begin();
13146 it_face_idx++)
13147 {
13148 tmp_face_idx_ele[help_counter++] = (*it_face_idx);
13149 }
13150
13151 // Store the nodes for the new shared polylines without
13152 // loops
13154 // Store the boundary elements of the shared polyline
13155 // without loops
13157 // Face indexes of the boundary elements without loops
13159 // Connection flags (to the left) of the shared boundaries
13160 // without loops
13162 // Connection flags (to the right) of the shared boundaries
13163 // without loops
13165
13166 // Break any possible loop created by the shared polyline
13167 break_loops_on_shared_polyline_helper(
13179
13180 // Get the number of final sorted nodes
13182
13183 // Loop over the list of final sorted nodes
13184 for (unsigned i = 0; i < n_final_sorted_nodes; i++)
13185 {
13186 // --------------------------------------------------------
13187 // Associate the list of sorted nodes with the boundary id
13188 // of the shared boundary that is going to be crated
13191
13192 // Create the shared polyline and fill the data
13193 // structured associated to it
13194 create_shared_polyline(my_rank,
13196 iproc,
13197 jproc,
13205
13206 // Increase the register for the number of created shared
13207 // polylines
13209
13210 // Increase the boundary id (the one that will be used by
13211 // the next shared boundary)
13213
13214 } // for (i < n_final_sorted_nodes)
13215
13216 } // while(nsorted_edges < nedges);
13217
13218 } // for (jproc < nproc)
13219
13220 // We already have all the shared polylines (shared boundaries)
13221 // of processor iproc with processor jproc. Now we sort them so
13222 // that they be contiguous and can create polygons.
13223
13224 // If there are polylines to be sorted then sort them
13225 if (unsorted_polylines_pt[iproc].size() > 0)
13226 {
13227 // Now that we have all the new unsorted polylines on "iproc"
13228 // processor it is time to sort them so they be all contiguous
13229 sort_polylines_helper(unsorted_polylines_pt[iproc],
13231 }
13232
13233#ifdef PARANOID
13234 const unsigned nunsorted_polylines_iproc =
13236
13237 // Verify that all the polylines have been sorted
13238 unsigned tmp_ntotal_polylines = 0;
13239 // Count the total number of sorted polylines
13240 for (unsigned ii = 0; ii < output_polylines_pt[iproc].size(); ii++)
13241 {
13243 }
13245 {
13246 std::ostringstream error_message;
13247 error_message << " The total number of unsorted polylines ("
13249 << ") in common with\nprocessor (" << iproc
13250 << ") is different from the total number of sorted "
13251 << "polylines (" << tmp_ntotal_polylines
13252 << ") with\nthe same "
13253 << "proessor\n";
13254 throw OomphLibError(error_message.str(),
13257 } // if (tmp_ntotal_polylines != nunsorted_polylines_iproc)
13258#endif
13259
13260 } // for (iproc < nproc)
13261
13262 // Establish the last used boundary id
13263 this->Final_shared_boundary_id = shared_boundary_id_start;
13264 }
13265
13266 // ======================================================================
13267 // Break any possible loop created by the sorted list of nodes
13268 // that is used to create a new shared polyline
13269 // ======================================================================
13270 template<class ELEMENT>
13272 const unsigned& initial_shd_bnd_id,
13273 std::list<Node*>& input_nodes,
13276 const int& input_connect_to_the_left,
13277 const int& input_connect_to_the_right,
13278 Vector<std::list<Node*>>& output_sorted_nodes_pt,
13283 {
13284 // Get the left and right node of the current list of sorted nodes
13285 Node* left_node_pt = input_nodes.front();
13286 Node* right_node_pt = input_nodes.back();
13287
13288 // Temporary storage for list of nodes, boundary elements and face
13289 // element's indexes
13293
13294 // Iterator for the list of input nodes
13295 std::list<Node*>::iterator it = input_nodes.begin();
13296
13297 // Counter
13298 unsigned counter = 0;
13299
13300 // Loop while not all nodes have been done
13301 while (it != input_nodes.end())
13302 {
13303 // Check if the current node is the final one
13304 it++;
13305 // Is the current node the final node?
13306 if (it == input_nodes.end())
13307 {
13308 // Break, add no more nodes
13309 break;
13310 }
13311 else
13312 {
13313 // Restore the iterator
13314 it--;
13315 }
13316
13317 // Get a list of nonrepeated nodes
13318 std::list<Node*> sub_nodes;
13319 // The temporary vector of boundary elements associated with the
13320 // nodes
13322 // The temporary vector of face indexes associated with the
13323 // boundary elements
13325
13326 // Add the current node to the list
13327 sub_nodes.push_back(*it);
13328
13329 // Add nodes until found a repeated node (the left or right
13330 // node) or until reaching the end of the list of nodes
13331 do
13332 {
13333 // Go to the next node
13334 ++it;
13335
13336 // Add the new node
13337 sub_nodes.push_back((*it));
13338
13339 // Add the boundary elements
13342
13343 // Add the face indexes
13346
13347 // Increase the counter
13348 counter += 2;
13349
13350 // Continue adding until reaching a repeated node or the end
13351 // of the list of nodes
13352 } while ((*it) != left_node_pt && (*it) != right_node_pt &&
13353 it != input_nodes.end());
13354
13355 // Add the sub-set of nodes to the temporary storage
13356 tmp_sub_nodes.push_back(sub_nodes);
13357 // Add the face elements to the temporary storage
13359 // Add the face indexes to the temporary storage
13361
13362 } // while((*it) != input_nodes.end())
13363
13364 // --------------------------------------------------
13365 // Now create as many shared boundaries as required
13366
13367 // Get the number of sub-list of nodes created
13368 const unsigned n_sub_list = tmp_sub_nodes.size();
13369
13370#ifdef PARANOID
13371 if (n_sub_list > 3)
13372 {
13373 std::stringstream error_message;
13374 error_message
13375 << "The number of sub-list of nodes created from the shared\n"
13376 << "polyline with loops was (" << n_sub_list << ").\n"
13377 << "We can only handle up to three sub-list of nodes\n";
13378 throw OomphLibError(
13379 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
13380 }
13381#endif
13382
13383 // If there is only one list it may be because there are no loops or
13384 // there is only one loop (a circle)
13385 if (n_sub_list == 1 && (left_node_pt != right_node_pt))
13386 {
13387 // There are no loops, return just after filling the data
13388 // structures
13389
13390 // This is the base case used most of the times
13391
13392 // Set the vector of lists of nodes
13394 // Set the vector of boundary elements
13396 // Set the vector of face indexes
13398
13399 // Set the connection flags, change them by the proper connection
13400 // flag
13401
13402#ifdef PARANOID
13403 if (input_connect_to_the_left == -2)
13404 {
13405 std::stringstream error_message;
13406 error_message
13407 << "The connection flag to the left (" << input_connect_to_the_left
13408 << ") indicates a connection\n"
13409 << "with the same polyline.\n However, only one sub-polyline was "
13410 << "found and no loop\nwas identified\n\n";
13411 throw OomphLibError(error_message.str(),
13414 }
13415#endif
13416
13417 // The left connection flag
13418 if (input_connect_to_the_left == -3)
13419 {
13420 output_connect_to_the_left.push_back(-1);
13421 }
13422 else
13423 {
13425 }
13426
13427#ifdef PARANOID
13429 {
13430 std::stringstream error_message;
13431 error_message
13432 << "The connection flag to the right (" << input_connect_to_the_right
13433 << ") indicates a connection\n"
13434 << "with the same polyline.\n However, only one sub-polyline was "
13435 << "found and no loop\nwas identified\n\n";
13436 throw OomphLibError(
13437 error_message.str(),
13438 "TriangleMesh::break_loops_on_shared_polyline_helper()",
13440 }
13441#endif
13442
13443 // The right connection flag
13445 {
13446 output_connect_to_the_right.push_back(-1);
13447 }
13448 else
13449 {
13451 }
13452
13453 // Return inmediately
13454 return;
13455 }
13456
13457 // The temporary storage for the shared boundary id
13459
13460 // -----------------------------------------------------------------
13461 // Check all the sub-list of nodes and create two shared boundaries
13462 // from those that make a loop (circle)
13463
13464 // -----------------------------------------------------------
13465 // Get the left and right node of the first sub-list of nodes
13466 Node* left_sub_node_pt = tmp_sub_nodes[0].front();
13468
13469 // Check if the sub-list of nodes creates a loop (circle)
13471 {
13472 // We need to create two shared polylines and therefore increase
13473 // the shared boundary id by two
13474
13475 // The first and second half of nodes
13476 std::list<Node*> first_half_node_pt;
13477 std::list<Node*> second_half_node_pt;
13478 // The first and second half of boundary elements
13481 // The first and second half of face indexes
13484
13485 // Get the number of sub-nodes in the sub-list of nodes
13486 const unsigned n_sub_nodes = tmp_sub_nodes[0].size();
13487
13488 // The number of sub-nodes for the first half of the shared
13489 // boundary
13490 const unsigned n_sub_nodes_half =
13491 static_cast<unsigned>(n_sub_nodes / 2.0);
13492
13493 // Copy as many sub-nodes for the first half of the sub-polyline
13494
13495 // Iterator to loop over the nodes
13496 std::list<Node*>::iterator it_sub = tmp_sub_nodes[0].begin();
13497
13498 // Add the first node
13499 first_half_node_pt.push_back(*it_sub);
13500
13501 // Skip the first node
13502 it_sub++;
13503
13504 // Counter
13505 unsigned counter_nodes = 0;
13506 unsigned counter2 = 0;
13507
13508 // Loop to copy the nodes
13509 for (; it_sub != tmp_sub_nodes[0].end(); it_sub++)
13510 {
13511 // Add the sub-node to the first half
13512 first_half_node_pt.push_back(*it_sub);
13513
13514 // Add the boundary elements of the first half
13517 // Add the face indexes of the first half
13520
13521 // Increase the counter of added nodes
13522 counter_nodes++;
13523
13524 // Increase the other counter
13525 counter2 += 2;
13526
13528 {
13529 // Stop adding to the first half of nodes
13530 break;
13531 }
13532
13533 } // Copy the first half of nodes
13534
13535 // The second half
13536
13537 // Add the first node of the second half
13538 second_half_node_pt.push_back(*it_sub);
13539
13540 // Skip the first node of the second half
13541 it_sub++;
13542
13543 // Loop to copy the nodes
13544 for (; it_sub != tmp_sub_nodes[0].end(); it_sub++)
13545 {
13546 // Add the sub-node to the first half
13547 second_half_node_pt.push_back(*it_sub);
13548
13549 // Add the boundary elements of the first half
13552 // Add the face indexes of the first half
13555
13556 // Increase the other counter
13557 counter2 += 2;
13558
13559 } // Copy the second half of nodes
13560
13561 // Add the sub-list of nodes to the vector of lists of nodes
13564 // Add the sub-vector of elements to the vector of boundary
13565 // elements
13568 // Add the sub-vector of face indexes to the vector of face
13569 // indexes
13572
13573 // Set the connection flags, change them by the proper connection
13574 // flag
13575
13576 // ----------------------------------------------------------------
13577 // Connections flags for the first half
13578
13579 // The left connection flag
13580
13581 // Connected with nothing but required to stop adding nodes
13582 if (input_connect_to_the_left == -3)
13583 {
13584 // Set connected to nothing
13585 output_connect_to_the_left.push_back(-1);
13586 }
13587 // Connected with itself
13588 else if (input_connect_to_the_left == -2)
13589 {
13590 // Set connected to nothing, this is the base node
13591 output_connect_to_the_left.push_back(-1);
13592 }
13593 else
13594 {
13595 // Any other value keep it
13597 }
13598
13599 // The right connection flag
13600
13601 // Set connected to nothing, this is the base node
13602 output_connect_to_the_right.push_back(-1);
13603
13604 // Increase the shared boundary id
13606
13607 // ----------------------------------------------------------------
13608 // Connections flags for the second half
13609
13610 // The left connection flag
13611
13612 // Set connected to the previous boundary
13614
13615 // The right connection flag
13616
13617 // Are we in the last sub-list of nodes, if that is the case we
13618 // need to respect the flag assigned to the right
13619 if (n_sub_list == 1)
13620 {
13622 {
13623 // Set connected to the previous shared boundary id
13625 }
13626 else if (input_connect_to_the_right == -2)
13627 {
13628 // Set connected to the previous shared boundary id
13630 }
13631 else if (input_connect_to_the_right == -1)
13632 {
13633 // Set connected to the previous shared boundary id
13635 }
13636 else
13637 {
13638 // Any other value keep it
13640 }
13641 } // if (n_sub_list == 1)
13642 else
13643 {
13644 // Set connected to the previous shared boundary id
13646 }
13647
13648 // Increase the shared boundary id
13650
13651 } // if (left_sub_node_pt == right_sub_node_pt)
13652 else
13653 {
13654 // No need to create two boundaries, create only one with the
13655 // sub-list of nodes
13656
13657 // Add the sub-list of nodes to the vector of lists of nodes
13659 // Add the sub-vector of elements to the vector of boundary
13660 // elements
13662 // Add the sub-vector of face indexes to the vector of face
13663 // indexes
13665
13666 // Set the connection flags, change them by the proper connection
13667 // flag
13668
13669 // The left connection flag
13670
13671 // Connected with nothing but required to stop adding nodes
13672 if (input_connect_to_the_left == -3)
13673 {
13674 // Set to connected to nothing
13675 output_connect_to_the_left.push_back(-1);
13676 }
13677 // Connected with itself
13678 else if (input_connect_to_the_left == -2)
13679 {
13680 // Set connected to the next shared polyline id
13682 }
13683 else
13684 {
13685 // Any other value keep it
13687 }
13688
13689 // The right connection flag
13690
13691 // Set connected to the next shared polyline id
13693
13694 // Increase the shared boundary id by one
13696
13697 } // else if (left_sub_node_pt == right_sub_node_pt)
13698
13699 // At least two sub-list of nodes were created
13700 if (n_sub_list > 1)
13701 {
13702 // ------------------------------------------------------------
13703 // Get the left and right node of the second sub-list of nodes
13704 left_sub_node_pt = tmp_sub_nodes[1].front();
13705 right_sub_node_pt = tmp_sub_nodes[1].back();
13706
13707 // Check if the sub-list of nodes creates a loop (circle)
13709 {
13710 // We need to create two shared polylines and therefore increase
13711 // the shared boundary id by two
13712
13713 // The first and second half of nodes
13714 std::list<Node*> first_half_node_pt;
13715 std::list<Node*> second_half_node_pt;
13716 // The first and second half of boundary elements
13719 // The first and second half of face indexes
13722
13723 // Get the number of sub-nodes in the sub-list of nodes
13724 const unsigned n_sub_nodes = tmp_sub_nodes[1].size();
13725
13726 // The number of sub-nodes for the first half of the shared
13727 // boundary
13728 const unsigned n_sub_nodes_half =
13729 static_cast<unsigned>(n_sub_nodes / 2.0);
13730
13731 // Copy as many sub-nodes for the first half of the sub-polyline
13732
13733 // Iterator to loop over the nodes
13734 std::list<Node*>::iterator it_sub = tmp_sub_nodes[1].begin();
13735
13736 // Add the first node
13737 first_half_node_pt.push_back(*it_sub);
13738
13739 // Skip the first node
13740 it_sub++;
13741
13742 // Counter
13743 unsigned counter_nodes = 0;
13744 unsigned counter2 = 0;
13745
13746 // Loop to copy the nodes
13747 for (; it_sub != tmp_sub_nodes[1].end(); it_sub++)
13748 {
13749 // Add the sub-node to the first half
13750 first_half_node_pt.push_back(*it_sub);
13751 // Add the boundary elements of the first half
13754 // Add the face indexes of the first half
13757
13758 // Increase the counter of added nodes
13759 counter_nodes++;
13760
13761 // Increase the other counter
13762 counter2 += 2;
13763
13765 {
13766 // Stop adding to the first half of nodes
13767 break;
13768 }
13769
13770 } // Copy the first half of nodes
13771
13772 // The second half
13773
13774 // Add the first node of the second half
13775 second_half_node_pt.push_back(*it_sub);
13776
13777 // Skip the first node of the second half
13778 it_sub++;
13779
13780 // Loop to copy the nodes
13781 for (; it_sub != tmp_sub_nodes[1].end(); it_sub++)
13782 {
13783 // Add the sub-node to the first half
13784 second_half_node_pt.push_back(*it_sub);
13785 // Add the boundary elements of the first half
13788 // Add the face indexes of the first half
13791
13792 // Increase the other counter
13793 counter2 += 2;
13794
13795 } // Copy the second half of nodes
13796
13797 // Add the sub-list of nodes to the vector of lists of nodes
13800 // Add the sub-vector of elements to the vector of boundary
13801 // elements
13804 // Add the sub-vector of face indexes to the vector of face
13805 // indexes
13808
13809 // Set the connection flags, change them by the proper
13810 // connection flag
13811
13812 // --------------------------------------
13813 // Connections flags for the first half
13814
13815 // The left connection flag
13816
13817 // Connected to the previous boundary
13819
13820 // The right connection flag
13821
13822 // Set connected to nothing, this is the base node
13823 output_connect_to_the_right.push_back(-1);
13824
13825 // Increase the shared boundary id
13827
13828 // --------------------------------------
13829 // Connections flags for the second half
13830
13831 // The left connection flag
13832
13833 // Set connected to the previous boundary
13835
13836 // The right connection flag
13837
13838 // Are we in the last sub-list of nodes, if that is the case we
13839 // need to respect the flag assigned to the right
13840 if (n_sub_list == 2)
13841 {
13842 // Connected with nothing
13844 {
13845 // Set connected to the previous shared boundary
13847 }
13848 // Connected with the same boundary
13849 else if (input_connect_to_the_right == -2)
13850 {
13851 // Set connected to the previous shared boundary
13853 }
13854 // Connetted with nothing but stop adding nodes
13855 else if (input_connect_to_the_right == -3)
13856 {
13857 // Set connected to the previous shared boundary
13859 }
13860 else
13861 {
13862 // Any other value keep it
13864 }
13865
13866 // Increase the shared boundary id
13868
13869 } // if (n_sub_list == 2)
13870#ifdef PARANOID
13871 else
13872 {
13873 std::stringstream error_message;
13874 error_message
13875 << "The second sub-list of nodes creates a loop but this is not\n"
13876 << "the last list of sub-nodes.\n"
13877 << "This configuration is not supported\n";
13878 throw OomphLibError(
13879 error_message.str(),
13880 "TriangleMesh::break_loops_on_shared_polyline_helper()",
13882 }
13883#endif
13884
13885 } // if (left_sub_node_pt == right_sub_node_pt)
13886 else
13887 {
13888 // No need to create two boundaries, create only one with the
13889 // sub-list of nodes
13890
13891 // Add the sub-list of nodes to the vector of lists of nodes
13893 // Add the sub-vector of elements to the vector of boundary
13894 // elements
13896 // Add the sub-vector of face indexes to the vector of face
13897 // indexes
13899
13900 // Set the connection flags, change them by the proper connection
13901 // flag
13902
13903 // The left connection flag
13904
13905 // Set connected to the previous shared boundary id
13907
13908 // The right connection flag
13909
13910 // Are we in the last sub-list of nodes, if that is the case we
13911 // need to respect the flag assigned to the right
13912 if (n_sub_list == 2)
13913 {
13914 // Connected with nothing but required to stop adding nodes
13916 {
13917 // Set to connected to nothing
13918 output_connect_to_the_right.push_back(-1);
13919 }
13920#ifdef PARANOID
13921 // Connected with itself
13922 else if (input_connect_to_the_right == -2)
13923 {
13924 std::stringstream error_message;
13925 error_message
13926 << "The connection flag to the right ("
13927 << input_connect_to_the_right << ") indicates a connection\n"
13928 << "with the same polyline.\n However, the second sub-list of\n"
13929 << "nodes was found not making a loop so no connection with\n"
13930 << "itself should be marked\n\n";
13931 throw OomphLibError(
13932 error_message.str(),
13933 "TriangleMesh::break_loops_on_shared_polyline_helper()",
13935 }
13936#endif
13937 else
13938 {
13939 // Any other value keep it
13941 }
13942 } // if (n_sub_list == 2)
13943 else
13944 {
13945 // Set connected to the next shared boundary id
13947 } // else if (n_sub_list == 2)
13948
13949 // Increase the shared boundary id by one
13951
13952 } // if (left_sub_node_pt == right_sub_node_pt)
13953
13954 } // if (n_sub_list > 1)
13955
13956 // Three sub-list of nodes were created
13957 if (n_sub_list > 2)
13958 {
13959 // ------------------------------------------------------------
13960 // Get the left and right node of the third sub-list of nodes
13961 left_sub_node_pt = tmp_sub_nodes[2].front();
13962 right_sub_node_pt = tmp_sub_nodes[2].back();
13963
13964 // Check if the sub-list of nodes creates a loop (circle)
13966 {
13967 // We need to create two shared polylines and therefore increase
13968 // the shared boundary id by two
13969
13970 // The first and second half of nodes
13971 std::list<Node*> first_half_node_pt;
13972 std::list<Node*> second_half_node_pt;
13973 // The first and second half of boundary elements
13976 // The first and second half of face indexes
13979
13980 // Get the number of sub-nodes in the sub-list of nodes
13981 const unsigned n_sub_nodes = tmp_sub_nodes[2].size();
13982
13983 // The number of sub-nodes for the first half of the shared
13984 // boundary
13985 const unsigned n_sub_nodes_half =
13986 static_cast<unsigned>(n_sub_nodes / 2.0);
13987
13988 // Copy as many sub-nodes for the first half of the sub-polyline
13989
13990 // Iterator to loop over the nodes
13991 std::list<Node*>::iterator it_sub = tmp_sub_nodes[2].begin();
13992
13993 // Add the first node
13994 first_half_node_pt.push_back(*it_sub);
13995
13996 // Skip the first node
13997 it_sub++;
13998
13999 // Counter
14000 unsigned counter_nodes = 0;
14001 unsigned counter2 = 0;
14002
14003 // Loop to copy the nodes
14004 for (; it_sub != tmp_sub_nodes[2].end(); it_sub++)
14005 {
14006 // Add the sub-node to the first half
14007 first_half_node_pt.push_back(*it_sub);
14008 // Add the boundary elements of the first half
14011 // Add the face indexes of the first half
14014
14015 // Increase the counter of added nodes
14016 counter_nodes++;
14017
14018 // Increase the other counter
14019 counter2 += 2;
14020
14022 {
14023 // Stop adding to the first half of nodes
14024 break;
14025 }
14026
14027 } // Copy the first half of nodes
14028
14029 // The second half
14030
14031 // Add the first node of the second half
14032 second_half_node_pt.push_back(*it_sub);
14033
14034 // Skip the first node of the second half
14035 it_sub++;
14036
14037 // Loop to copy the nodes
14038 for (; it_sub != tmp_sub_nodes[2].end(); it_sub++)
14039 {
14040 // Add the sub-node to the first half
14041 second_half_node_pt.push_back(*it_sub);
14042 // Add the boundary elements of the first half
14045 // Add the face indexes of the first half
14048
14049 // Increase the other counter
14050 counter2 += 2;
14051
14052 } // Copy the second half of nodes
14053
14054 // Add the sub-list of nodes to the vector of lists of nodes
14057 // Add the sub-vector of elements to the vector of boundary
14058 // elements
14061 // Add the sub-vector of face indexes to the vector of face
14062 // indexes
14065
14066 // --------------------------------------
14067 // Connections flags for the first half
14068
14069 // The left connection flag
14070
14071 // Connected to the previous shared boundary
14073
14074 // The right connection flag
14075
14076 // Set connected to nothing, this is the base node
14077 output_connect_to_the_right.push_back(-1);
14078
14079 // Increase the shared boundary id
14081
14082 // --------------------------------------
14083 // Connections flags for the second half
14084
14085 // The left connection flag
14086
14087 // Set connected to the previous boundary
14089
14090 // The right connection flag
14091
14093 {
14094 // Set connected to the previous shared boundary id
14096 }
14097 else if (input_connect_to_the_right == -2)
14098 {
14099 // Set connected to the previous shared boundary id
14101 }
14102 else if (input_connect_to_the_right == -1)
14103 {
14104 // Set connected to the previous shared boundary id
14106 }
14107 else
14108 {
14109 // Any other value keep it
14111 }
14112
14113 // Increase the shared boundary id
14115
14116 } // if (left_sub_node_pt == right_sub_node_pt)
14117 else
14118 {
14119 // No need to create two boundaries, create only one with the
14120 // sub-list of nodes
14121
14122 // Add the sub-list of nodes to the vector of lists of nodes
14124 // Add the sub-vector of elements to the vector of boundary
14125 // elements
14127 // Add the sub-vector of face indexes to the vector of face
14128 // indexes
14130
14131 // Set the connection flags, change them by the proper
14132 // connection flag
14133
14134 // The left connection flag
14135
14136 // Set connected to the previous shared boundary id
14138
14139 // The right connection flag
14140
14141 // Connected with nothing but required to stop adding nodes
14143 {
14144 std::stringstream error_message;
14145 error_message
14146 << "The connection flag to the right ("
14147 << input_connect_to_the_right << ") indicates 'no connection and\n"
14148 << "stop adding nodes'.\n However, the thrid sub-list of\n"
14149 << "nodes must have a connection to the right with the same\n"
14150 << "shared polyline or with any other polyline\n\n";
14151 throw OomphLibError(
14152 error_message.str(),
14153 "TriangleMesh::break_loops_on_shared_polyline_helper()",
14155 }
14156 else if (input_connect_to_the_right == -1)
14157 {
14158 std::stringstream error_message;
14159 error_message
14160 << "The connection flag to the right ("
14161 << input_connect_to_the_right << ") indicates 'no connection.\n"
14162 << "However, the thrid sub-list of nodes must have a connection\n"
14163 << "to the right with the same shared polyline or with any other\n"
14164 << "polyline\n\n";
14165 throw OomphLibError(
14166 error_message.str(),
14167 "TriangleMesh::break_loops_on_shared_polyline_helper()",
14169 }
14170 // Connected with itself
14171 else if (input_connect_to_the_right == -2)
14172 {
14173 // Set connected to the previous shared boundary id
14175 }
14176 else
14177 {
14178 // Any other value keep it
14180 }
14181
14182 // Increase the shared boundary id by one
14184
14185 } // if (left_sub_node_pt == right_sub_node_pt)
14186
14187 } // if (n_sub_list > 2)
14188 }
14189
14190 // ======================================================================
14191 // Break any possible loop created by the sorted list of nodes
14192 // that is used to create a new shared polyline
14193 // ======================================================================
14194 template<class ELEMENT>
14197 const unsigned& initial_shd_bnd_id,
14198 std::list<Node*>& input_nodes,
14202 const int& input_connect_to_the_left,
14203 const int& input_connect_to_the_right,
14204 Vector<std::list<Node*>>& output_sorted_nodes_pt,
14210 {
14211 // Get the left and right node of the current list of sorted nodes
14212 Node* left_node_pt = input_nodes.front();
14213 Node* right_node_pt = input_nodes.back();
14214
14215 // Temporary storage for list of nodes, boundary elements, boundary
14216 // face elements and face element's indexes
14221
14222 // Iterator for the list of input nodes
14223 std::list<Node*>::iterator it = input_nodes.begin();
14224
14225 // Counter
14226 unsigned counter = 0;
14227
14228 // Loop while not all nodes have been done
14229 while (it != input_nodes.end())
14230 {
14231 // Check if the current node is the final one
14232 it++;
14233 // Is the current node the final node?
14234 if (it == input_nodes.end())
14235 {
14236 // Break, add no more nodes
14237 break;
14238 }
14239 else
14240 {
14241 // Restore the iterator
14242 it--;
14243 }
14244
14245 // Get a list of nonrepeated nodes
14246 std::list<Node*> sub_nodes;
14247 // The temporary vector of boundary elements associated with the
14248 // nodes
14250 // The temporary vector of boundary face elements associated with
14251 // the nodes
14253 // The temporary vector of face indexes associated with the
14254 // boundary elements
14256
14257 // Add the current node to the list
14258 sub_nodes.push_back(*it);
14259
14260 // Add nodes until found a repeated node (the left or right
14261 // node) or until reaching the end of the list of nodes
14262 do
14263 {
14264 // Go to the next node
14265 ++it;
14266
14267 // Add the new node
14268 sub_nodes.push_back((*it));
14269
14270 // Add the boundary elements
14272
14273 // Add the boundary face elements
14275
14276 // Add the face indexes
14278
14279 // Increase the counter
14280 counter++;
14281
14282 // Continue adding until reaching a repeated node or the end
14283 // of the list of nodes
14284 } while ((*it) != left_node_pt && (*it) != right_node_pt &&
14285 it != input_nodes.end());
14286
14287 // Add the sub-set of nodes to the temporary storage
14288 tmp_sub_nodes.push_back(sub_nodes);
14289
14290 // Add the boundary elements to the temporary storage
14292 // Add the boundary face elements to the temporary storage
14294 // Add the face indexes to the temporary storage
14296
14297 } // while((*it) != input_nodes.end())
14298
14299 // --------------------------------------------------
14300 // Now create as many shared boundaries as required
14301
14302 // Get the number of sub-list of nodes created
14303 const unsigned n_sub_list = tmp_sub_nodes.size();
14304
14305#ifdef PARANOID
14306 if (n_sub_list > 1)
14307 {
14308 std::stringstream error_message;
14309 error_message
14310 << "The number of sub-list of nodes created from the shared\n"
14311 << "polyline with loops was (" << n_sub_list << ").\n"
14312 << "We can only handle one list which may still contain loops\n"
14313 << "(or repeated nodes)\n";
14314 throw OomphLibError(
14315 error_message.str(),
14316 "TriangleMesh::break_loops_on_shared_polyline_load_balance_helper()",
14318 }
14319#endif
14320
14321 // If there is only one list it may be because there are no loops or
14322 // there is only one loop (a circle)
14323 if (n_sub_list == 1 && (left_node_pt != right_node_pt))
14324 {
14325 // There are no loops, return just after filling the data
14326 // structures
14327
14328 // This is the base case used most of the times
14329
14330 // Set the vector of lists of nodes
14332 // Set the vector of boundary elements
14334 // Set the vector of boundary face elements
14336 // Set the vector of face indexes
14338
14339 // Set the connection flags, change them by the proper connection
14340 // flag
14341
14342#ifdef PARANOID
14343 if (input_connect_to_the_left == -2)
14344 {
14345 std::stringstream error_message;
14346 error_message
14347 << "The connection flag to the left (" << input_connect_to_the_left
14348 << ") indicates a connection\n"
14349 << "with the same polyline.\n However, only one sub-polyline was "
14350 << "found and no loops\nwere identified\n\n";
14351 throw OomphLibError(
14352 error_message.str(),
14353 "TriangleMesh::break_loops_on_shared_polyline_load_balance_helper()",
14355 }
14356#endif
14357
14358 // The left connection flag
14359 if (input_connect_to_the_left == -3)
14360 {
14361 output_connect_to_the_left.push_back(-1);
14362 }
14363 else
14364 {
14366 }
14367
14368#ifdef PARANOID
14370 {
14371 std::stringstream error_message;
14372 error_message
14373 << "The connection flag to the right (" << input_connect_to_the_right
14374 << ") indicates a connection\n"
14375 << "with the same polyline.\n However, only one sub-polyline was "
14376 << "found and no loops\nwere identified\n\n";
14377 throw OomphLibError(
14378 error_message.str(),
14379 "TriangleMesh::break_loops_on_shared_polyline_load_balance_helper()",
14381 }
14382#endif
14383
14384 // The right connection flag
14386 {
14387 output_connect_to_the_right.push_back(-1);
14388 }
14389 else
14390 {
14392 }
14393
14394 // Return immediately
14395 return;
14396 }
14397
14398 // The temporary storage for the shared boundary id
14400
14401 // -----------------------------------------------------------------
14402 // Check all the sub-list of nodes and create two shared boundaries
14403 // from those that make a loop (circle)
14404
14405 // -----------------------------------------------------------
14406 // Get the left and right node of the first sub-list of nodes
14407 Node* left_sub_node_pt = tmp_sub_nodes[0].front();
14409
14410 // Check if the sub-list of nodes creates a loop (circle)
14412 {
14413 // We need to create two shared polylines and therefore increase
14414 // the shared boundary id by two
14415
14416 // The first and second half of nodes
14417 std::list<Node*> first_half_node_pt;
14418 std::list<Node*> second_half_node_pt;
14419 // The first and second half of boundary elements
14422 // The first and second half of boundary face elements
14425 // The first and second half of face indexes
14428
14429 // Get the number of sub-nodes in the sub-list of nodes
14430 const unsigned n_sub_nodes = tmp_sub_nodes[0].size();
14431
14432 // The number of sub-nodes for the first half of the shared
14433 // boundary
14434 const unsigned n_sub_nodes_half =
14435 static_cast<unsigned>(n_sub_nodes / 2.0);
14436
14437 // Copy as many sub-nodes for the first half of the sub-polyline
14438
14439 // Iterator to loop over the nodes
14440 std::list<Node*>::iterator it_sub = tmp_sub_nodes[0].begin();
14441
14442 // Add the first node
14443 first_half_node_pt.push_back(*it_sub);
14444
14445 // Skip the first node
14446 it_sub++;
14447
14448 // Counter
14449 unsigned counter_nodes = 0;
14450 unsigned counter2 = 0;
14451
14452 // Loop to copy the nodes
14453 for (; it_sub != tmp_sub_nodes[0].end(); it_sub++)
14454 {
14455 // Add the sub-node to the first half
14456 first_half_node_pt.push_back(*it_sub);
14457
14458 // Add the boundary elements of the first half
14460 // Add the boundary face elements of the first half
14462 // Add the face indexes of the first half
14464
14465 // Increase the counter of added nodes
14466 counter_nodes++;
14467
14468 // Increase the other counter (of the elements/face)
14469 counter2++;
14470
14472 {
14473 // Stop adding to the first half of nodes
14474 break;
14475 }
14476
14477 } // Copy the first half of nodes
14478
14479 // The second half
14480
14481 // Add the first node of the second half
14482 second_half_node_pt.push_back(*it_sub);
14483
14484 // Skip the first node of the second half
14485 it_sub++;
14486
14487 // Loop to copy the nodes
14488 for (; it_sub != tmp_sub_nodes[0].end(); it_sub++)
14489 {
14490 // Add the sub-node to the first half
14491 second_half_node_pt.push_back(*it_sub);
14492
14493 // Add the boundary elements of the first half
14495 // Add the boundary face elements of the first half
14497 // Add the face indexes of the first half
14499
14500 // Increase the other counter
14501 counter2++;
14502
14503 } // Copy the second half of nodes
14504
14505 // Add the sub-list of nodes to the vector of lists of nodes
14508 // Add the sub-vector of elements to the vector of boundary
14509 // elements
14512 // Add the sub-vector of face elements to the vector of boundary
14513 // elements
14516 // Add the sub-vector of face indexes to the vector of face
14517 // indexes
14520
14521 // Set the connection flags, change them by the proper connection
14522 // flag
14523
14524 // ----------------------------------------------------------------
14525 // Connections flags for the first half
14526
14527 // The left connection flag
14528
14529 // Connected with nothing but required to stop adding nodes
14530 if (input_connect_to_the_left == -3)
14531 {
14532 // Set connected to nothing
14533 output_connect_to_the_left.push_back(-1);
14534 }
14535 // Connected with itself
14536 else if (input_connect_to_the_left == -2)
14537 {
14538 // Set connected to nothing, this is the base node
14539 output_connect_to_the_left.push_back(-1);
14540 }
14541 else
14542 {
14543 // Any other value keep it
14545 }
14546
14547 // The right connection flag
14548
14549 // Set connected to nothing, this is the base node
14550 output_connect_to_the_right.push_back(-1);
14551
14552 // Increase the shared boundary id
14554
14555 // ----------------------------------------------------------------
14556 // Connections flags for the second half
14557
14558 // The left connection flag
14559
14560 // Set connected to the previous boundary
14562
14563 // The right connection flag
14564
14565 // Are we in the last sub-list of nodes, if that is the case we
14566 // need to respect the flag assigned to the right
14567 if (n_sub_list == 1)
14568 {
14570 {
14571 // Set connected to the previous shared boundary id
14573 }
14574 else if (input_connect_to_the_right == -2)
14575 {
14576 // Set connected to the previous shared boundary id
14578 }
14579 else if (input_connect_to_the_right == -1)
14580 {
14581 // Set connected to the previous shared boundary id
14583 }
14584 else
14585 {
14586 // Any other value keep it
14588 }
14589 } // if (n_sub_list == 1)
14590 else
14591 {
14592 // Set connected to the previous shared boundary id
14594 }
14595
14596 // Increase the shared boundary id
14598
14599 } // if (left_sub_node_pt == right_sub_node_pt)
14600#ifdef PARANOID
14601 else
14602 {
14603 std::stringstream error_message;
14604 error_message
14605 << "The initial and final node in the current shared polyline are not\n"
14606 << "the same and the number of sublists is (" << n_sub_list << ").\n"
14607 << "We can not handle more than one sublist in the method to break\n"
14608 << "loops at the load balance stage\n\n";
14609 throw OomphLibError(
14610 error_message.str(),
14611 "TriangleMesh::break_loops_on_shared_polyline_load_balance_helper()",
14613 }
14614#endif
14615 }
14616
14617 // ======================================================================
14618 // Create the shared polyline and fill the data structured
14619 // that keep all the information associated with the creationg of the
14620 // shared boundary
14621 // ======================================================================
14622 template<class ELEMENT>
14624 const unsigned& my_rank,
14625 const unsigned& shd_bnd_id,
14626 const unsigned& iproc,
14627 const unsigned& jproc,
14628 std::list<Node*>& sorted_nodes,
14629 const int& root_edge_bnd_id,
14633 const int& connect_to_the_left_flag,
14634 const int& connect_to_the_right_flag)
14635 {
14636 // ----------------------------------------------------------------
14637 // Associate the shared boundary with the respective processors
14638 // ----------------------------------------------------------------
14639
14640 // Setup the global look-up scheme, where all processors know the
14641 // associations of others processors and the shared boundaries they
14642 // created
14643
14644 // Set up the boundary shared by "iproc" with "jproc" processor
14645 Shared_boundaries_ids[iproc][jproc].push_back(shd_bnd_id);
14646
14647 // Set up the boundary shared by "jproc" with "iproc" processor
14648 Shared_boundaries_ids[jproc][iproc].push_back(shd_bnd_id);
14649
14650 // Specify the processors involved on the creation of the shared
14651 // boundary
14653 processors[0] = iproc;
14654 processors[1] = jproc;
14655 Shared_boundary_from_processors[shd_bnd_id] = processors;
14656
14657 // ----------------------------------------------------------------
14658 // If one of the processor associated with the shared boundary is
14659 // the current processor then it needs to create a polyline from the
14660 // input sorted nodes, other processors can skip this part
14661 if (iproc == my_rank || jproc == my_rank)
14662 {
14663 // ------------------------------------------------------------
14664 // Create a vertices representation from the sorted nodes list
14665 // ------------------------------------------------------------
14666
14667 // Get the number of nodes on the list
14668 const unsigned n_nodes = sorted_nodes.size();
14669 // The vector to store the vertices (assign space)
14671
14672 // Copy the vertices from the nodes
14673 unsigned counter = 0;
14674
14675 for (std::list<Node*>::iterator it = sorted_nodes.begin();
14676 it != sorted_nodes.end();
14677 it++)
14678 {
14679 vertices[counter].resize(2);
14680 vertices[counter][0] = (*it)->x(0);
14681 vertices[counter][1] = (*it)->x(1);
14682 counter++;
14683 }
14684
14685 // ---------------------------------------------
14686 // Create the polyline from the input vertices
14687 // ---------------------------------------------
14688 TriangleMeshPolyLine* polyline_pt =
14690
14691 // ---------------------------------------------
14692 // Establish the internal boundary information
14693 // ---------------------------------------------
14694
14695 // Check if the shared boundary is overlapping (or is part) of an
14696 // internal boundary
14697 if (root_edge_bnd_id != -1)
14698 {
14699 // If the shared boundary is part of an internal boundary then
14700 // mark the shared boundary
14701 Shared_boundary_overlaps_internal_boundary[shd_bnd_id] =
14702 static_cast<unsigned>(root_edge_bnd_id);
14703 } // if (root_edge_bnd_id != -1)
14704
14705 // ---------------------------------------------
14706 // Store the boundary elements and face indexes
14707 // ---------------------------------------------
14708
14709 // Store the shared boundary elements
14711#ifdef PARANOID
14712 // Check that the number of shared boundy elements is the same as
14713 // the number of face indexes
14714 const unsigned n_face_index = face_index_ele.size();
14716 {
14717 std::ostringstream error_message;
14718 error_message
14719 << "The number of shared boundary elements is different from the\n"
14720 << "number of face indexes associated to the shared boundary\n"
14721 << "elements\n"
14722 << "Number of shared boundary elements: ("
14723 << n_shared_boundary_elements << ")\n"
14724 << "Number of face indexes: (" << n_face_index << ")\n\n";
14725 throw OomphLibError(error_message.str(),
14726 "TriangleMesh::create_shared_polyline()",
14728 } // if (n_shared_boundary_elements != n_face_index)
14729#endif
14730
14731 // Add the shared boundary elements and their respective face
14732 // indexes to their permanent containers
14733 for (unsigned i = 0; i < n_shared_boundary_elements; i++)
14734 {
14735 add_shared_boundary_element(shd_bnd_id, bulk_bnd_ele_pt[i]);
14736 add_face_index_at_shared_boundary(shd_bnd_id, face_index_ele[i]);
14737 } // for (i < nshared_boundary_elements)
14738
14739 // Store the shared boundary nodes
14740 for (std::list<Node*>::iterator it = sorted_nodes.begin();
14741 it != sorted_nodes.end();
14742 it++)
14743 {
14744 add_shared_boundary_node(shd_bnd_id, (*it));
14745 } // for (it != sorted_nodes.end())
14746
14747 // ----------------------------------------------------------
14748 // Create additional look-up schemes for the shared boundary
14749 // ----------------------------------------------------------
14750
14751 // Updates bnd_id <---> curve section map
14752 this->Boundary_curve_section_pt[shd_bnd_id] = polyline_pt;
14753
14754 // Check the size of the unsorted_polylines_pt structure. This
14755 // will have n_procs = 1 when it was called from the
14756 // create_new_shared_boundaries() methods
14757 const unsigned n_procs = unsorted_polylines_pt.size();
14758 if (n_procs > 1)
14759 {
14760 // Add the new created polyline to the list of unsorted
14761 // polylines
14762 unsorted_polylines_pt[iproc].push_back(polyline_pt);
14763
14764 // ... do this on both processors involved in the creation of
14765 // the shared boundary
14766 unsorted_polylines_pt[jproc].push_back(polyline_pt);
14767 }
14768 else
14769 {
14770 // Add the new created polyline to the list of unsorted
14771 // polylines
14772 unsorted_polylines_pt[0].push_back(polyline_pt);
14773 }
14774
14775 // Mark the polyline for deletion (when calling destructor)
14776 this->Free_curve_section_pt.insert(polyline_pt);
14777
14778 // ----------------------------
14779 // Set connection information
14780 // ----------------------------
14781
14782 // Check that the flags are correct, no connection or the boundary
14783 // id of the boundary to connect
14784#ifdef PARANOID
14785 // Is the shared polyline not connected to the left
14787 {
14788 // If not connected then should be specified by -1
14789 if (connect_to_the_left_flag != -1)
14790 {
14791 std::ostringstream error_message;
14792 error_message
14793 << "The only accepted values for the connection flags are:\n"
14794 << "POSITIVE values or -1, any other value is rejected, please\n"
14795 << "check that you previously called the methods to deal with\n"
14796 << "other flag values\n"
14797 << "The current flag value for connection to the left is: ("
14798 << connect_to_the_left_flag << ")\n\n";
14799 throw OomphLibError(error_message.str(),
14800 "TriangleMesh::create_shared_polyline()",
14802 } // if (connect_to_the_left_flag != -1)
14803 } // if (connect_to_the_left_flag < 0)
14804
14805 // Is the shared polyline not connected to the right
14807 {
14808 // If not connected then should be specified by -1
14809 if (connect_to_the_right_flag != -1)
14810 {
14811 std::ostringstream error_message;
14812 error_message
14813 << "The only accepted values for the connection flags are:\n"
14814 << "POSITIVE values or -1, any other value is rejected, please\n"
14815 << "check that you previously called the methods to deal with\n"
14816 << "other flag values\n"
14817 << "The current flag value for connection to the right is: ("
14818 << connect_to_the_right_flag << ")\n\n";
14819 throw OomphLibError(error_message.str(),
14820 "TriangleMesh::create_shared_polyline()",
14822 } // if (connect_to_the_right_flag != -1)
14823 } // if (connect_to_the_right_flag < 0)
14824#endif
14825
14826 // Set the connection to the left
14827 if (connect_to_the_left_flag != -1)
14828 {
14829 // Get the unsigned version of the boundary id to the left
14830 const unsigned bnd_id_connection_to_the_left =
14831 static_cast<unsigned>(connect_to_the_left_flag);
14832 // Set the initial vertex as connected
14833 polyline_pt->set_initial_vertex_connected();
14834 // Set the initial vertex connected boundary id
14835 polyline_pt->initial_vertex_connected_bnd_id() =
14837 // Set the chunk number to zero
14838 polyline_pt->initial_vertex_connected_n_chunk() = 0;
14839
14840 } // if (connect_to_the_left_flag != -1)
14841
14842 // Set the connection to the right
14843 if (connect_to_the_right_flag != -1)
14844 {
14845 // Get the unsigned version of the boundary id to the right
14846 const unsigned bnd_id_connection_to_the_right =
14847 static_cast<unsigned>(connect_to_the_right_flag);
14848 // Set the final vertex as connected
14849 polyline_pt->set_final_vertex_connected();
14850 // Set the final vertex connected boundary id
14851 polyline_pt->final_vertex_connected_bnd_id() =
14853 // Set the chunk number to zero
14854 polyline_pt->final_vertex_connected_n_chunk() = 0;
14855
14856 } // if (connect_to_the_right_flag != -1)
14857
14858 } // if (iproc == my_rank || jproc == my_rank)
14859 }
14860
14861 //======================================================================
14862 /// Reset the boundary elements info. after load balance have
14863 /// taken place
14864 //======================================================================
14865 template<class ELEMENT>
14870 {
14871 // Get the number of boundaries
14872 const unsigned nbound = this->nboundary();
14873
14874 // Are there regions?
14875 const unsigned n_regions = this->nregion();
14876
14877 // Loop over the boundaries
14878 for (unsigned b = 0; b < nbound; b++)
14879 {
14880 // Get the boundary elements and back them up
14881 // -----------------------------------------------------------------
14882 // Get the number of boundary elements (mixed with the old and new)
14883 const unsigned nbound_ele = this->nboundary_element(b);
14884 // Back-up the boundary elements
14887 for (unsigned e = 0; e < nbound_ele; e++)
14888 {
14889 // Get the old boundary element
14890 backed_up_boundary_element_pt[e] = this->boundary_element_pt(b, e);
14891 // Get the old face index
14893 this->face_index_at_boundary(b, e);
14894 } // for (n < nold_boundary_elements)
14895
14896 // Back up the elements in boundary for each region
14898 n_regions);
14900
14901 // Loop over the regions and back up the boundary elements in
14902 // regions
14903 for (unsigned ir = 0; ir < n_regions; ir++)
14904 {
14905 // Get the region id
14906 const unsigned region_id =
14907 static_cast<unsigned>(this->region_attribute(ir));
14908 // Get the number of boundary region elements (mixed old and new)
14909 const unsigned nbnd_region_ele =
14910 this->nboundary_element_in_region(b, region_id);
14911
14912 // Loop over the elements in the region
14913 for (unsigned e = 0; e < nbnd_region_ele; e++)
14914 {
14915 // Get the old boundary region element
14917 this->boundary_element_in_region_pt(b, region_id, e);
14918
14919 // Get the old face index
14921 this->face_index_at_boundary_in_region(b, region_id, e);
14922 } // for (e < nbnd_region_ele)
14923
14924 } // for (ir < n_regions)
14925
14926 // Clean all previous storages
14927 this->Boundary_element_pt[b].clear();
14928 this->Face_index_at_boundary[b].clear();
14929 if (n_regions > 0)
14930 {
14931 this->Boundary_region_element_pt[b].clear();
14932 this->Face_index_region_at_boundary[b].clear();
14933 }
14934
14935 // -------------------------------------------------------------------
14936 // Now copy only the elements that are still alive, from those before
14937 // the re-establishment of halo and haloed elements
14938 // -------------------------------------------------------------------
14939 // Start with the boundary elements
14940 // Get the old number of boundary elements
14941 const unsigned nold_bnd_ele = ntmp_boundary_elements[b];
14942 // Loop over the boundary elements and check those still alive
14943 for (unsigned e = 0; e < nold_bnd_ele; e++)
14944 {
14946 // Include only those elements still alive
14949 // Only copy thoes elements not found on the deleted elements
14950 // container
14951 if (it == deleted_elements.end())
14952 {
14954 this->Boundary_element_pt[b].push_back(add_ele_pt);
14955 const int face_index = backed_up_face_index_at_boundary[e];
14956 this->Face_index_at_boundary[b].push_back(face_index);
14957 } // if (tmp_ele_pt != 0)
14958
14959 } // for (n < nold_bnd_ele)
14960
14961 // ... continue with the boundary elements in specific regions
14962
14963 // Loop over the regions
14964 for (unsigned ir = 0; ir < n_regions; ir++)
14965 {
14966 // Get the region id
14967 const unsigned region_id =
14968 static_cast<unsigned>(this->region_attribute(ir));
14969
14970 // Get the old number of boundary elements in region
14971 const unsigned nold_bnd_region_ele =
14973
14974 // Loop over the boundary region elements and check those still
14975 // alive
14976 for (unsigned e = 0; e < nold_bnd_region_ele; e++)
14977 {
14978 // Get the element
14981 // Include only those elements still alive
14984 // Only copy those elements not found on the deleted elements
14985 // container
14986 if (it == deleted_elements.end())
14987 {
14990 this->Boundary_region_element_pt[b][region_id].push_back(
14991 add_ele_pt);
14992 const int face_index =
14994 this->Face_index_region_at_boundary[b][region_id].push_back(
14995 face_index);
14996 } // if (tmp_ele_pt != 0)
14997
14998 } // for (n < nbound_ele)
14999
15000 } // for (ir < n_regions)
15001
15002 // ----------------------------------------------------------------
15003 // Now copy all those elements created after the re-establishment
15004 // of halo and haloed elements
15005 // ----------------------------------------------------------------
15006 // Loop over the boundary elements
15007 for (unsigned e = nold_bnd_ele; e < nbound_ele; e++)
15008 {
15010 this->Boundary_element_pt[b].push_back(add_ele_pt);
15011 const int face_index = backed_up_face_index_at_boundary[e];
15012 this->Face_index_at_boundary[b].push_back(face_index);
15013 } // for (e < nbound_ele)
15014
15015 // Now add the boundary elements in regions
15016
15017 // Loop over the regions
15018 for (unsigned ir = 0; ir < n_regions; ir++)
15019 {
15020 // Get the region id
15021 const unsigned region_id =
15022 static_cast<unsigned>(this->region_attribute(ir));
15023
15024 // Get the old number of boundary elements in region
15025 const unsigned nold_bnd_region_ele =
15027
15028 // Get the new number of boundary elements in region
15029 const unsigned nbnd_region_ele =
15030 this->nboundary_element_in_region(b, region_id);
15031
15032 // Loop over the boundary region elements and check those still
15033 // alive
15034 for (unsigned e = nold_bnd_region_ele; e < nbnd_region_ele; e++)
15035 {
15038 this->Boundary_region_element_pt[b][region_id].push_back(add_ele_pt);
15039 const int face_index = backed_up_face_index_at_boundary_region[ir][e];
15040 this->Face_index_region_at_boundary[b][region_id].push_back(
15041 face_index);
15042 } // for (e < nbnd_region_ele)
15043
15044 } // for (ir < n_regions)
15045
15046 } // for (b < nbound)
15047
15048 // Lookup scheme has now been setup yet
15049 Lookup_for_elements_next_boundary_is_setup = true;
15050 }
15051
15052#endif // OOMPH_HAS_MPI
15053
15054#ifdef OOMPH_HAS_TRIANGLE_LIB
15055
15056 //========================================================================
15057 /// Build a new TriangulateIO object based on target areas specified
15058 //========================================================================
15059 template<class ELEMENT>
15064 {
15065 // Initialize
15067
15068 // Store the global number of vertices and segments
15069 // in the list
15070 unsigned n_points = triangulate_io.numberofpoints;
15071 triangle_refine.numberofpoints = n_points;
15072
15073 unsigned n_segments = triangulate_io.numberofsegments;
15074 triangle_refine.numberofsegments = n_segments;
15075
15076 // Initialization of the TriangulateIO objects to store the values
15077 triangle_refine.pointlist =
15078 (double*)malloc(triangulate_io.numberofpoints * 2 * sizeof(double));
15079 triangle_refine.pointmarkerlist =
15080 (int*)malloc(triangulate_io.numberofpoints * sizeof(int));
15081 triangle_refine.segmentlist =
15082 (int*)malloc(triangulate_io.numberofsegments * 2 * sizeof(int));
15083 triangle_refine.segmentmarkerlist =
15084 (int*)malloc(triangulate_io.numberofsegments * sizeof(int));
15085
15086 // Storing the point's coordinates in the list
15087 // and in two vectors with x and y coordinates
15090
15091 for (unsigned count_point = 0; count_point < n_points * 2; count_point++)
15092 {
15093 triangle_refine.pointlist[count_point] =
15094 triangulate_io.pointlist[count_point];
15095
15096 // Even vaules represent the x coordinate
15097 // Odd values represent the y coordinate
15098 if (count_point % 2 == 0)
15099 {
15101 }
15102 else
15103 {
15104 y_coord[(count_point - 1) / 2] = triangulate_io.pointlist[count_point];
15105 }
15106 }
15107
15108 // Store the point's markers in the list
15109 for (unsigned count_marker = 0; count_marker < n_points; count_marker++)
15110 {
15111 triangle_refine.pointmarkerlist[count_marker] =
15112 triangulate_io.pointmarkerlist[count_marker];
15113 }
15114
15115 // Storing the segment's edges in the list
15116 for (unsigned count_seg = 0; count_seg < n_segments * 2; count_seg++)
15117 {
15118 triangle_refine.segmentlist[count_seg] =
15119 triangulate_io.segmentlist[count_seg];
15120 }
15121
15122 // Store the segment's markers in the list
15123 for (unsigned count_markers = 0; count_markers < n_segments;
15124 count_markers++)
15125 {
15126 triangle_refine.segmentmarkerlist[count_markers] =
15127 triangulate_io.segmentmarkerlist[count_markers];
15128 }
15129
15130 // Store the hole's center coordinates
15131 unsigned n_holes = triangulate_io.numberofholes;
15132 triangle_refine.numberofholes = n_holes;
15133
15134 triangle_refine.holelist =
15135 (double*)malloc(triangulate_io.numberofholes * 2 * sizeof(double));
15136
15137 // Loop over the holes to get centre coords
15138 for (unsigned count_hole = 0; count_hole < n_holes * 2; count_hole++)
15139 {
15140 triangle_refine.holelist[count_hole] =
15141 triangulate_io.holelist[count_hole];
15142 }
15143
15144 // Store the triangles values
15145 unsigned n_triangles = triangulate_io.numberoftriangles;
15146 triangle_refine.numberoftriangles = n_triangles;
15147
15148#ifdef PARANOID
15149 if (n_triangles != target_area.size())
15150 {
15151 std::stringstream err;
15152 err << "Number of triangles in triangulate_io=" << n_triangles
15153 << " doesn't match\n"
15154 << "size of target area vector (" << target_area.size() << ")\n";
15155 throw OomphLibError(
15157 }
15158#endif
15159
15160 unsigned n_corners = triangulate_io.numberofcorners;
15161 triangle_refine.numberofcorners = n_corners;
15162
15163 triangle_refine.trianglelist =
15164 (int*)malloc(triangulate_io.numberoftriangles * 3 * sizeof(int));
15165
15166 // Store the triangle's corners in the list and get element sizes
15167 for (unsigned count_tri = 0; count_tri < n_triangles * 3; count_tri++)
15168 {
15169 triangle_refine.trianglelist[count_tri] =
15170 triangulate_io.trianglelist[count_tri];
15171 }
15172
15173 // Store the triangle's area in the list
15174 triangle_refine.trianglearealist =
15175 (double*)malloc(triangulate_io.numberoftriangles * sizeof(double));
15176 for (unsigned count_area = 0; count_area < n_triangles; count_area++)
15177 {
15178 triangle_refine.trianglearealist[count_area] = target_area[count_area];
15179 }
15180
15181 // Store the triangles attributes in the list
15182 triangle_refine.numberoftriangleattributes =
15183 triangulate_io.numberoftriangleattributes;
15184
15185 triangle_refine.triangleattributelist = (double*)malloc(
15186 triangulate_io.numberoftriangles *
15187 triangulate_io.numberoftriangleattributes * sizeof(double));
15188 for (unsigned count_attribute = 0;
15190 (n_triangles * triangulate_io.numberoftriangleattributes);
15192 {
15193 triangle_refine.triangleattributelist[count_attribute] =
15194 triangulate_io.triangleattributelist[count_attribute];
15195 }
15196 }
15197
15198#ifdef OOMPH_HAS_MPI
15199
15200 // ===================================================================
15201 // The comparison class for the map that sorts the nodes on the
15202 // shared boundary (using a lexicographic order)
15203 // ===================================================================
15205 {
15206 // Tolerance for lower-left comparison
15207 static double Tol;
15208
15209 // Comparison operator for "lower left" ordering
15210 bool operator()(const std::pair<double, double>& lhs,
15211 const std::pair<double, double>& rhs) const
15212 {
15213 double diff_y = lhs.second - rhs.second;
15214 if (diff_y < -Tol) // (lhs.second < rhs.second)
15215 {
15216 return true;
15217 }
15218 else
15219 {
15220 // Are they "equal" with 1.0e-14 tolerance?
15221 if (diff_y < Tol) // (lhs.second == rhs.second)
15222 {
15223#ifdef PARANOID
15224 double diff_x = lhs.first - rhs.first;
15225 if (fabs(diff_x) < Tol)
15226 {
15227 std::ostringstream warning_message;
15229 << "Dodgy \"lower left\" (lexicographic) comparison "
15230 << "of points with cooordinates: "
15231 << " lhs = ( " << lhs.first << " , " << lhs.second << " ) \n"
15232 << " rhs = ( " << rhs.first << " , " << rhs.second << " ) \n"
15233 << "x and y coordinates differ by less than tolerance!\n"
15234 << "diff_x = " << diff_x << "\n"
15235 << "diff_y = " << diff_y << "\n"
15236 << "Tol = " << Tol << "\n";
15240 }
15241#endif
15242 if (lhs.first < rhs.first)
15243 {
15244 return true;
15245 }
15246 else
15247 {
15248 return false;
15249 }
15250 }
15251 else
15252 {
15253 return false;
15254 }
15255 }
15256
15257 // if (lhs.second < rhs.second)
15258 // {
15259 // return true;
15260 // }
15261 // else
15262 // {
15263 // // // Are "equal" with 1.0e-14 tolerance
15264 // // if (lhs.second - rhs.second < 1.0e-14)
15265 // // Are equal?
15266 // if (lhs.second == rhs.second)
15267 // {
15268 // if (lhs.first < rhs.first)
15269 // {
15270 // return true;
15271 // }
15272 // else
15273 // {
15274 // return false;
15275 // }
15276 // }
15277 // else
15278 // {
15279 // return false;
15280 // }
15281 // }
15282 }
15283
15284 } Bottom_left_sorter; // struct classcomp
15285
15286 // Assign value for tolerance
15287 double classcomp::Tol = 1.0e-14;
15288
15289 //======================================================================
15290 // Sort the nodes on shared boundaries so that the processors that share
15291 // a boundary agree with the order of the nodes on the boundary
15292 //======================================================================
15293 template<class ELEMENT>
15295 {
15296 // Get the shared boundaries in this processor
15298 this->shared_boundaries_in_this_processor(my_rank_shared_boundaries_ids);
15299
15300 // Get the number of shared boundaries
15302
15303 // Loop over the shared boundaries
15304 for (unsigned i = 0; i < nmy_rank_shd_bnd; i++)
15305 {
15306 // A map is used to sort the nodes using their coordinates as the key
15307 // of the map
15308 // std::map<std::pair<double, double>, Node*> sorted_nodes_pt;
15309 std::map<std::pair<double, double>, Node*, classcomp> sorted_nodes_pt;
15310
15311#ifdef PARANOID
15312
15313 // Check min distance between nodes; had better be less than the
15314 // tolerance used for the bottom left sorting
15316
15317#endif
15318
15319 // Get the boundary id
15320 const unsigned b = my_rank_shared_boundaries_ids[i];
15321
15322 // Get the number of nodes on the current boundary
15323 const unsigned nbnd_node = this->nshared_boundary_node(b);
15324
15325 // Go through all the nodes on the boundary and temporarily store
15326 // them on the map container
15327 for (unsigned i_node = 0; i_node < nbnd_node; i_node++)
15328 {
15329 Node* node_pt = this->shared_boundary_node_pt(b, i_node);
15330 std::pair<double, double> vertex =
15331 std::make_pair(node_pt->x(0), node_pt->x(1));
15333
15334#ifdef PARANOID
15335
15336 // Check for minimum distance
15337 for (unsigned j_node = 0; j_node < nbnd_node; j_node++)
15338 {
15339 if (i_node != j_node)
15340 {
15341 Node* node2_pt = this->shared_boundary_node_pt(b, j_node);
15342
15343 // Squared distance
15344 double squared_distance = 0.0;
15345 for (unsigned ii = 0; ii < 2; ii++)
15346 {
15347 squared_distance += (node_pt->x(ii) - node2_pt->x(ii)) *
15348 (node_pt->x(ii) - node2_pt->x(ii));
15349 }
15351 {
15353 }
15354 }
15355 }
15356
15358 {
15359 std::ostringstream warning_message;
15360 warning_message << "Minimum distance between nodes on boundary " << b
15361 << "\n"
15362 << "is " << sqrt(min_distance_squared)
15363 << " which is less than "
15364 << "Bottom_left_sorter.Tol = "
15365 << Bottom_left_sorter.Tol << "\n"
15366 << "This may screw up the ordering of the nodes on "
15367 "shared boundaries\n";
15371 }
15372
15373#endif
15374 }
15375
15376 unsigned counter = 0;
15377 // Resize the sorted shared boundary node vector
15378 this->Sorted_shared_boundary_node_pt[b].resize(nbnd_node);
15379
15380 // Now go through the map container, get the elements and store their
15381 // members on the Sorted_shared_boundary_node_pt container
15382 // The map has already sorted the nodes, now they keep the same sorting
15383 // on all processors
15384 for (std::map<std::pair<double, double>, Node*>::iterator it_map =
15385 sorted_nodes_pt.begin();
15386 it_map != sorted_nodes_pt.end();
15387 it_map++)
15388 {
15389 // Store the pointer to the node
15390 this->Sorted_shared_boundary_node_pt[b][counter++] = (*it_map).second;
15391 }
15392
15393 } // for (i < nmy_rank_shd_bnd)
15394 }
15395
15396 //========================================================================
15397 // Re-establish the shared boundary elements after the adaptation
15398 // process (the updating of shared nodes is optional and performed by
15399 // default)
15400 //========================================================================
15401 template<class ELEMENT>
15404 const bool update_elements,
15405 const bool flush_nodes,
15406 const bool update_nodes)
15407 {
15408 // Get the rank of the current processor
15409 const unsigned my_rank = this->communicator_pt()->my_rank();
15410
15411 // Go through the boundaries know as shared boundaries and copy the
15412 // elements to the corresponding storage
15413
15414 // Get the initial shared boundary id
15415 const unsigned initial_id = this->initial_shared_boundary_id();
15416
15417 // Get the final shared boundary id
15418 const unsigned final_id = this->final_shared_boundary_id();
15419
15420 if (flush_elements)
15421 {
15422 // Flush the shared boundaries storage for elements
15423 this->flush_shared_boundary_element();
15424 // .. and also flush the face indexes associated with the element
15425 this->flush_face_index_at_shared_boundary();
15426 } // if (flush_elements)
15427
15428 if (flush_nodes)
15429 {
15430 // Flush the shared boundaries storage for nodes
15431 this->flush_shared_boundary_node();
15432 } // if (flush_nodes)
15433
15434 for (unsigned b = initial_id; b < final_id; b++)
15435 {
15436 // Check if the boundary is on the current processor
15438 procs_from_shrd_bnd = this->shared_boundary_from_processors(b);
15441 for (unsigned p = 0; p < n_procs_from_shrd_bnd; p++)
15442 {
15444 {
15446 break; // break for (p < n_procs_from_shrd_bnd)
15447 }
15448 } // for (p < n_procs_from_shrd_bnd)
15449
15451 {
15452 if (update_elements)
15453 {
15454 const unsigned nboundary_ele = this->nboundary_element(b);
15455 for (unsigned e = 0; e < nboundary_ele; e++)
15456 {
15457 // Get the boundary element and add it to the shared
15458 // boundary elements structure
15459 FiniteElement* bnd_ele_pt = this->boundary_element_pt(b, e);
15460 this->add_shared_boundary_element(b, bnd_ele_pt);
15461 // ... do the same with the face index information
15462 int face_index = this->face_index_at_boundary(b, e);
15463 this->add_face_index_at_shared_boundary(b, face_index);
15464 } // for (e < nboundary_element)
15465 } // if (update_elements)
15466
15467 if (update_nodes)
15468 {
15469 const unsigned nboundary_node = this->nboundary_node(b);
15470 for (unsigned n = 0; n < nboundary_node; n++)
15471 {
15472 Node* bnd_node_pt = this->boundary_node_pt(b, n);
15473 this->add_shared_boundary_node(b, bnd_node_pt);
15474 } // for (n < nboundary_node)
15475 } // if (update_nodes)
15476
15477 } // if (current_processor_has_b_boundary)
15478 } // for (b < final_id)
15479 }
15480
15481 //======================================================================
15482 // Sort the nodes on shared boundaries so that the processors that share
15483 // a boundary agree with the order of the nodes on the boundary
15484 //======================================================================
15485 template<class ELEMENT>
15487 {
15488 // Get the number of processors
15489 unsigned nproc = this->communicator_pt()->nproc();
15490 // Get the rank of the current processor
15491 unsigned my_rank = this->communicator_pt()->my_rank();
15492
15493 // Get some timings
15494 double tt_start = 0.0;
15495 double tt_end = 0.0;
15497 {
15499 }
15500
15501 // -------------------------------------------------------------------
15502 // BEGIN: Get the node names and the shared nodes
15503 // -------------------------------------------------------------------
15504
15505 // Container where to store the nodes on shared boundaries no
15506 // associated with the processor that receives the elements/nodes
15507 // other_proc_shd_bnd_node_pt[iproc][jproc][shd_bnd_id][index]
15510 // Resize the container
15511 for (unsigned iproc = 0; iproc < nproc; iproc++)
15512 {
15513 // Resize the container
15515 for (unsigned jproc = 0; jproc < nproc; jproc++)
15516 {
15517 // Get the number of shared boundaries
15518 const unsigned initial_shd_bnd_id = this->initial_shared_boundary_id();
15519 const unsigned final_shd_bnd_id = this->final_shared_boundary_id();
15522 } // for (jproc < nproc)
15523
15524 } // for (iproc < nproc)
15525
15526 // Store the global node names
15527 // global_node_name[x][ ][ ] Global node number
15528 // global_node_name[ ][x][ ] Global node names
15529 // global_node_name[ ][ ][x] Global node info.
15531
15532 // Creates a map between the node name and the index of the global
15533 // node so we can access all its node names
15534 std::map<Vector<unsigned>, unsigned> node_name_to_global_index;
15535
15536 // Store the global shared nodes pointers
15538
15539 // Get the time for computation of global nodes names and shared
15540 // nodes
15542
15543 // Compute all the names of the nodes and fill in the
15544 // "other_proc_shd_bnd_node_pt" structure with the nodes that live
15545 // on this processor (my_rank) by looking over all their names
15546 compute_global_node_names_and_shared_nodes(other_proc_shd_bnd_node_pt,
15550
15551 // Compute the number of elements before adding new ones
15552 const unsigned n_ele = this->nelement();
15553
15554 if (Print_timings_level_adaptation > 1)
15555 {
15556 // The total time for computation of global nodes names and
15557 // shared nodes
15560 oomph_info << "CPU for computing global node names and shared nodes "
15561 << "[n_ele=" << n_ele
15563 << std::endl;
15564 }
15565
15566 // -------------------------------------------------------------------
15567 // END: Get the node names and the shared nodes
15568 // -------------------------------------------------------------------
15569
15570 // -------------------------------------------------------------------
15571 // BEGIN: Using the global node names each processor sends info. of
15572 // the nodes shared with other processors regarding whether they are
15573 // on an original boundary or not. This is required so that at the
15574 // re-generation of halo(ed) elements stage they have the updated
15575 // information
15576 // -------------------------------------------------------------------
15577
15578 // Get the time for sending info. of shared nodes on original
15579 // boundaries
15582
15583 // Send the boundary node info. of nodes on shared boundaries across
15584 // processors
15585 send_boundary_node_info_of_shared_nodes(
15587
15588 if (Print_timings_level_adaptation > 1)
15589 {
15590 // The total time for sending info. of shared nodes lying on
15591 // original boundaries
15593 << "CPU for sending info. of shared nodes on original boundaries: "
15595 << std::endl;
15596 }
15597
15598 // -------------------------------------------------------------------
15599 // END: Using the global node names each processor sends info. of
15600 // the nodes shared with other processors regarding whether they are
15601 // on an original boundary or not. This is required so that at the
15602 // re-generation of halo(ed) elements stage they have the updated
15603 // information
15604 // -------------------------------------------------------------------
15605
15606 // -------------------------------------------------------------------
15607 // BEGIN: Identify the elements of the mesh that have nodes on the
15608 // shared boundaries
15609 // -------------------------------------------------------------------
15610
15611 // Store the elements that have a node on a shared boundary with
15612 // other processors
15613 // ele_with_node_on_shd_bnd_pt[x][ ][ ]: iproc
15614 // ele_with_node_on_shd_bnd_pt[ ][x][ ]: ishd boundary with iproc
15615 // ele_with_node_on_shd_bnd_pt[ ][ ][x]: element with node on shared
15616 // boundary with iproc
15618 // Resize the container with the number of shared boundaries within
15619 // each processor
15620
15621 // loop over the processors
15622 for (unsigned iproc = 0; iproc < nproc; iproc++)
15623 {
15624 const unsigned n_shd_bnd_iproc = this->nshared_boundaries(my_rank, iproc);
15626 } // for (iproc < nproc)
15627
15628 // Go through all the elements and check whether any of their nodes
15629 // lies on any of the shared boundaries
15630
15631 // loop over the elements
15632 for (unsigned e = 0; e < n_ele; e++)
15633 {
15634 // Get the element
15635 FiniteElement* ele_pt = this->finite_element_pt(e);
15636 // Get the number of nodes
15637 const unsigned n_nodes = ele_pt->nnode();
15638 // loop over the nodes and check whether any of them lies on a
15639 // shared boundary
15640 for (unsigned n = 0; n < n_nodes; n++)
15641 {
15642 // Get the node
15644
15645 // Now check whether the current node lies on a shared boundary
15646 // within any other processor
15647
15648 // loop over the processors
15649 for (unsigned iproc = 0; iproc < nproc; iproc++)
15650 {
15651 // The number of boundaries shared with the current processor
15652 // (if iproc==my_rank then there are no shared boundaries
15653 // between them)
15654 const unsigned n_shd_bnd_iproc =
15655 this->nshared_boundaries(my_rank, iproc);
15656
15657 // There are no info. with myself
15658 if (iproc != my_rank && n_shd_bnd_iproc > 0)
15659 {
15660 // Get the boundaries ids of the shared boundaries with
15661 // iproc processor
15663 this->shared_boundaries_ids(my_rank, iproc);
15664
15665 // Loop over shd bnds with processor "iproc"
15666 for (unsigned isb = 0; isb < n_shd_bnd_iproc; isb++)
15667 {
15668 const unsigned shd_bnd_id = shd_bnd_ids[isb];
15669 const unsigned n_ele_shd_bnd =
15670 this->nshared_boundary_element(shd_bnd_id);
15671
15672 // Check if the node is on this boundary only if there are
15673 // elements on it
15674 if (n_ele_shd_bnd > 0 &&
15675 this->is_node_on_shared_boundary(shd_bnd_id, node_pt))
15676 {
15677 // Add the element into those that have a
15678 // node on the current shared boundary
15680
15681 } // Are there elements on the boundary and the node lies
15682 // on this boundary
15683
15684 } // for (isb < n_shd_bnd_iproc)
15685
15686 } // if (iproc != my_rank && n_shd_bnd_iproc > 0)
15687
15688 } // for (iproc < nproc)
15689
15690 } // for (n < n_nodes)
15691
15692 } // for (e < n_ele)
15693
15694 // -------------------------------------------------------------------
15695 // END: Identify the elements of the mesh that have nodes on the
15696 // shared boundaries
15697 // -------------------------------------------------------------------
15698
15699 // -------------------------------------------------------------------
15700 // BEGIN: Create the halo(ed) elements. Loop over the processors and
15701 // the shared boundaries within each processor. Get the elements on
15702 // the shared boundaries, mark them as haloed in this processor and
15703 // as halo on the element that will receive the info.
15704 // -------------------------------------------------------------------
15705
15706 // ********************************************************************
15707 // General strategy:
15708 // 1) Go through all the elements on the shared boundaries, mark these
15709 // elements as haloed, same as their nodes.
15710 // 2) Package the info. of the nodes and the elements.
15711 // 3) Send and receive the info across processors
15712 // 4) Unpackage it and create halo elements and nodes as indicated by
15713 // the received info.
15714 // ********************************************************************
15715
15716 // Keep track of the currently created nodes within each
15717 // processor. We need to keep track of these nodes so they can be
15718 // referred at a second stage.
15720
15721 // Get the time to re-generate halo(ed) elements/nodes (first stage)
15724
15725 // Go through all processors
15726 for (unsigned iproc = 0; iproc < nproc; iproc++)
15727 {
15728 // Send and receive info. to/from other processors
15729 if (iproc != my_rank)
15730 {
15731 // Get the number of boundaries shared with the send proc (iproc)
15732 const unsigned nshared_boundaries_with_iproc =
15733 this->nshared_boundaries(my_rank, iproc);
15734
15736 {
15737 // ******************************************************************
15738 // Stage 1
15739 // ******************************************************************
15740 // Step (1) Mark the elements adjacent to the shared boundaries as
15741 // haloed, mark the nodes on these elements as haloed nodes
15742 // Step (2) Create packages of information indicating the generation
15743 // of halo elements and nodes
15744 // ******************************************************************
15745
15746 // Clean send and receive buffers
15747 Flat_packed_unsigneds.clear();
15748 Flat_packed_doubles.clear();
15749#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
15750 Flat_packed_unsigneds_string.clear();
15751#endif
15752
15753 // Get the boundaries ids shared with "iproc"
15755 bound_shared_with_iproc = this->shared_boundaries_ids(my_rank, iproc);
15756
15757 // Loop over shared boundaries with processor "iproc"
15758 for (unsigned bs = 0; bs < nshared_boundaries_with_iproc; bs++)
15759 {
15760 const unsigned bnd_id = bound_shared_with_iproc[bs];
15761 // DEBP(bnd_id);
15762 const unsigned nel_bnd = this->nshared_boundary_element(bnd_id);
15763 // DEBP(nel_bnd);
15764
15765 // Container to store the elements marked as haloed
15767
15768 // All the elements adjacent to the boundary should be
15769 // marked as haloed elements
15770 if (nel_bnd > 0)
15771 {
15772 // Map to know which element have been already added
15773 std::map<FiniteElement*, bool> already_added;
15774
15775 // Loop over the elements adjacent to boundary "bnd_id"
15776 for (unsigned e = 0; e < nel_bnd; e++)
15777 {
15778 // Get pointer to the element adjacent to boundary bnd_id
15780 this->shared_boundary_element_pt(bnd_id, e);
15781
15782 // Check if the element has been already added. Elemets
15783 // are repeated if they have two faces on the shared
15784 // boundary
15785 if (!already_added[ele_pt])
15786 {
15787 // Add the element to the container of haloed elements
15788 haloed_element.push_back(ele_pt);
15789 // Mark the element as already added
15790 already_added[ele_pt] = true;
15791 }
15792
15793 } // for (e < nel_bnd)
15794
15795 // In addition to the elements on the boundary we also
15796 // need to mark (as haloed) any element on the mesh with a
15797 // node on the shared boundary
15798
15799 // Get the number of elements with a node on the current
15800 // shared boundary
15801 const unsigned n_ele_with_node_on_shd_bnd =
15803 // loop and add the elements that have a node on the
15804 // current shared boundary with the current processor
15805 for (unsigned iele = 0; iele < n_ele_with_node_on_shd_bnd; iele++)
15806 {
15807 // Get the element
15810 // Check if it has not been already added
15811 if (!already_added[ele_pt])
15812 {
15813 // Add it!!
15814 haloed_element.push_back(ele_pt);
15815 // Mark it as done
15816 already_added[ele_pt] = true;
15817 } // if (!already_added[ele_pt])
15818
15819 } // for (iele < n_ele_with_node_on_shd_bnd)
15820
15821 } // if (nel_bnd > 0)
15822
15823 // Get the total number of haloed elements
15824 const unsigned nhaloed_ele = haloed_element.size();
15825 // DEBP(nhaloed_ele);
15826 // DEBP(my_rank);
15827 // DEBP(iproc);
15828 // The very first data of the flat packed is the number of haloed
15829 // elements, this will be the number of halo element to create on
15830 // the receiver processor
15831 Flat_packed_unsigneds.push_back(nhaloed_ele);
15832#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
15833 std::stringstream junk;
15834 junk << "Number of haloed elements " << nhaloed_ele;
15835 Flat_packed_unsigneds_string.push_back(junk.str());
15836#endif
15837
15838 // Loop over the marked haloed elements
15839 for (unsigned e = 0; e < nhaloed_ele; e++)
15840 {
15841 // Get pointer to the marked haloed element
15843 const unsigned nroot_haloed_ele =
15844 this->nroot_haloed_element(iproc);
15845
15846 // Check if the element has been already added to the
15847 // halo(ed) scheme
15849 const unsigned haloed_ele_index =
15850 this->try_to_add_root_haloed_element_pt(iproc, gen_ele_pt);
15851
15852 // Was the element added or only returned the index of the
15853 // element
15855 {
15856 Flat_packed_unsigneds.push_back(1);
15857#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
15858 Flat_packed_unsigneds_string.push_back(
15859 "Haloed element needs to be constructed");
15860#endif
15861
15862 // Get additional info. related with the haloed element
15863 get_required_elemental_information_helper(iproc, ele_pt);
15864
15865 // Get the nodes on the element
15866 const unsigned nnodes = ele_pt->nnode();
15867 for (unsigned j = 0; j < nnodes; j++)
15868 {
15870
15871 // Package the info. of the nodes
15872 // The destination processor goes in the arguments
15873 add_haloed_node_helper(iproc, node_pt);
15874
15875 } // for (j < nnodes)
15876 } // add the element and send its nodes
15877 else // The haloed element already exists
15878 {
15879 Flat_packed_unsigneds.push_back(0);
15880#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
15881 Flat_packed_unsigneds_string.push_back(
15882 "Haloed element already exists");
15883#endif
15884 Flat_packed_unsigneds.push_back(haloed_ele_index);
15885#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
15886 Flat_packed_unsigneds_string.push_back(
15887 "Index of existing haloed element");
15888#endif
15889 } // else (next_haloed_ele == external_haloed_ele_index)
15890 } // for (e < nel_bnd)
15891
15892 } // for (bs < nshared_boundaries_with_iproc)
15893
15894 // *******************************************************************
15895 // Stage (2)
15896 // *******************************************************************
15897 // Step (1) Send and receive the data to create halo elements and
15898 // nodes
15899 // *******************************************************************
15900 // The processor to which send the elements
15901 int send_proc = static_cast<int>(iproc);
15902 // The processor from which receive the elements
15903 int recv_proc = static_cast<int>(iproc);
15904 send_and_receive_elements_nodes_info(send_proc, recv_proc);
15905
15906 // *******************************************************************
15907 // Stage (3)
15908 // *******************************************************************
15909 // Step (1) Unpackage the info and create the halo elements and nodes
15910 // *******************************************************************
15911
15912 // Reset the counters
15913 Counter_for_flat_packed_doubles = 0;
15914 Counter_for_flat_packed_unsigneds = 0;
15915
15916 // Loop over shared boundaries with processor "iproc"
15917 for (unsigned bs = 0; bs < nshared_boundaries_with_iproc; bs++)
15918 {
15919 // Get the number of halo element to be created
15920 const unsigned nhaloed_ele =
15921 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
15922
15923#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
15925 << "Rec:" << Counter_for_flat_packed_unsigneds
15926 << " Number of elements need to be constructed "
15927 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
15928 << std::endl;
15929#endif
15930
15931 // Loop over boundaries shared with processor "urecv_proc"
15932 for (unsigned e = 0; e < nhaloed_ele; e++)
15933 {
15934 // Create halo element from received info. of "iproc"
15935 // processor on the current processor
15936 create_halo_element(iproc,
15942
15943 } // for (e < nhaloed_ele)
15944
15945 } // for (bs < nshared_boundaries_with_iproc)
15946
15947 } // if (nshared_bound_recv_proc > 0)
15948
15949 } // if (iproc != my_rank)
15950
15951 } // for (iproc < nproc) (general loop to send and receive info.)
15952
15953 if (Print_timings_level_adaptation > 1)
15954 {
15955 // Get the time to re-generate halo(ed) elements/nodes (first stage)
15959
15960 oomph_info << "CPU for re-generating halo(ed) elements/nodes "
15961 << "(first stage) [n_ele=" << n_ele << "]: "
15963 << std::endl;
15964 }
15965
15966 // -------------------------------------------------------------------
15967 // END: Create the halo(ed) elements. Loop over the processors and
15968 // the shared boundaries within each processor. Get the elements on
15969 // the shared boundaries, mark them as haloed in this processor and
15970 // as halo on the element that will receive the info.
15971 // -------------------------------------------------------------------
15972
15973 // -------------------------------------------------------------------
15974 // BEGIN: Create any additional haloed element, those that dont lie
15975 // on a shared boundary but that shared a node with other processor
15976 // -------------------------------------------------------------------
15977
15978 // Get the time to re-generate halo(ed) elements/nodes (second stage)
15981
15982 // Create any additional halo(ed) elements between processors that
15983 // have no shared boundaries but that have shared nodes
15984 reset_halo_haloed_scheme_helper(other_proc_shd_bnd_node_pt,
15989
15990 if (Print_timings_level_adaptation > 1)
15991 {
15992 // Get the time to re-generate halo(ed) elements/nodes (second stage)
15996
15997 oomph_info << "CPU for re-generating halo(ed) elements/nodes "
15998 << "(second stage) [n_ele=" << n_ele << "]: "
16000 << std::endl;
16001 }
16002
16003 // -------------------------------------------------------------------
16004 // END: Create any additional haloed element, those that dont lie on
16005 // a shared boundary but that shared a node with other processor
16006 // -------------------------------------------------------------------
16007
16008 // Clean send and receive buffers
16009 Flat_packed_unsigneds.clear();
16010 Flat_packed_doubles.clear();
16011#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
16012 Flat_packed_unsigneds_string.clear();
16013#endif
16014
16015 // Document the timings for reseting halo and haloed scheme (without
16016 // classification of halo and haloed nodes)
16017 if (Print_timings_level_adaptation > 1)
16018 {
16020 oomph_info << "CPU for resetting halo-haloed scheme (without "
16021 "classification of halo and haloed nodes): "
16022 << tt_end - tt_start << std::endl;
16023 }
16024
16025 // ------------------------------------------------------------------
16026 // BEGIN: Classify halo(ed) elements and nodes
16027 // ------------------------------------------------------------------
16028 const bool report_stats = true;
16030 tmp_doc_info.disable_doc();
16031
16032 // Classify nodes
16033 this->classify_halo_and_haloed_nodes(tmp_doc_info, report_stats);
16034
16035 // Document the timings for reseting halo and haloed scheme (with
16036 // classification of halo and haloed nodes)
16037 if (Print_timings_level_adaptation > 1)
16038 {
16040 oomph_info << "CPU for resetting halo-haloed scheme (with classification "
16041 "of halo and haloed nodes): "
16042 << tt_end - tt_start << std::endl;
16043 }
16044
16045 // ------------------------------------------------------------------
16046 // END: Classify halo(ed) elements and nodes
16047 // ------------------------------------------------------------------
16048 }
16049
16050 //======================================================================
16051 // Compute the alias of the nodes on shared boundaries in this
16052 // (my_rank) processor with other processors. Also compute the alias
16053 // of nodes on shared boundaries of other processors with other
16054 // processors (useful when there is an element that requires to be
16055 // sent to this (my_rank) processor because there is a shared node
16056 // between this (my_rank) and other processors BUT there is not a
16057 // shared boundary between this and the other processor
16058 // ======================================================================
16059 template<class ELEMENT>
16062 Vector<Vector<Vector<std::map<unsigned, Node*>>>>&
16065 std::map<Vector<unsigned>, unsigned>& node_name_to_global_index,
16067 {
16068 // Get the number of processors
16069 const unsigned nproc = this->communicator_pt()->nproc();
16070 // Get the rank of the current processor
16071 const unsigned my_rank = this->communicator_pt()->my_rank();
16072 // Get the communicator of the mesh
16073 OomphCommunicator* comm_pt = this->communicator_pt();
16074
16075 // ---------------------------------------------------------------
16076 // BEGIN: Get the elements adjacent to shared boundaries and give
16077 // a unique node number to the nodes on the shared boundaries in
16078 // this processor
16079 // ---------------------------------------------------------------
16080
16081 // Counter for the nodes on shared boundaries in this (my_rank)
16082 // processor
16083 unsigned counter_nodes = 0;
16084 // Keep track of visited nodes
16085 std::map<Node*, bool> done_node;
16086 // ... and its local node number
16087 std::map<Node*, unsigned> local_node_number;
16088 // ... and the inverted relation from local node number to node_pt
16090
16091 // Stores the j-th node name associated with the i-th local node
16092 // on shared boundaries in this processor (my_rank)
16093 // local_node_names[i][j][0] = my_rank (this processor)
16094 // local_node_names[i][j][1] = iproc (the processor with which there
16095 // is a shared boundary)
16096 // local_node_names[i][j][2] = the shared boundary id between this
16097 // (my_rank) processor and iproc
16098 // processor
16099 // local_node_names[i][j][3] = the node index on the shared boundary
16100 // local_node_names[i][j][4] = the local node index (i). This may
16101 // be unnecessary since we alread know the
16102 // index but we also send this info. to
16103 // the root processor that is why we store
16104 // them here
16106
16107 // loop over the processors
16108 for (unsigned iproc = 0; iproc < nproc; iproc++)
16109 {
16110 // There are not shared boundaries with myself
16111 if (iproc != my_rank)
16112 {
16113 // Get the number of shared boundaries with iproc
16114 const unsigned n_shd_bnds_with_iproc =
16115 this->nshared_boundaries(my_rank, iproc);
16116
16117 // Get the boundaries ids shared with iproc
16119 this->shared_boundaries_ids(my_rank, iproc);
16120
16121 // Loop over the shared boundaries with processor iproc
16122 for (unsigned ishd = 0; ishd < n_shd_bnds_with_iproc; ishd++)
16123 {
16124 // Keep track of visited nodes with this shared boundary
16125 std::map<Node*, bool> done_node_shd_bnd;
16126 // The boundary id
16128 // Get the number of element on the shared boundary
16129 const unsigned n_shd_bnd_ele =
16130 this->nshared_boundary_element(shd_bnd_id);
16131
16132 // loop over the elements adjacent to the shared boundary
16133 for (unsigned e = 0; e < n_shd_bnd_ele; e++)
16134 {
16135 // Get the element
16137 this->shared_boundary_element_pt(shd_bnd_id, e);
16138
16139 // Get the number of nodes on the element
16140 const unsigned n_nodes = ele_pt->nnode();
16141
16142 // loop over the nodes of the current element
16143 for (unsigned n = 0; n < n_nodes; n++)
16144 {
16145 // Get the node
16147
16148 // Has the node been visited with this shared boundary?
16149 // And, is this a node on the shd_bnd_id shared boundary
16150 // with processor iproc?
16151 if (!done_node_shd_bnd[node_pt] &&
16152 this->is_node_on_shared_boundary(shd_bnd_id, node_pt))
16153 {
16154 // Mark the done as done with this shared boundary
16155 done_node_shd_bnd[node_pt] = true;
16156
16157 // Get the index of the node on the shared boundary
16158 // -------------------------------------------------
16159 // Get the number of nodes on the shared boundary
16160 const unsigned n_nodes_shd_bnd =
16161 nsorted_shared_boundary_node(shd_bnd_id);
16162
16163 // The index
16164 unsigned index = 0;
16165
16166#ifdef PARANOID
16167 // Flag to know if the node has been found
16168 bool found_node_on_shared_boundary = false;
16169#endif
16170 // Loop over the nodes on the shared boundary to find
16171 // the node
16172 for (unsigned k = 0; k < n_nodes_shd_bnd; k++)
16173 {
16174 // Get the k-th node on the shared boundary
16176 sorted_shared_boundary_node_pt(shd_bnd_id, k);
16177
16178 // Is the same node?
16179 if (shd_bnd_node_pt == node_pt)
16180 {
16181 // This is the index
16182 index = k;
16183#ifdef PARANOID
16184 // Mark as found
16186#endif
16187 break; // break
16188
16189 } // if (shd_bnd_node_pt == node_pt)
16190
16191 } // for (k < n_nodes_shd_bnd)
16192
16193#ifdef PARANOID
16195 {
16196 std::ostringstream error_message;
16197 error_message << "The index of the node on boundary ("
16198 << shd_bnd_id << ") was not found.\n"
16199 << "These are the node coordinates\n"
16200 << "(" << node_pt->x(0) << "," << node_pt->x(1)
16201 << ").\n";
16202 throw OomphLibError(error_message.str(),
16205 }
16206#endif
16207
16208 // Create the node name
16210 node_name[0] = my_rank;
16211 node_name[1] = iproc;
16212 node_name[2] = shd_bnd_id;
16213 node_name[3] = index;
16214 // The node number is filled in the following if/else
16215 // node_name[4] = ?;
16216
16217 // Has the node already been visited?
16218 if (!done_node[node_pt])
16219 {
16220 // If not ...
16221
16222 // Add the node to the local nodes
16223 local_node_pt.push_back(node_pt);
16224
16225 // Assign a local node number to the node
16227 // Store the local node number
16229 // Increase the counter of nodes
16230 counter_nodes++;
16231 // ... and mark it as visited
16232 done_node[node_pt] = true;
16233
16234 // Push back the node name (the first
16235 // one found for this node)
16239 }
16240 else
16241 {
16242 // If yes ...
16243
16244 // Get the local node number
16246
16247 // Store the local node number
16249
16250 // Push back the node name for the
16251 // node number
16253 }
16254
16255 } // Is on shared boundary?
16256
16257 } // for (n < nnodes)
16258
16259 } // for (e < n_shd_bnd_ele)
16260
16261 } // for (ishd < n_shd_bnds_with_iproc)
16262
16263 } // if (iproc != my_rank)
16264
16265 } // for (iproc < nproc)
16266
16267 // ---------------------------------------------------------------
16268 // END: Get the elements adjacent to shared boundaries and give
16269 // a unique node number to the nodes on the shared boundaries in
16270 // this processor
16271 // ---------------------------------------------------------------
16272
16273 // ---------------------------------------------------------------
16274 // BEGIN: Package the names of the local nodes
16275 // ---------------------------------------------------------------
16276 // Counter for the number of names of the nodes
16277 unsigned n_total_local_names = 0;
16278 // Get the number of local nodes
16279 const unsigned n_local_nodes = local_node_names.size();
16280 // loop over the number of local nodes and get the number of names
16281 // of each node
16282 for (unsigned i = 0; i < n_local_nodes; i++)
16283 {
16284 // Get the number of names of the i-th local node
16285 const unsigned n_inode_names = local_node_names[i].size();
16286 // ... and add them to the total number of local names
16288 } // for (i < n_local_nodes)
16289
16290 // We store five data per node name (my_rank,iproc,shd_bnd_id,idx,node#)
16291 // where node# is the node number on this processor (my_rank)
16292 const unsigned n_info_per_node_name = 5;
16293 // Storage for the flat package
16296 // A counter
16297 unsigned counter = 0;
16298 // loop over the local nodes
16299 for (unsigned i = 0; i < n_local_nodes; i++)
16300 {
16301 // Get the number of names of the i-th local node
16302 const unsigned n_inode_names = local_node_names[i].size();
16303 // loop over the names of the i-th local node
16304 for (unsigned j = 0; j < n_inode_names; j++)
16305 {
16306 // Store this processor id (my_rank)
16308 // Store the processor with which the shared boundary exist
16310 // Store the shared boundary id
16312 // Store the index of the node on the shared boundary
16314 // Store the local node number on this processor (my_rank)
16316 } // for (j < n_inode_names)
16317
16318 } // for (i < n_local_nodes)
16319
16320 // Reset the counter
16321 counter = 0;
16322
16323 // The number of data that will be sent to root from this
16324 // (my_rank) processor
16326
16327 // ---------------------------------------------------------------
16328 // END: Package the names of the local nodes
16329 // ---------------------------------------------------------------
16330 // ---------------------------------------------------------------
16331 // BEGIN: Send the data to the root processor
16332 // ---------------------------------------------------------------
16333
16334 // The root processor is in charge of computing all the node names
16335 // of the nodes on the shared boundaries
16336
16337 // Choose the root processor
16338 const unsigned root_processor = 0;
16339
16340 // The vector where the root processor receives how many names
16341 // will receive from the other processors
16343
16344 // Send the number of names that the root processor will receive
16345 // from each processor
16347 1,
16350 1,
16353 comm_pt->mpi_comm());
16354
16355 // Get the total number of data to receive from all processor in
16356 // root
16357 unsigned root_n_total_udata_receive = 0;
16359 for (unsigned iproc = 0; iproc < nproc; iproc++)
16360 {
16364 }
16365
16366 // Stores and compute the offsets (in root) for the data received
16367 // from each processor
16369 root_uoffsets_receive[0] = 0;
16370 for (unsigned iproc = 1; iproc < nproc; iproc++)
16371 {
16372 // Compute the offset to obtain the data from each processor
16375 }
16376
16377 // Create at least one entry so we don't get a seg fault below
16378 if (flat_packed_send_udata.size() == 0)
16379 {
16380 flat_packed_send_udata.resize(1);
16381 }
16382
16383 // Vector where to receive the info on root from all processors
16385 // Only root receive data, the others dont, then resize the
16386 // container to have at least one entry
16387 if (my_rank != root_processor)
16388 {
16389 // Create at least one entry so we don't get a seg fault below
16391 {
16393 }
16394 } // if (my_rank!=root_processor)
16395
16396 // Send the info. to the root processor
16397 MPI_Gatherv(&flat_packed_send_udata[0], // Flat package to send
16398 // info. from each
16399 // processor
16400 n_udata_send_to_root, // Total number of data send
16401 // from each processor to root
16403 &root_flat_packed_receive_udata[0], // Container where
16404 // to receive the
16405 // info. from all
16406 // processors
16407 &root_n_udata_to_receive[0], // Number of data to
16408 // receive from each
16409 // processor
16410 &root_uoffsets_receive[0], // The offset to store the
16411 // info. from each
16412 // processor
16414 root_processor, // The processor that receives all the
16415 // info.
16416 comm_pt->mpi_comm());
16417
16418 // Clear and resize the flat package to send
16419 flat_packed_send_udata.clear();
16420 flat_packed_send_udata.resize(0);
16421 // ---------------------------------------------------------------
16422 // END: Send the data to the root processor
16423 // ---------------------------------------------------------------
16424
16425 // Container where root stores the info. that will be sent to all
16426 // processors. This includes the number of global nodes, the
16427 // number of names for each global node and the names
16429
16430 // ---------------------------------------------------------------
16431 // BEGIN: Unpackage the info. received on root. Compute the alias
16432 // of the nodes
16433 // ---------------------------------------------------------------
16434 if (my_rank == root_processor)
16435 {
16436 // Compute all the names of a node
16437 // root_global_node_name[x][ ][ ] Global node number
16438 // root_global_node_name[ ][x][ ] Global node names
16439 // root_global_node_name[ ][ ][x] Global node info.
16441
16442 // Store the info. extracted from the flat package sent to
16443 // root
16444 // root_local_node_names[x][ ] Node name
16445 // root_local_node_names[ ][x] Node info
16447
16448 // Extract all the node names
16449 unsigned rcounter = 0;
16450 // loop over the processors
16451 for (unsigned iproc = 0; iproc < nproc; iproc++)
16452 {
16453 // Get the number of node names received from iproc
16455 for (unsigned i = 0; i < n_local_names_iproc; i++)
16456 {
16457 // Get the i-thnode name from iproc
16459 for (unsigned j = 0; j < n_info_per_node_name; j++)
16460 {
16462 }
16463
16464 // Add the i-th node name
16466
16467 } // for (i < n_local_names_iproc)
16468
16469 } // for (iproc < nproc)
16470
16471 // Get the number of node names received
16473
16474 // For each name of the node identify the position of its
16475 // counter-part
16476
16477 // Given a node name on the iproc,
16478 // (iproc, jproc, ishd_bnd, idx, local_node_number1)
16479 // its counter part must live in jproc, so we look for the
16480 // node name
16481 // (jproc, iproc, ishd_bnd, idx, local_node_number2)
16482
16483 // Store the index of the node name counter-part
16485
16486 // Keep track of the names of nodes already done
16487 std::map<Vector<unsigned>, bool> done_name;
16488
16489 // loop over the names of the nodes received from all
16490 // processors
16491 for (unsigned i = 0; i < n_root_local_node_names; i++)
16492 {
16493 // Get the i-th node name
16495
16496 // Check if this name node has been already done
16497 if (!done_name[node_name])
16498 {
16499 // Mark it as done
16500 done_name[node_name] = true;
16501#ifdef PARANOID
16502 // Flag to indicate the counter-part name node was
16503 // found
16504 bool found_both_names_node = false;
16505#endif
16506 // Find the counter-part name node (start from j+1
16507 // since all previous have been found, otherwise we
16508 // would not be here)
16509 for (unsigned j = i + 1; j < n_root_local_node_names; j++)
16510 {
16512
16513 // Check if this name node has been already done
16514 if (!done_name[node_name_r])
16515 {
16516 // Check whether this node is the
16517 // counter-part of the current name node
16518 if (node_name[0] == node_name_r[1] &&
16519 node_name[1] == node_name_r[0] &&
16520 node_name[2] == node_name_r[2] &&
16521 node_name[3] == node_name_r[3])
16522 {
16523 // Mark the name as node
16524 done_name[node_name_r] = true;
16525 // Store the index of the counter-part of
16526 // the current node name
16528 // ... and indicate the current node name
16529 // as the index of the counter-part
16531#ifdef PARANOID
16532 // The node has been found
16533 found_both_names_node = true;
16534#endif
16535 // Break the loop to find the
16536 // counter-part
16537 break;
16538 }
16539
16540 } // if (!done_name[node_name_r])
16541
16542 } // for (j < n_root_local_node_names)
16543#ifdef PARANOID
16544 // Check whether the node counter-part was found
16546 {
16547 std::ostringstream error_message;
16548 error_message << "The counter-part of the current name node was "
16549 << "not found,\nthe current node name is:\n"
16550 << "iproc:(" << node_name[0] << ")\n"
16551 << "jproc:(" << node_name[1] << ")\n"
16552 << "ishd_bnd:(" << node_name[2] << ")\n"
16553 << "index:(" << node_name[3] << ")\n";
16554 throw OomphLibError(error_message.str(),
16557 } // if (!found_both_names_node)
16558#endif
16559
16560 } // if (!done_name[node_name])
16561
16562 } // for (i < n_root_local_node_names)
16563
16564 // -----------------------------------------------------------
16565 // Look for all the names of each node received and store them
16566 // in the "global node names" container
16567
16568 // Keep track of the names of nodes already done
16569 done_name.clear();
16570 // loop over the names of the nodes received from all
16571 // processors
16572 for (unsigned i = 0; i < n_root_local_node_names; i++)
16573 {
16574 // Get the i-th node name
16576
16577 // Check if this name node has been already done
16578 if (!done_name[node_name])
16579 {
16580 // Store all the names of the current node
16582
16583 // Add the name of the node as the initial node name
16584 all_node_names.push_back(node_name);
16585
16586 // Get the index of the counter-part
16587 unsigned idx_c = node_name_counter_part[i];
16588 // Get the counter-part of the node name
16590
16591 // Add the name of the counter-part of the node
16592 all_node_names.push_back(node_name_r);
16593 // We do not mark it as done since we are interested in
16594 // the names that the counter-part may generate
16595
16596 // Get the number of names for the current node (two at
16597 // the first time)
16598 unsigned n_current_names = all_node_names.size();
16599 // Counter to ensure to visit all the names of the current
16600 // node
16601 unsigned icounter = 0;
16602
16603 // Visit all the names of the current node
16604 while (icounter < n_current_names)
16605 {
16606 // Get the current node name
16608
16609 // Search for other names for the current name of the
16610 // node, but first check if this has been already
16611 // visited
16613 {
16614 // Mark it as done
16616
16617 // loop over the names of the nodes (start from the
16618 // j+1 position, all previous node names have all
16619 // their names already assigned)
16620 for (unsigned j = i + 1; j < n_root_local_node_names; j++)
16621 {
16622 // Get the j-th node name
16624
16625 // Is this name node already done
16627 {
16628 // Is this another name for the current name node?
16629 if ((current_node_name[0] == other_node_name[0]) &&
16631 {
16632 // Mark it as done. If we search again using the
16633 // "other_node_name" as the current node name we
16634 // are not going to find new nodes to add
16635 done_name[other_node_name] = true;
16636 // Before adding it check that it is not already
16637 // part of the names of the node
16639 std::find(all_node_names.begin(),
16640 all_node_names.end(),
16642 if (it == all_node_names.end())
16643 {
16645 // Get the index of the counter-part
16646 unsigned k = node_name_counter_part[j];
16647 // Get the counter-part of the node name
16650 // Add the name of the counter-part of the
16651 // node only if it has not been previously
16652 // done
16654 {
16656 }
16657 }
16658
16659 } // // Is this another name for the current name
16660 // node?
16661
16662 } // if (!done_name[other_node_name])
16663
16664 } // for (j < n_root_local_node_names)
16665
16666 } // if (!done_name[current_node_name])
16667
16668 // Get the number of names
16670 // Increase the icounter to indicate we have visited the
16671 // current name of the node
16672 icounter++;
16673
16674 } // while(icounter < n_current_names)
16675
16676 // We now have all the names for the i-th global node
16678
16679 } // if (!done_name[node_name])
16680
16681 } // for (i < n_root_local_node_names)
16682
16683 // -------------------------------------------------------------
16684 // Prepare the info to be sent to all processors. The number
16685 // of global nodes, the number of names for each global node,
16686 // and their respective names
16687 // -------------------------------------------------------------
16688
16689 // Clear the container
16691 // Get the number of global nodes
16692 const unsigned n_global_nodes = root_global_node_names.size();
16693 // ... and store this info. to be sent from root to all
16694 // processors
16696
16697 // loop over the nodes
16698 for (unsigned i = 0; i < n_global_nodes; i++)
16699 {
16700 // Get the names of the i-th global node
16702 // Get the number of names for the i-th global node
16703 const unsigned n_names_global_inode = global_inode_names.size();
16704 // ... and store this info. to be sent from root to all
16705 // processors
16707 // loop over the names of the global i-th node
16708 for (unsigned j = 0; j < n_names_global_inode; j++)
16709 {
16710 // loop over the info. associated with each name
16711 for (unsigned k = 0; k < n_info_per_node_name; k++)
16712 {
16713 // Store the name info. of the current name in the
16714 // container to be sent from root to all processors
16717 } // for (k < n_info_per_node_name)
16718
16719 } // for (j < n_names_inode)
16720
16721 } // for (i < n_global_nodes)
16722
16723 } // if (my_rank == root_processor)
16724
16725 // ----------------------------------------------------------------
16726 // END: Unpackage the info. received on root. Compute the alias
16727 // of the nodes and prepare the info. to be sent back from
16728 // root to all processors
16729 // ----------------------------------------------------------------
16730
16731 // ---------------------------------------------------------------
16732 // BEGIN: Send the info. back to all processors, unpackage the
16733 // info. and create the map from node name to global node
16734 // index
16735 // ---------------------------------------------------------------
16736 // The number of data that root send to other processors.
16739
16740 MPI_Bcast(&root_n_udata_sent_to_all_proc, // Data to send and
16741 // receive
16742 1,
16745 comm_pt->mpi_comm());
16746
16747 // Resize the container if this is a processor that receives data
16748 if (my_rank != root_processor)
16749 {
16751 }
16752
16753 // Send the info. from root and receive it on all processors
16755 // from root to
16756 // all
16757 // processors
16758 root_n_udata_sent_to_all_proc, // Number of data sent
16759 // from root to each
16760 // procesor
16762 root_processor, // The processor that sends all the info.
16763 comm_pt->mpi_comm());
16764
16765 // Counter to extract the info.
16766 counter = 0;
16767 // Read the number of global nodes
16768 const unsigned n_global_nodes =
16770 // Store the global names of the nodes
16771 // global_node_name[x][ ][ ] Global node number
16772 // global_node_name[ ][x][ ] Global node names
16773 // global_node_name[ ][ ][x] Global node info.
16774 // Vector<Vector<Vector<unsigned> > > global_node_names(n_global_nodes);
16775 // Resize the input vector
16777 // Now loop until all global nodes info. has been read
16778 unsigned n_read_global_nodes = 0;
16780 {
16781 // Read the number of names for the current global node
16782 const unsigned n_names_global_inode =
16784 // Counter for the global node
16785 const unsigned i = n_read_global_nodes;
16786 // Resize the container
16788 // loop over the names of the global inode
16789 for (unsigned j = 0; j < n_names_global_inode; j++)
16790 {
16791 // Resize the container
16793 // loop over the info. of the j-th node name of the i-th
16794 // global node
16795 for (unsigned k = 0; k < n_info_per_node_name; k++)
16796 {
16797 // Read the k-th node info. from the j-th node name of
16798 // the i-th global node
16799 global_node_names[i][j][k] =
16801
16802 } // for (k < n_info_per_node_name)
16803
16804 // Create the map from the node name to the global node
16805 // index
16807 node_name[0] = global_node_names[i][j][0];
16808 node_name[1] = global_node_names[i][j][1];
16809 node_name[2] = global_node_names[i][j][2];
16810 node_name[3] = global_node_names[i][j][3];
16811 // Do not add the local index since it will not longer be
16812 // used. Additionally, we will not know the local node
16813 // index outside this method
16814 // node_name[4] = global_node_names[i][j][4];
16816
16817 } // for (j < n_names_global_inode)
16818
16819 // Increase the counter for read global nodes
16821
16822 } // while (n_read_global_nodes < n_global_nodes)
16823
16824#ifdef PARANOID
16825 // Check we have read all the info.
16827 {
16828 std::ostringstream error_stream;
16830 << "The info. received from root regarding the global names of "
16831 << "the nodes\nwas not completely read.\n"
16832 << "The number of data sent/received from root is: ("
16834 << "The number of data read from the received info. is: (" << counter
16835 << ")\n\n";
16836 throw OomphLibError(
16838 } // if (counter != root_n_udata_sent_to_all_proc)
16839#endif
16840
16841 // ---------------------------------------------------------------
16842 // END: Send the info. back to all processors, unpackage the info.
16843 // and create the map from node name to global node index
16844 // ---------------------------------------------------------------
16845
16846 // ---------------------------------------------------------------
16847 // BEGIN: Add the info. from the global node names into the
16848 // info. of the local node names. We do this because the
16849 // local node names have pointers to the nodes.
16850 // Additionally, create a map from the node name to the
16851 // index of its global node
16852 // ---------------------------------------------------------------
16853
16854 // Resize the global shared node pointers container
16856
16857 // loop over the number of global nodes
16858 for (unsigned i = 0; i < n_global_nodes; i++)
16859 {
16860 // Flag to indicate that the iglobal node is part of the nodes
16861 // on the current processor
16862 bool is_this_a_local_node_name = false;
16863 unsigned local_node_number;
16864 // Get the number of names of the i-th global node
16865 const unsigned n_names_global_inode = global_node_names[i].size();
16866 // loop over the names of the i-th global node
16867 for (unsigned j = 0; j < n_names_global_inode; j++)
16868 {
16869 // Get the node name info.
16870 const unsigned iproc = global_node_names[i][j][0];
16872
16873 // Check if this node name lives on this processor
16874 if (my_rank == iproc)
16875 {
16876 // The node is part of the local node names
16878 // Break
16879 break;
16880 } // if (my_rank == iproc)
16881
16882 } // for (j < n_names_global_inode)
16883
16884 // If the node is part of the local nodes then add the
16885 // additional names of the node in the local container
16887 {
16888#ifdef PARANOID
16889 // Check that the global node include at least all the names
16890 // of the node on this processor
16891 const unsigned n_names_local_node =
16894#endif
16895
16896 // Add the pointer of the node into the global shared node
16897 // pointers container
16899
16900 // Add all the global names of the node onto the local node
16901 // names
16902
16903 // loop again over the names of the i-th global node
16904 for (unsigned j = 0; j < n_names_global_inode; j++)
16905 {
16906 // Get the node name info.
16907 const unsigned iproc = global_node_names[i][j][0];
16908
16909 // Is this a node name on this processor?
16910 if (iproc != my_rank)
16911 {
16912 // Add the name
16915 }
16916#ifdef PARANOID
16917 else
16918 {
16919 const unsigned jproc = global_node_names[i][j][1];
16920 const unsigned ishd_bnd = global_node_names[i][j][2];
16921 const unsigned idx = global_node_names[i][j][3];
16922 const unsigned n_local_node = global_node_names[i][j][4];
16923 // loop over the names of the local node
16924 for (unsigned k = 0; k < n_names_local_node; k++)
16925 {
16926 if ((local_node_names[local_node_number][k][0] == iproc) &&
16931 {
16932 // Increase the number of local nodes found on the
16933 // global nodes
16935 } // found global node on local nodes
16936
16937 } // for (k < n_names_local_node)
16938
16939 } // if (iproc != my_rank)
16940#endif
16941
16942 } // for (j < n_names_global_inode)
16943
16944#ifdef PARANOID
16945 // The number of local nodes names must be the same as the the
16946 // number of global nodes names associated with this processor
16947 // (my_rank, that start with iproc = my_rank)
16949 {
16950 std::ostringstream error_stream;
16951 error_stream << "The local node names corresponding to the local "
16952 << "node (" << local_node_number << ") were\n"
16953 << "not found on the global node names.\n\n"
16954 << "These are the names of the local node\n"
16955 << "Name k: iproc, jproc, ishd_bnd, idx. #node\n";
16956 for (unsigned k = 0; k < n_names_local_node; k++)
16957 {
16958 error_stream << "Name(" << k
16959 << "): " << local_node_names[local_node_number][k][0]
16960 << ", " << local_node_names[local_node_number][k][1]
16961 << ", " << local_node_names[local_node_number][k][2]
16962 << ", " << local_node_names[local_node_number][k][3]
16963 << ", " << local_node_names[local_node_number][k][4]
16964 << "\n";
16965 }
16966
16967 error_stream << "\n\nThese are the names of the global node\n"
16968 << "Name k: iproc, jproc, ishd_bnd, idx. #node\n";
16969 for (unsigned k = 0; k < n_names_global_inode; k++)
16970 {
16971 error_stream << "Name(" << k << "): " << global_node_names[i][k][0]
16972 << ", " << global_node_names[i][k][1] << ", "
16973 << global_node_names[i][k][2] << ", "
16974 << global_node_names[i][k][3] << ", "
16975 << global_node_names[i][k][4] << "\n";
16976 }
16977
16978 throw OomphLibError(error_stream.str(),
16981 }
16982#endif
16983
16984 } // if (is_this_a_local_node_name)
16985
16986 } // for (i < n_global_nodes)
16987
16988 // ---------------------------------------------------------------
16989 // END: Add the info. from the global node names into the info.
16990 // of the local node names. We do this because the local
16991 // node names have pointers to the nodes
16992 // ---------------------------------------------------------------
16993
16994 // ---------------------------------------------------------------
16995 // BEGIN: Fill the data structure other_proc_shd_bnd_node_pt with
16996 // the local nodes.
16997 // ---------------------------------------------------------------
16998
16999 // Loop over the local nodes and fill the
17000 // other_proc_shd_bnd_node_pt container with the corresponding
17001 // info. NOTE: We are using the old size of the local node names,
17002 // before adding the names of the global nodes so we only loop
17003 // over the local nodes and not global.
17004
17005 // Compute the local shared boudary id
17006 const unsigned initial_shd_bnd_id = this->initial_shared_boundary_id();
17007
17008 // loop over the local nodes names
17009 for (unsigned i = 0; i < n_local_nodes; i++)
17010 {
17011 // Get the number of names for the i-th local node
17012 const unsigned n_names = local_node_names[i].size();
17013 // Get a pointer to the first name of the node found on this
17014 // processor (this ensures that the node lives on this
17015 // processor)
17017 // loop over the names of the i-th local node and add an entry
17018 // to the other_proc_shd_bnd_node_pt structure
17019 for (unsigned j = 0; j < n_names; j++)
17020 {
17021 // Get the node name info.
17022 const unsigned iproc = local_node_names[i][j][0];
17023 const unsigned jproc = local_node_names[i][j][1];
17024 const unsigned ishd_bnd =
17026 const unsigned index = local_node_names[i][j][3];
17027 // We can ignore the last entry, it was just used to compute
17028 // the global node number by the root processor
17029
17030 // Get the smallest processor number
17031 if (iproc < jproc)
17032 {
17034 }
17035 else
17036 {
17038 }
17039
17040 } // for (j < n_names)
17041
17042 } // for (i < n_local_node_names)
17043
17044 // ---------------------------------------------------------------
17045 // END: Fill the data structure other_proc_shd_bnd_node_pt with
17046 // the local nodes.
17047 // ---------------------------------------------------------------
17048 }
17049
17050 //======================================================================
17051 // Get the original boundaries to which is associated each
17052 // shared node, and send the info. to the related processors. We
17053 // need to do this so that at the reset of halo(ed) info. stage,
17054 // the info. is updated
17055 template<class ELEMENT>
17058 std::map<Vector<unsigned>, unsigned>& node_name_to_global_index,
17060 {
17061 // Get the rank and number of processors
17062 const unsigned nproc = this->communicator_pt()->nproc();
17063 const unsigned my_rank = this->communicator_pt()->my_rank();
17064
17065 // The number of nodes on shared boundaries
17066 const unsigned n_nodes_on_shd_bnds = global_node_names.size();
17067 // ---------------------------------------------------------
17068 // BEGIN: Get the shared nodes between each of processors
17069 // ---------------------------------------------------------
17070
17071 // Store the nodes on shared boundaries in this processor with other
17072 // processors
17074
17075 // A map to get access to the global shared node number from the
17076 // node pointer
17077 std::map<Node*, unsigned> node_pt_to_global_shd_bnd_index;
17078
17079 // loop over the global nodes names and get only those in this
17080 // processor
17081 for (unsigned i = 0; i < n_nodes_on_shd_bnds; i++)
17082 {
17083 // Get the number of names of the current node on shared
17084 // boundaries
17085 const unsigned n_names = global_node_names[i].size();
17086 // loop over the names
17087 for (unsigned j = 0; j < n_names; j++)
17088 {
17089 // Store the node name
17091 node_name[0] = global_node_names[i][j][0];
17092 node_name[1] = global_node_names[i][j][1];
17093 node_name[2] = global_node_names[i][j][2];
17094 node_name[3] = global_node_names[i][j][3];
17095
17096 // Check whether the node is in the current processor
17097 if (node_name[0] == my_rank)
17098 {
17099 // Check with which processor the node is shared
17100 const unsigned jproc = node_name[1];
17101
17102#ifdef PARANOID
17103 std::map<Vector<unsigned>, unsigned>::iterator it =
17105 if (it != node_name_to_global_index.end())
17106 {
17107 // Check whether the global node index correspond with that
17108 // of the current global node name
17109 if (i != (*it).second)
17110 {
17111 std::ostringstream error_message;
17112 error_message
17113 << "The global node number " << (*it).second
17114 << ") obtained from the current node\n"
17115 << "name is not the same as the current node number (" << i
17116 << ").\n\n"
17117 << "Node name:\n"
17118 << "iproc:" << node_name[0] << "\n"
17119 << "jproc:" << node_name[1] << "\n"
17120 << "shd_bnd_id:" << node_name[2] << "\n"
17121 << "index:" << node_name[3] << "\n\n";
17122 throw OomphLibError(error_message.str(),
17125 }
17126 }
17127 else
17128 {
17129 std::ostringstream error_message;
17130 error_message
17131 << "The node name is not registerd as living in this processor.\n"
17132 << "Node name:\n"
17133 << "iproc:" << node_name[0] << "\n"
17134 << "jproc:" << node_name[1] << "\n"
17135 << "shd_bnd_id:" << node_name[2] << "\n"
17136 << "index:" << node_name[3] << "\n\n";
17137 throw OomphLibError(error_message.str(),
17140 }
17141
17142#endif // #ifdef PARANOID
17143
17144 // Get the node pointer
17146
17147#ifdef PARANOID
17148 if (node_pt == 0)
17149 {
17150 std::ostringstream error_message;
17151 error_message << "There is not global shared node within this\n"
17152 << "global node number (" << i
17153 << "). The global shared\n"
17154 << "node pointer is null\n\n";
17155 throw OomphLibError(error_message.str(),
17158 }
17159#endif // #ifdef PARANOID
17160
17161 // Add the node to the nodes on shared boundaries in this
17162 // processor
17164
17165 // And store the global node index
17167
17168 } // if (node_name[0]==my_rank)
17169 else if (node_name[1] == my_rank)
17170 {
17171 // Check with which processor the node is shared
17172 const unsigned jproc = node_name[0];
17173
17174#ifdef PARANOID
17175 std::map<Vector<unsigned>, unsigned>::iterator it =
17177 if (it != node_name_to_global_index.end())
17178 {
17179 // Check whether the global node index correspond with that
17180 // of the current global node name
17181 if (i != (*it).second)
17182 {
17183 std::ostringstream error_message;
17184 error_message
17185 << "The global node number " << (*it).second
17186 << ") obtained from the current node\n"
17187 << "name is not the same as the current node number (" << i
17188 << ").\n\n"
17189 << "Node name:\n"
17190 << "iproc:" << node_name[0] << "\n"
17191 << "jproc:" << node_name[1] << "\n"
17192 << "shd_bnd_id:" << node_name[2] << "\n"
17193 << "index:" << node_name[3] << "\n\n";
17194 throw OomphLibError(error_message.str(),
17197 }
17198 }
17199 else
17200 {
17201 std::ostringstream error_message;
17202 error_message
17203 << "The node name is not registerd as living in this processor.\n"
17204 << "Node name:\n"
17205 << "iproc:" << node_name[0] << "\n"
17206 << "jproc:" << node_name[1] << "\n"
17207 << "shd_bnd_id:" << node_name[2] << "\n"
17208 << "index:" << node_name[3] << "\n\n";
17209 throw OomphLibError(error_message.str(),
17212 }
17213
17214#endif // #ifdef PARANOID
17215
17216 // Get the node pointer
17218
17219#ifdef PARANOID
17220 if (node_pt == 0)
17221 {
17222 std::ostringstream error_message;
17223 error_message << "There is not global shared node within this\n"
17224 << "global node number (" << i
17225 << "). The global shared\n"
17226 << "node pointer is null\n\n";
17227 throw OomphLibError(error_message.str(),
17230 }
17231#endif // #ifdef PARANOID
17232
17233 // Add the node to the nodes on shared boundaries in this
17234 // processor
17236
17237 // And store the global node index
17239 }
17240
17241 } // for (j < n_names)
17242
17243 } // for (i < n_nodes_on_shd_bnds)
17244
17245 // ---------------------------------------------------------
17246 // END: Get the shared nodes between each of processors
17247 // ---------------------------------------------------------
17248
17249 // ---------------------------------------------------------
17250 // BEGIN: Get the original boundaries associated to each
17251 // node on a shared boundary
17252 // ---------------------------------------------------------
17253
17254 // Store the global shared node number
17256 // Store the boundaries associated with the global shared node
17257 // number
17259 // Store the zeta boundary coordinate of the nodes on original
17260 // boundaries
17262
17263 // loop over the processors
17264 for (unsigned iproc = 0; iproc < nproc; iproc++)
17265 {
17266 // Get the nodes added to be shared with the iproc processor
17267 std::set<Node*> nodes_shared_pt = node_on_shd_bnd_pt[iproc];
17268
17269 // loop over the nodes
17270 for (std::set<Node*>::iterator it = nodes_shared_pt.begin();
17271 it != nodes_shared_pt.end();
17272 it++)
17273 {
17274 // Get the node
17275 Node* node_pt = (*it);
17276 // Store the boundaries on which it is stored
17278 // For each boundary get the corresponding z value of the node
17279 // on the boundary
17281 // Get the number of boudandaries
17282 const unsigned n_bnd = this->initial_shared_boundary_id();
17283 // loop over the boundaries and register the boundaries to which
17284 // it is associated
17285 for (unsigned bb = 0; bb < n_bnd; bb++)
17286 {
17287 // Is the node on original boundary bb?
17289 {
17290 // Then save it as being on boundary bb
17291 on_original_boundaries.push_back(bb);
17292 // Get the boundary coordinate
17295 // Save the boundary coordinate
17296 zeta_coordinate.push_back(zeta[0]);
17297 }
17298
17299 } // for (bb < n_bnd)
17300
17301 // Is the node on an original boundary
17302 if (on_original_boundaries.size() > 0)
17303 {
17304 // Get the global shared node number
17305 std::map<Node*, unsigned>::iterator it_index =
17307#ifdef PARANOID
17309 {
17310 std::ostringstream error_message;
17311 error_message
17312 << "We could not find the global shared node index associated\n"
17313 << "with the node pointer with vertices coordinates:\n"
17314 << "(" << node_pt->x(0) << ", " << node_pt->x(1) << ")\n\n";
17315 throw OomphLibError(error_message.str(),
17318 }
17319#endif
17320 // The global shared node index
17321 const unsigned global_shared_node_number = (*it_index).second;
17322 // Store the global shared node number
17325 // And store the original boundaries to which it is associated
17328 // and the corresponding zeta coordinate
17330 }
17331
17332 } // loop over nodes on shared boundaries with iproc
17333
17334 } // for (iproc < nproc)
17335
17336 // ---------------------------------------------------------
17337 // END: Get the original boundaries associated to each
17338 // node on a shared boundary
17339 // ---------------------------------------------------------
17340
17341 // ---------------------------------------------------------
17342 // BEGIN: Send the info. to the corresponding processors,
17343 // package the info, send it and receive it in the
17344 // corresponding processor, unpackage and set the
17345 // boundaries associated with the received nodes
17346 // ---------------------------------------------------------
17347
17348 // Get the communicator of the mesh
17349 OomphCommunicator* comm_pt = this->communicator_pt();
17350
17351 // Set MPI info
17354
17355 // loop over the processors
17356 for (unsigned iproc = 0; iproc < nproc; iproc++)
17357 {
17358 // The number of nodes shared between the pair of processors
17359 const unsigned n_shd_nodes_my_rank_iproc =
17361
17362 // Are there shared nodes between these pair of processors
17363 // (my_rank, iproc)? Also ensure not to send info. within myself
17365 {
17366 // The flat package to send the info, to the iproc processor
17368 // The very first entry is the number of nodes shared by the
17369 // pair of processors (my_rank, iproc)
17371
17372 // Get the number of shared nodes on original boundaries
17375
17376 // The second data is the number of shared nodes on original
17377 // boundaries
17380
17381 // ... also send the zeta coordinates associated with the
17382 // original boundaries
17384
17385 // loop over the nodes shared between this pair of processors
17386 for (unsigned i = 0; i < n_global_shared_node_on_original_boundary; i++)
17387 {
17388 // Get the global shared node index
17389 const unsigned global_shared_node_index =
17391
17392 // Put in the package the shared node index of the current
17393 // node
17395
17396 // Get the original boundaries to which the node is associated
17399
17400 // Get the associated zeta boundary coordinates
17403
17404 // Get the number of original boundaries to which the node is
17405 // associated
17407
17408 // Put in the package the number of original boundaries the
17409 // node is associated
17411
17412 // loop over the original boundaries ids and include them in
17413 // the package
17414 for (unsigned j = 0; j < n_original_boundaries; j++)
17415 {
17416 // Put in the package each of the original boundaries to
17417 // which it is associated
17419 // The zeta coordinate on the boundary
17421 } // for (j < n_original_boundaries)
17422
17423 } // for (i < n_global_shared_node_on_original_boundary)
17424
17425 // Send data UNSIGNED -----------------------------------------
17426 // Get the size of the package to communicate to the iproc
17427 // processor
17428 const unsigned n_udata_send = flat_package_unsigned_send.size();
17429 unsigned n_udata_send_int = n_udata_send;
17430
17431 // Send/receive data to/from iproc processor
17433 1,
17435 iproc,
17436 1,
17437 comm_pt->mpi_comm(),
17438 &request);
17439
17440 unsigned n_udata_received_int = 0;
17442 1,
17444 iproc,
17445 1,
17446 comm_pt->mpi_comm(),
17447 &status);
17449
17450 if (n_udata_send != 0)
17451 {
17455 iproc,
17456 2,
17457 comm_pt->mpi_comm(),
17458 &request);
17459 }
17460
17461 const unsigned n_udata_received =
17462 static_cast<unsigned>(n_udata_received_int);
17463
17464 // Where to receive the data from the iproc processor
17466
17467 if (n_udata_received != 0)
17468 {
17472 iproc,
17473 2,
17474 comm_pt->mpi_comm(),
17475 &status);
17476 }
17477
17478 if (n_udata_send != 0)
17479 {
17481 }
17482
17483 // Send data DOUBLE -----------------------------------------
17484 // Get the size of the package to communicate to the iproc
17485 // processor
17486 const unsigned n_ddata_send = flat_package_double_send.size();
17487 unsigned n_ddata_send_int = n_ddata_send;
17488
17489 // Send/receive data to/from iproc processor
17491 1,
17493 iproc,
17494 1,
17495 comm_pt->mpi_comm(),
17496 &request);
17497
17498 unsigned n_ddata_received_int = 0;
17500 1,
17502 iproc,
17503 1,
17504 comm_pt->mpi_comm(),
17505 &status);
17507
17508 if (n_ddata_send != 0)
17509 {
17512 MPI_DOUBLE,
17513 iproc,
17514 2,
17515 comm_pt->mpi_comm(),
17516 &request);
17517 }
17518
17519 const unsigned n_ddata_received =
17520 static_cast<unsigned>(n_ddata_received_int);
17521
17522 // Where to receive the data from the iproc processor
17524
17525 if (n_ddata_received != 0)
17526 {
17529 MPI_DOUBLE,
17530 iproc,
17531 2,
17532 comm_pt->mpi_comm(),
17533 &status);
17534 }
17535
17536 if (n_ddata_send != 0)
17537 {
17539 }
17540
17541 // Unpackage -------------------------------------------------
17542 // ... and associate the nodes to the corresponding original
17543 // boundaries
17544
17545 // The number of nodes to be received
17547
17548 // Increase and decrease the number of received shared nodes to
17549 // avoid the warning when compiling without PARANOID
17552
17553#ifdef PARANOID
17555 {
17556 std::ostringstream error_message;
17557 error_message
17558 << "The number of shared nodes between the pair of processors is\n"
17559 << "not the same\n"
17560 << "N.shared nodes proc (" << my_rank << ") with proc (" << iproc
17561 << "): (" << n_shd_nodes_my_rank_iproc << "\n"
17562 << "N.shared nodes proc (" << iproc << ") with proc (" << my_rank
17563 << "): (" << n_shared_nodes_received << "\n\n"
17564 << "You should have got the same error in proc: (" << iproc
17565 << ")\n\n";
17566 throw OomphLibError(error_message.str(),
17569 } // if (n_shd_nodes_my_rank_iproc != n_shared_nodes_received)
17570#endif
17571
17572 // Skip the number of nodes on shared boundaries on original
17573 // boundaries received (that is why next lines are commented)
17574
17575 // The number of nodes on shared boundaries on original
17576 // boundaries
17577 // const unsigned n_shared_nodes_on_original_boundaries_received =
17578 // flat_package_unsigned_receive[1];
17579
17580 // loop over the received info.
17581 unsigned current_index_data = 2;
17582 unsigned current_index_ddata = 0;
17584 {
17585 // The global shared node number
17586 const unsigned global_shared_node_index =
17588
17589 // The pointer to the node
17590 Node* node_pt = 0;
17591
17592 // The number of original boundaries the node is associated
17593 // with
17594 const unsigned n_original_boundaries =
17596
17597 // Get the node pointer
17599#ifdef PARANOID
17600 if (node_pt == 0)
17601 {
17602 std::ostringstream error_message;
17603 error_message
17604 << "The global shared node (" << global_shared_node_index << ") "
17605 << "could not be found in this processor!!!\n"
17606 << "However, it was found in processor (" << iproc << "). The "
17607 << "data may be no synchronised,\ntherefore "
17608 << "we may be looking for a global shared node number that "
17609 << "do not\ncorrespond with the one that was sent by "
17610 << "processor (" << iproc << ")\n\n";
17611 throw OomphLibError(error_message.str(),
17614 }
17615#endif // #ifdef PARANOID
17616
17617 // loop over the number of original boundaries and associate
17618 // the node to each of those boundaries
17619 for (unsigned i = 0; i < n_original_boundaries; i++)
17620 {
17621 // Get the original boundary to which the node is associated
17622 // with
17623 const unsigned original_bound_id =
17625
17626 // Associate the node with the boundary
17627 this->add_boundary_node(original_bound_id, node_pt);
17628
17629 // Get the zeta boundary coordinate
17633 }
17634
17635 } // while(current_data < n_data_received)
17636
17637 } // if ((node_on_shd_bnd_pt(iproc) > 0) && iproc!=my_rank)
17638
17639 } // for (iproc < nproc)
17640
17641 // ---------------------------------------------------------
17642 // END: Send the info. to the corresponding processors,
17643 // package the info, send it and receive it in the
17644 // corresponding processor, unpackage and set the
17645 // boundaries associated with the received nodes
17646 // ---------------------------------------------------------
17647 }
17648
17649 //======================================================================
17650 // In charge of creating additional halo(ed) elements on those
17651 // processors that have no shared boundaries in common but have
17652 // shared nodes
17653 // ======================================================================
17654 template<class ELEMENT>
17656 Vector<Vector<Vector<std::map<unsigned, Node*>>>>&
17660 std::map<Vector<unsigned>, unsigned>& node_name_to_global_index,
17662 {
17663 // Get the rank and number of processors
17664 const unsigned nproc = this->communicator_pt()->nproc();
17665 const unsigned my_rank = this->communicator_pt()->my_rank();
17666
17667 // ---------------------------------------------------------------
17668 // BEGIN: Create a map to check whether a node is on the global
17669 // shared nodes. Also set a map to obtain the global
17670 // shared node index (this index is the same as the global
17671 // node name)
17672 // ---------------------------------------------------------------
17673 std::map<Node*, bool> is_global_shared_node;
17674 std::map<Node*, unsigned> global_shared_node_index;
17675
17676 // Get the number of global shared nodes
17678 // loop over the global shared nodes
17679 for (unsigned i = 0; i < n_global_shared_nodes; i++)
17680 {
17681 // Get the node
17683 // Indicate this is a shared global node
17685 // Set the map to obtain the index of the global shared node
17687
17688 } // for (i < n_global_shared_nodes)
17689
17690 // ---------------------------------------------------------------
17691 // END: Create a map to check whether a node is on the global
17692 // shared nodes. Also set a map to obtain the global
17693 // shared node index (this index is the same as the global
17694 // node name)
17695 // ---------------------------------------------------------------
17696
17697 // ---------------------------------------------------------------
17698 // BEGIN: Loop over the haloed elements and check whether the nodes
17699 // on the haloed elements are part of the global shared
17700 // nodes. If that is the case then check whether the
17701 // element should be sent to the processors with which the
17702 // node is shared
17703 // ---------------------------------------------------------------
17704
17705 // Elements that may be sent to other processors
17707
17708 // loop over the processors
17709 for (unsigned iproc = 0; iproc < nproc; iproc++)
17710 {
17711 if (iproc != my_rank)
17712 {
17713 // Get the haloed element with iproc
17715 this->root_haloed_element_pt(iproc);
17716
17717 // Get the number of haloed elements
17718 const unsigned n_haloed_ele = this->nroot_haloed_element(iproc);
17719
17720 // loop over the haloed elements with iproc
17721 for (unsigned ihd = 0; ihd < n_haloed_ele; ihd++)
17722 {
17723 // A pointer to the generalised element
17725 // Get the finite element representation of the element
17726 FiniteElement* ele_pt = dynamic_cast<FiniteElement*>(gele_pt);
17727 // Get the number of nodes
17728 const unsigned n_nodes = ele_pt->nnode();
17729 // loop over the nodes of the element
17730 for (unsigned n = 0; n < n_nodes; n++)
17731 {
17732 // Get the node
17734 // Is the node a global shared node?
17736 {
17737 // Get the index of the global shared node
17739 // Get the global names of the node
17742
17743 // Get the number of names
17744 const unsigned n_names = iglobal_names.size();
17745 // loop over the names and check which processors share
17746 // this node (the processors to which the element may be
17747 // sent
17748 for (unsigned j = 0; j < n_names; j++)
17749 {
17750 // Get the processors to which the element should be
17751 // sent
17752 const unsigned proc1 = iglobal_names[j][0];
17753 const unsigned proc2 = iglobal_names[j][1];
17754 // Add the element to the set of additional elements to
17755 // sent from proc1 to proc2
17758
17759 } // for (j < n_names)
17760
17761 } // if (is_global_shared_node[node_pt])
17762
17763 } // for (n < n_nodes)
17764
17765 } // for (ihd < n_haloed_ele)
17766
17767 } // if (iproc!=my_rank)
17768
17769 } // for (iproc < nproc)
17770
17771 // ---------------------------------------------------------------
17772 // Now check whether the element should really be sent to the
17773 // indicated processors
17774
17775 // The elements from this (my_rank) processor that will be sent to
17776 // other processors
17778
17779 // loop over the processors
17780 for (unsigned iproc = 0; iproc < nproc; iproc++)
17781 {
17782 if (iproc != my_rank)
17783 {
17784 // Get the set of element that may be sent to the iproc
17785 // processor
17786 std::set<GeneralisedElement*> iproc_ele_pt =
17788 // loop over the element that may be sent to the iproc
17789 // processor
17790 for (std::set<GeneralisedElement*>::iterator it = iproc_ele_pt.begin();
17791 it != iproc_ele_pt.end();
17792 it++)
17793 {
17794 // Get a pointer to the element
17795 GeneralisedElement* gele_pt = (*it);
17796
17797 // Get the haloed element with iproc
17799 this->root_haloed_element_pt(iproc);
17800
17801 // Get the number of haloed elements
17802 const unsigned n_haloed_ele = this->nroot_haloed_element(iproc);
17803
17804 // Flag to indicate whether the element has been already sent
17805 // to the iproc processor
17806 bool send_ele_to_iproc_processor = true;
17807 // loop over the haloed elements with iproc and check whether
17808 // the element has been already sent to iproc (if it is
17809 // already a haloed element with iproc then it has been
17810 // already sent)
17811 for (unsigned ihd = 0; ihd < n_haloed_ele; ihd++)
17812 {
17813 // A pointer to the generalised element
17815 if (gele_pt == ghd_ele_pt)
17816 {
17817 // Mark the element as not required to be sent
17819 // Break the loop that searchs for the element on the
17820 // haloed elements with iproc
17821 break;
17822 }
17823
17824 } // for (ihd < n_haloed_ele)
17825
17826 // Do we need to sent the element?
17828 {
17829 // Get the finite element representation of the element
17830 FiniteElement* ele_pt = dynamic_cast<FiniteElement*>(gele_pt);
17831 // Add the element to those that will be sent to the iproc
17832 // processor
17833 send_haloed_ele_pt[iproc].push_back(ele_pt);
17834 }
17835
17836 } // loop over the elements that may be sent to the iproc
17837 // processor
17838
17839 } // if (iproc!=my_rank)
17840
17841 } // for (iproc < nproc)
17842
17843 // ---------------------------------------------------------------
17844 // END: Loop over the haloed element and check whether the nodes
17845 // on the haloed elements are part of the global shared
17846 // nodes. If that is the case then check whether the element
17847 // should be sent to the processors with which the node is
17848 // shared
17849 // ---------------------------------------------------------------
17850
17851 // ============================================================
17852 // Now send the additional elements
17853 // ============================================================
17854 // Loop over the processors to send data
17855 for (unsigned iproc = 0; iproc < nproc; iproc++)
17856 {
17857 // There are no elements to send with myself
17858 if (iproc != my_rank)
17859 {
17860 // Get the number of additional haloed elements to send
17861 const unsigned n_additional_haloed_ele =
17863
17864 // Clear send and receive buffers
17865 Flat_packed_unsigneds.clear();
17866 Flat_packed_doubles.clear();
17867#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
17868 Flat_packed_unsigneds_string.clear();
17869#endif
17870
17871 // The very first data of the flat packed is the number of
17872 // additional haloed elements, this will be the number of
17873 // additional halo elements to create on the receiver processor
17874 Flat_packed_unsigneds.push_back(n_additional_haloed_ele);
17875#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
17876 std::stringstream junk;
17877 junk << "Number of haloed elements " << nhaloed_ele;
17878 Flat_packed_unsigneds_string.push_back(junk.str());
17879#endif
17880
17881 // Loop over the additioanl haloed elements
17882 for (unsigned e = 0; e < n_additional_haloed_ele; e++)
17883 {
17884 // Get pointer to the additional haloed element
17886 const unsigned nroot_haloed_ele = this->nroot_haloed_element(iproc);
17887
17888 // Check if the element has been already added to the
17889 // halo(ed) scheme
17890
17891 // Get the generalised version of the element
17893 // Try to add the haloed element
17894 const unsigned haloed_ele_index =
17895 this->try_to_add_root_haloed_element_pt(iproc, gen_ele_pt);
17896
17897 // Was the element added or only returned the index of the
17898 // element
17900 {
17901 Flat_packed_unsigneds.push_back(1);
17902#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
17903 Flat_packed_unsigneds_string.push_back(
17904 "Haloed element needs to be constructed");
17905#endif
17906
17907 // Get additional info. related with the haloed element
17908 get_required_elemental_information_helper(iproc, ele_pt);
17909
17910 // Get the nodes on the element
17911 const unsigned nnodes = ele_pt->nnode();
17912 for (unsigned j = 0; j < nnodes; j++)
17913 {
17915
17916 // Package the info. of the nodes
17917 // The destination processor goes in the arguments
17918 add_haloed_node_helper(iproc, node_pt);
17919
17920 } // for (j < nnodes)
17921
17922 } // add the element and send its nodes
17923 else // The haloed element already exists
17924 {
17925 Flat_packed_unsigneds.push_back(0);
17926#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
17927 Flat_packed_unsigneds_string.push_back(
17928 "Haloed element already exists");
17929#endif
17930 Flat_packed_unsigneds.push_back(haloed_ele_index);
17931#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
17932 Flat_packed_unsigneds_string.push_back(
17933 "Index of existing haloed element");
17934#endif
17935 } // else (next_haloed_ele == external_haloed_ele_index)
17936
17937 } // for (e < n_additional_haloed_ele)
17938
17939 // Send and received the additional haloed elements (all
17940 // processors send and receive)
17941
17942 // The processor to which send the elements
17943 int send_proc = static_cast<int>(iproc);
17944 // The processor from which receive the elements
17945 int recv_proc = static_cast<int>(iproc);
17946 send_and_receive_elements_nodes_info(send_proc, recv_proc);
17947
17948 // Reset the counters
17949 Counter_for_flat_packed_doubles = 0;
17950 Counter_for_flat_packed_unsigneds = 0;
17951
17952 // Get the number of additional halo element to be created
17953 const unsigned n_additional_halo_ele =
17954 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
17955
17956#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
17957 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
17958 << " Number of elements need to be constructed "
17959 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
17960 << std::endl;
17961#endif
17962
17963 // Create the additional halo elements
17964 for (unsigned e = 0; e < n_additional_halo_ele; e++)
17965 {
17966 // Create halo element from received info. of "iproc"
17967 // processor on the current processor
17968 create_halo_element(iproc,
17974
17975 } // for (e < n_additional_halo_ele)
17976
17977 } // if (iproc != my_rank)
17978
17979 } // for (iproc < nproc)
17980 }
17981
17982 // *********************************************************************
17983 // Start communication functions
17984 // *********************************************************************
17985
17986 //========start of get_required_elemental_information_helper==============
17987 /// Helper function to get the required elemental information from
17988 /// an haloed element. This info. involves the association of the element
17989 /// to a boundary or region.
17990 //========================================================================
17991 template<class ELEMENT>
17993 ELEMENT>::get_required_elemental_information_helper(unsigned& iproc,
17995 {
17996 // Check if the element is associated with the original boundaries
17997 const unsigned nbound = this->initial_shared_boundary_id();
17998
17999 // ------------------------------------------------------------------
18000 // Stores the information regarding the boundaries associated to the
18001 // element (it that is the case)
18004
18005 unsigned counter_face_indexes = 0;
18006
18007 for (unsigned b = 0; b < nbound; b++)
18008 {
18009 // Get the number of elements associated to boundary i
18010 const unsigned nboundary_ele = nboundary_element(b);
18011 for (unsigned e = 0; e < nboundary_ele; e++)
18012 {
18013 if (ele_pt == this->boundary_element_pt(b, e))
18014 {
18015 // Keep track of the boundaries associated to the element
18016 associated_boundaries.push_back(b);
18017 // Get the face index
18018 face_index_on_boundary.push_back(face_index_at_boundary(b, e));
18020#ifdef PARANOID
18021 if (counter_face_indexes > 2)
18022 {
18023 std::stringstream error_message;
18024 error_message
18025 << "A triangular element can not have more than two of its faces "
18026 << "on a boundary!!!\n\n";
18027 throw OomphLibError(error_message.str(),
18030 }
18031#else
18032 // Already found 2 face indexes on the same boundary?
18033 if (counter_face_indexes == 2)
18034 {
18035 break;
18036 }
18037#endif // #ifdef PARANOID
18038
18039 } // if (ele_pt == this->boundary_element_pt(b,e))
18040
18041 } // (e < nboundary_ele)
18042
18043 } // (b < nbound)
18044
18045 // If the element is associated to any boundary then package all the
18046 // relevant info
18048 if (nassociated_boundaries > 0)
18049 {
18050 Flat_packed_unsigneds.push_back(1);
18051#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18052 Flat_packed_unsigneds_string.push_back(
18053 "The element is a boundary element");
18054#endif
18055 Flat_packed_unsigneds.push_back(nassociated_boundaries);
18056#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18057 std::stringstream junk;
18058 junk << "The elements is associated to " << nassociated_boundaries
18059 << " boundaries";
18060 Flat_packed_unsigneds_string.push_back(junk.str());
18061#endif
18062
18063 // Package the ids of the associated boundaries and the
18064 // corresponding face index for each boundary (if the element is a
18065 // corner element, it will have two faces associated to the
18066 // boundary)
18067 for (unsigned i = 0; i < nassociated_boundaries; i++)
18068 {
18069 unsigned b = associated_boundaries[i];
18070 Flat_packed_unsigneds.push_back(b);
18071#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18072 std::stringstream junk;
18073 junk << "Element associated to boundary " << b << " of "
18074 << nassociated_boundaries << " total associated boundaries";
18075 Flat_packed_unsigneds_string.push_back(junk.str());
18076#endif
18077 unsigned f = face_index_on_boundary[i];
18078 Flat_packed_unsigneds.push_back(f);
18079#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18080 std::stringstream junk2;
18081 junk2 << "Face index " << f << " for associated boundary " << b;
18082 Flat_packed_unsigneds_string.push_back(junk2.str());
18083#endif
18084 }
18085
18086 // If the element is associated to any boundary then we should
18087 // check if the mesh has regions, if that is the case then we need
18088 // to check to which region the boundary element does belong
18089
18090 // If the mesh has regions we should look for the element
18091 // associated to a boundary and a specified region
18094
18095 // Now check for the case when we have regions in the mesh
18096 const unsigned n_regions = this->nregion();
18097 if (n_regions > 1)
18098 {
18099 // Used to count the number of faces associated with
18100 // boundary-regions
18102 // Loop over the boundaries
18103 for (unsigned b = 0; b < nbound; b++)
18104 {
18105 // Go through each region by getting the region id
18106 for (unsigned i_reg = 0; i_reg < n_regions; i_reg++)
18107 {
18108 // Get thre region id associated with the (i_reg)-th region
18109 const unsigned region_id =
18110 static_cast<unsigned>(this->Region_attribute[i_reg]);
18111
18112 // Loop over all elements associated with the current boundary
18113 // and the i_reg-th region and check if the element is part of
18114 // any region
18115 const unsigned nele_in_region =
18116 this->nboundary_element_in_region(b, region_id);
18117 for (unsigned ee = 0; ee < nele_in_region; ee++)
18118 {
18119 // Check if the boundary-region element is the same as the
18120 // element
18121 if (ele_pt ==
18122 this->boundary_element_in_region_pt(b, region_id, ee))
18123 {
18124 // Storage for the boundary and region associated to the
18125 // element
18127
18128 // Keep track of the boundaries associated to the element
18129 bound_and_region[0] = b;
18130 // Keep track of the regions associated to the element
18132 // Add the boundaries and regions in the storage to be
18133 // sent to other processors
18135 // Get the face index and keep track of it
18137 this->face_index_at_boundary_in_region(b, region_id, ee));
18138
18139 // Increase the number of faces of the element associated
18140 // to boundary-regions
18142
18143#ifdef PARANOID
18145 {
18146 std::stringstream error_message;
18147 error_message << "A triangular element can not have more "
18148 "than two of its\n"
18149 << "faces on a boundary!!!\n\n";
18150 throw OomphLibError(error_message.str(),
18153 } // if (counter_face_indexes_in_regions > 2)
18154#endif
18155
18156 } // The element is a boundary-region element
18157
18158 } // for (ee < nele_in_region)
18159
18160 } // for (i_reg < n_regions)
18161
18162 } // for (b < nbound)
18163
18164 } // if (n_regions > 1)
18165
18166 // Now package the info. to be sent to other processors
18167 const unsigned nassociated_boundaries_and_regions =
18170 {
18171 Flat_packed_unsigneds.push_back(1);
18172#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18173 Flat_packed_unsigneds_string.push_back(
18174 "The element is associated to boundaries and regions");
18175#endif
18176
18177 Flat_packed_unsigneds.push_back(nassociated_boundaries_and_regions);
18178#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18179 std::stringstream junk;
18180 junk << "The element is associated to "
18181 << nassociated_boundaries_and_regions << " boundaries-regions";
18182 Flat_packed_unsigneds_string.push_back(junk.str());
18183#endif
18184
18185 // Package the ids of the associated boundaries, regions and the
18186 // corresponding face index for each boundary-region (if the
18187 // element is a corner element, it will have two faces
18188 // associated to the boundary-region)
18189 for (unsigned i = 0; i < nassociated_boundaries_and_regions; i++)
18190 {
18191 const unsigned b = associated_boundaries_and_regions[i][0];
18192 Flat_packed_unsigneds.push_back(b);
18193#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18194 std::stringstream junk;
18195 junk << "Element associated to boundary " << b << " of "
18197 << " total associated boundaries-regions";
18198 Flat_packed_unsigneds_string.push_back(junk.str());
18199#endif
18200
18201 const unsigned r = associated_boundaries_and_regions[i][1];
18202 Flat_packed_unsigneds.push_back(r);
18203#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18204 std::stringstream junk2;
18205 junk2 << "Element associated to region " << r << " of "
18207 << " total associated boundaries-regions";
18208 Flat_packed_unsigneds_string.push_back(junk2.str());
18209#endif
18210
18211 const unsigned f = face_index_on_boundary_and_region[i];
18212 Flat_packed_unsigneds.push_back(f);
18213#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18214 std::stringstream junk3;
18215 junk3 << "Face index " << f << " for associated boundary-region ("
18216 << b << "-" << r << ")";
18217 Flat_packed_unsigneds_string.push_back(junk3.str());
18218#endif
18219 } // for (i < nassociated_boundaries_and_regions)
18220 } // if (nassociated_boundaries_and_regions > 0)
18221 else
18222 {
18223 Flat_packed_unsigneds.push_back(0);
18224#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18225 Flat_packed_unsigneds_string.push_back(
18226 "The element is NOT associated to boundaries and regions");
18227#endif
18228 } // else if (nassociated_boundaries_and_regions > 0)
18229 }
18230 else
18231 {
18232 Flat_packed_unsigneds.push_back(0);
18233#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18234 Flat_packed_unsigneds_string.push_back(
18235 "The element is not associated to any original boundary");
18236#endif
18237 }
18238
18239 // ------------------------------------------------------------
18240 // Now review if the element is associated to a shared boundary
18241
18242 // Store the shared boundaries, and therefore the face indexes
18243 // associated to the element
18246
18247 // Get the shared boundaries in this processor
18249 this->shared_boundaries_in_this_processor(my_rank_shared_boundaries_ids);
18250
18251 // Get the number of shared boundaries
18253 // Loop over the shared boundaries
18254 for (unsigned i = 0; i < nmy_rank_shd_bnd; i++)
18255 {
18256 // Get the boundary id
18257 const unsigned sb = my_rank_shared_boundaries_ids[i];
18258
18259 // Get the number of elements associated to shared boundary sb
18260 const unsigned nboundary_ele = this->nshared_boundary_element(sb);
18261 for (unsigned e = 0; e < nboundary_ele; e++)
18262 {
18263 if (ele_pt == this->shared_boundary_element_pt(sb, e))
18264 {
18265 // Keep track of the boundaries associated to the element
18267 // Get the face index
18269 this->face_index_at_shared_boundary(sb, e));
18270 }
18271 } // (e < nboundary_ele)
18272 } // (i < nmy_rank_shd_bnd)
18273
18274 // If the element is associated to a shared boundary then package
18275 // all the relevant info
18276 const unsigned nassociated_shared_boundaries =
18279 {
18280 Flat_packed_unsigneds.push_back(3);
18281#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18282 Flat_packed_unsigneds_string.push_back(
18283 "The element is a shared boundary element");
18284#endif
18285 Flat_packed_unsigneds.push_back(nassociated_shared_boundaries);
18286#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18287 std::stringstream junk;
18288 junk << "The elements is associated to " << nassociated_shared_boundaries
18289 << "shared boundaries";
18290 Flat_packed_unsigneds_string.push_back(junk.str());
18291#endif
18292
18293 // Package the ids of the associated boundaries
18294 for (unsigned i = 0; i < nassociated_shared_boundaries; i++)
18295 {
18296 const unsigned b = associated_shared_boundaries[i];
18297 Flat_packed_unsigneds.push_back(b);
18298#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18299 std::stringstream junk;
18300 junk << "Element associated to shared boundary " << b << " of "
18301 << nassociated_shared_boundaries << " total associated boundaries";
18302 Flat_packed_unsigneds_string.push_back(junk.str());
18303#endif
18304
18305 const unsigned f = face_index_on_shared_boundary[i];
18306 Flat_packed_unsigneds.push_back(f);
18307#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18308 std::stringstream junk2;
18309 junk2 << "Face index " << f << " for associated shared boundary " << b;
18310 Flat_packed_unsigneds_string.push_back(junk2.str());
18311#endif
18312 }
18313 }
18314 else
18315 {
18316 Flat_packed_unsigneds.push_back(0);
18317#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18318 Flat_packed_unsigneds_string.push_back(
18319 "The element is not associated to any shared boundary");
18320#endif
18321 }
18322 }
18323
18324 //========start of get_required_nodal_information_helper==================
18325 /// Helper function to get the required nodal information from an
18326 /// haloed node so that a fully-functional halo node (and therefore element)
18327 /// can be created on the receiving process
18328 //========================================================================
18329 template<class ELEMENT>
18331 unsigned& iproc, Node* nod_pt)
18332 {
18333 unsigned my_rank = this->communicator_pt()->my_rank();
18334 const unsigned nproc = this->communicator_pt()->nproc();
18335
18336 // Tell the halo copy of this node how many values there are
18337 // [NB this may be different for nodes within the same element, e.g.
18338 // when using Lagrange multipliers]
18339 unsigned n_val = nod_pt->nvalue();
18340 Flat_packed_unsigneds.push_back(n_val);
18341#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18342 Flat_packed_unsigneds_string.push_back("Number of values");
18343#endif
18344
18345 unsigned n_dim = nod_pt->ndim();
18346
18347 // Default number of previous values to 1
18348 unsigned n_prev = 1;
18349 if (this->Time_stepper_pt != 0)
18350 {
18351 // Add number of history values to n_prev
18352 n_prev = this->Time_stepper_pt->ntstorage();
18353 }
18354
18355 // -----------------------------------------------------
18356 // Is the node on an original boundary?
18357 // Store the original boundaries where the node may be
18359 // Loop over the original boundaries of the mesh and check if live
18360 // on one of them
18361 const unsigned n_bnd = this->initial_shared_boundary_id();
18362 for (unsigned bb = 0; bb < n_bnd; bb++)
18363 {
18364 // Which boundaries (could be more than one) is it on?
18365 if (nod_pt->is_on_boundary(bb))
18366 {
18367 original_boundaries.push_back(bb);
18368 }
18369 }
18370
18372 // Is the node on any original boundary?
18373 if (n_original_boundaries > 0)
18374 {
18375 // Indicate that the node is on an original boundary
18376 Flat_packed_unsigneds.push_back(2);
18377#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18378 Flat_packed_unsigneds_string.push_back(
18379 "Node is on the original boundaries");
18380#endif
18381
18382 Flat_packed_unsigneds.push_back(n_original_boundaries);
18383#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18384 std::stringstream junk;
18385 junk << "Node is on " << n_original_boundaries << " original boundaries";
18386 Flat_packed_unsigneds_string.push_back(junk.str());
18387#endif
18388
18389 // Loop over the original boundaries the node is on
18390 for (unsigned i = 0; i < n_original_boundaries; i++)
18391 {
18392 Flat_packed_unsigneds.push_back(original_boundaries[i]);
18393#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18394 std::stringstream junk;
18395 junk << "Node is on boundary " << original_boundaries[i] << " of "
18396 << nb;
18397 Flat_packed_unsigneds_string.push_back(junk.str());
18398#endif
18399 // Get the boundary coordinate of the node
18401 nod_pt->get_coordinates_on_boundary(original_boundaries[i], zeta);
18402 Flat_packed_doubles.push_back(zeta[0]);
18403 }
18404 }
18405 else
18406 {
18407 // Indicate that the node is NOT on an original boundary
18408 Flat_packed_unsigneds.push_back(0);
18409#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18410 Flat_packed_unsigneds_string.push_back(
18411 "Node is on any original boundary");
18412#endif
18413 }
18414
18415 // -------------------------------------------------------
18416 // Is the node on shared boundaries?
18417 bool node_on_shared_boundary = false;
18418 // Loop over the shared boundaries with the iproc processors and
18419 // check if live on one of them
18420 const unsigned n_shd_bnd = this->nshared_boundaries(my_rank, iproc);
18421 for (unsigned bb = 0; bb < n_shd_bnd; bb++)
18422 {
18423 // Get the boundary id
18424 unsigned i_bnd = this->shared_boundaries_ids(my_rank, iproc, bb);
18425 // Which boundaries (could be more than one) is it on?
18426 if (this->is_node_on_shared_boundary(i_bnd, nod_pt))
18427 {
18429 break;
18430 }
18431 }
18432
18433 // If the node live on any of the shared boundaries with the iproc
18434 // processor then just get the node number according to the
18435 // sorted_shared_boundary_node_pt() scheme and send it accross
18437 {
18438 Flat_packed_unsigneds.push_back(1);
18439#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18440 Flat_packed_unsigneds_string.push_back("Node is on shared boundary");
18441#endif
18442
18443 // Store the shared boundaries where the node is on
18445 // Loop over the shared boundaries with the iproc processor
18446 for (unsigned bb = 0; bb < n_shd_bnd; bb++)
18447 {
18448 // Get the boundary id
18449 const unsigned i_bnd = this->shared_boundaries_ids(my_rank, iproc, bb);
18450 // Which boundaries (could be more than one) is it on?
18451 if (this->is_node_on_shared_boundary(i_bnd, nod_pt))
18452 {
18453 shd_boundaries.push_back(i_bnd);
18454 }
18455 }
18456
18457 // Get the number of shared boundaries the node is on
18458 const unsigned n_shd_bnd_is_on = shd_boundaries.size();
18459 // Send the number of shared boundaries the node is on
18460 Flat_packed_unsigneds.push_back(n_shd_bnd_is_on);
18461#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18462 std::stringstream junk;
18463 junk << "Node is on " << n_shd_bnd_is_on << " shared boundaries";
18464 Flat_packed_unsigneds_string.push_back(junk.str());
18465#endif
18466
18467 // Loop over the shared boundaries to send their ids
18468 for (unsigned i = 0; i < n_shd_bnd_is_on; i++)
18469 {
18470 Flat_packed_unsigneds.push_back(shd_boundaries[i]);
18471#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18472 std::stringstream junk;
18473 junk << "Node is on boundary " << shd_boundaries[i] << " of " << nb;
18474 Flat_packed_unsigneds_string.push_back(junk.str());
18475#endif
18476 }
18477
18478 // Given that the node is on at least one boundary get the index
18479 // of the node in one of the boundaries and send this index
18480 unsigned shared_boundary_id = shd_boundaries[0];
18481 // Get the number of nodes on the given shared boundary
18482 const unsigned n_nodes_on_shared_boundary =
18483 nsorted_shared_boundary_node(shared_boundary_id);
18484 // Store the index of the node on the shared boundary
18486#ifdef PARANOID
18487 // Flag to know if the node has been found
18489#endif
18490 // Loop over the nodes on the shared boundary to find the node
18491 for (unsigned i = 0; i < n_nodes_on_shared_boundary; i++)
18492 {
18493 // Get the i-th node on the shared boundary
18494 Node* shared_node_pt =
18495 sorted_shared_boundary_node_pt(shared_boundary_id, i);
18496 // Is the node we are looking for
18497 if (shared_node_pt == nod_pt)
18498 {
18499 // Store the index
18501#ifdef PARANOID
18502 // Mark as found
18504#endif
18505 break; // break
18506 }
18507
18508 } // for (i < nnodes_on_shared_boundary)
18509
18510#ifdef PARANOID
18512 {
18513 std::ostringstream error_message;
18514 error_message << "The index of the node on boundary ("
18515 << shared_boundary_id << ") was not found.\n"
18516 << "The node coordinates are (" << nod_pt->x(0) << ","
18517 << nod_pt->x(1) << ").\n";
18518 throw OomphLibError(
18519 error_message.str(),
18520 "RefineableTriangleMesh::get_required_nodal_information_helper()",
18522 }
18523#endif
18524 // Send the index of the node on the shared boundary
18525 Flat_packed_unsigneds.push_back(index_node_on_shared_boundary);
18526#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18527 std::stringstream junk2;
18528 junk2 << "Node index on boundary " << boundaries[0] << " is "
18530 Flat_packed_unsigneds_string.push_back(junk2.str());
18531#endif
18532
18533 } // if (node_on_shared_boundary)
18534 else
18535 {
18536 // The node is not on a shared boundary
18537 Flat_packed_unsigneds.push_back(0);
18538#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18539 Flat_packed_unsigneds_string.push_back(
18540 "Node is not on a shared boundary");
18541#endif
18542 }
18543
18544 // ----------------------------------------------------------------
18545 // Is the node on any shared boundary where the receiver processor
18546 // is not involved?
18547
18548 // Now check if the node is on a shared boundary created by the
18549 // current processor (my_rank) and other processor different that
18550 // the iproc processor. This info. will help to complete the sending
18551 // of halo(ed) information between processors
18552
18553 // Flag to know if the node is on a shared boundary with other
18554 // processor
18556 // Count the number of other shared boundaries it could be on
18558
18559 // Loop over the shared boundaries of the sent processor (my_rank)
18560 // and other processors (jproc)
18561 for (unsigned jproc = 0; jproc < nproc; jproc++)
18562 {
18563 // Do not search with the iproc processor , that was done before
18564 // above because we are sending info to that processor
18565 if (jproc != iproc)
18566 {
18567 // Get the number of shared boundaries with the jproc processor
18568 const unsigned n_jshd_bnd = this->nshared_boundaries(my_rank, jproc);
18569 // Loop over the shared boundaries
18570 for (unsigned bb = 0; bb < n_jshd_bnd; bb++)
18571 {
18572 // Get the boundary id
18573 const unsigned j_shd_bnd =
18574 this->shared_boundaries_ids(my_rank, jproc, bb);
18575 // Is the node part of this boundary?
18576 if (this->is_node_on_shared_boundary(j_shd_bnd, nod_pt))
18577 {
18578 // DEBP("Sending to");
18579 // DEBP(iproc);
18580 // DEBP("Pair of procs where other shared");
18581 // DEBP(my_rank);
18582 // DEBP(jproc);
18583 // DEBP(i_bnd);
18585 // Increase the counter for the number of shared boundaries
18586 // with other processors the node is on
18588 } // if (this->is_node_on_shared_boundary(j_shd_bnd, nod_pt)
18589
18590 } // for (bb<n_jshd_bnd)
18591
18592 } // if (jproc != iproc)
18593
18594 } // for (jproc < nproc)
18595
18596 // If the node is on a shared boundary with another processor
18597 // (my_rank, jproc), then send the flag and look for the info.
18599 {
18600 Flat_packed_unsigneds.push_back(4);
18601#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18602 Flat_packed_unsigneds_string.push_back(
18603 "Node is on shared boundary no related with the received processor: 4");
18604#endif
18605
18606 // The number of packages of information that will be sent to the
18607 // "iproc" processor. This helps to know how many packages of data
18608 // read from the received processor
18609 Flat_packed_unsigneds.push_back(
18611#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18612 std::stringstream junk;
18613 junk << "Number of other shared boundaries that the node is on: "
18615 Flat_packed_unsigneds_string.push_back(junk.str());
18616#endif
18617
18618 // Counter to ensure that the correct number of data has been sent
18620 // Loop over the shared boundaries with other processors and get:
18621 // 1) The processors defining the shared boundary
18622 // 2) The shared boundary id
18623 // 3) The index of the node on the shared boundary
18628 // Loop over the processors again
18629 for (unsigned jproc = 0; jproc < nproc; jproc++)
18630 {
18631 // Do not search with the iproc processor, that was done before
18632 // above
18633 if (jproc != iproc)
18634 {
18635 // Get the number of shared boundaries with the jproc
18636 // processor
18637 const unsigned n_jshd_bnd = this->nshared_boundaries(my_rank, jproc);
18638 for (unsigned bb = 0; bb < n_jshd_bnd; bb++)
18639 {
18640 // Get the boundary id
18641 const unsigned j_shd_bnd =
18642 this->shared_boundaries_ids(my_rank, jproc, bb);
18643 // Is the node part of this boundary?
18644 if (this->is_node_on_shared_boundary(j_shd_bnd, nod_pt))
18645 {
18646 // Include the first processor
18647 other_processor_1.push_back(my_rank);
18648 // Include the second processor
18649 other_processor_2.push_back(jproc);
18650 // Include the shared boundary id
18651 shd_bnd_ids.push_back(j_shd_bnd);
18652 // Increase the counter for found shared boundaries with
18653 // other processors
18655 }
18656
18657 } // for (bb < nshared_bnd)
18658
18659 } // if (jproc != iproc)
18660
18661 } // for (jproc < nproc)
18662
18663 // Get the indexes of the node on all the shared boundaries where
18664 // it was found
18665 const unsigned n_other_processors = other_processor_1.size();
18666 // Loop over the processors where the node was found
18667 for (unsigned i = 0; i < n_other_processors; i++)
18668 {
18669 // Get the shared boundary id
18670 unsigned shd_bnd_id = shd_bnd_ids[i];
18671 // Get the number of nodes on that shared boundary
18672 const unsigned n_nodes_on_shd_bnd =
18673 nsorted_shared_boundary_node(shd_bnd_id);
18674
18675#ifdef PARANOID
18677#endif
18678 for (unsigned i = 0; i < n_nodes_on_shd_bnd; i++)
18679 {
18680 // Get the i-th shared boundary node
18681 Node* shared_node_pt = sorted_shared_boundary_node_pt(shd_bnd_id, i);
18682 // Is the same node?
18683 if (shared_node_pt == nod_pt)
18684 {
18685 // DEBP(i_node);
18686 // DEBP(nod_pt->x(0));
18687 // DEBP(nod_pt->x(1));
18688 // Include the index of the node
18689 indexes.push_back(i);
18690#ifdef PARANOID
18691 // Mark as found the node
18693#endif
18694 break;
18695 } // if (shared_node_pt == nod_pt)
18696
18697 } // for (i < n_nodes_on_shd_bnd)
18698
18699#ifdef PARANOID
18701 {
18702 std::ostringstream error_message;
18703 error_message << "The index of the node on boundary (" << shd_bnd_id
18704 << "), shared by other processors\nwas not found.\n"
18705 << "The node coordinates are (" << nod_pt->x(0) << ","
18706 << nod_pt->x(1) << ").\n";
18707 throw OomphLibError(
18708 error_message.str(),
18709 "RefineableTriangleMesh::get_required_nodal_information_helper()",
18711 }
18712#endif
18713 } // for (i < n_other_processors)
18714
18715 // Now send the info. but first check that the number of found
18716 // nodes be the same that the previously found shared boundaries
18717 // with the node
18718#ifdef PARANOID
18721 {
18722 std::ostringstream error_message;
18723 error_message << "The number of shared boundaries where the node is on "
18724 << "is different:\n"
18725 << "nshared_boundaries_with_other_processors_have_node: ("
18727 << ")\n"
18728 << "counter_shd_bnd_with_other_procs_have_node: ("
18730 throw OomphLibError(
18731 error_message.str(),
18732 "RefineableTriangleMesh::get_required_nodal_information_helper()",
18734 } // if (counter_shd_bnd_with_other_procs_have_node !=
18735 // nshared_boundaries_with_other_processors_have_node)
18736#endif
18737
18738 // Loop over the info. to send it
18739 for (unsigned i = 0; i < n_other_processors; i++)
18740 {
18741 Flat_packed_unsigneds.push_back(other_processor_1[i]);
18742#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18743 std::stringstream junk1;
18744 junk1 << "Processor where the other shared boundary "
18745 << "has the node: " << other_processor_1[i];
18746 Flat_packed_unsigneds_string.push_back(junk1.str());
18747#endif
18748
18749 Flat_packed_unsigneds.push_back(other_processor_2[i]);
18750#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18751 std::stringstream junk2;
18752 junk2 << "Processor where the other shared boundary "
18753 << "has the node: " << other_processor_2[i];
18754 Flat_packed_unsigneds_string.push_back(junk2.str());
18755#endif
18756
18757 Flat_packed_unsigneds.push_back(shd_bnd_ids[i]);
18758#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18759 std::stringstream junk3;
18760 junk3 << "Other shared boundary id where the node is on"
18761 << boundaries[i];
18762 Flat_packed_unsigneds_string.push_back(junk3.str());
18763#endif
18764
18765 Flat_packed_unsigneds.push_back(indexes[i]);
18766#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18767 std::stringstream junk4;
18768 junk4 << "Node index on other shared boundary " << boundaries[i]
18769 << " is " << indexes[i];
18770 Flat_packed_unsigneds_string.push_back(junk4.str());
18771#endif
18772
18773 } // for (i < n_other_processors)
18774
18775 } // if (node_on_shared_boundary_with_other_processors)
18776 else
18777 {
18778 Flat_packed_unsigneds.push_back(0);
18779#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18780 Flat_packed_unsigneds_string.push_back(
18781 "Node is on any shared boundary with other processors");
18782#endif
18783 } // else if (node_on_shared_boundary_with_other_processors)
18784
18785 // Now check if it is required to send the info. of the node. If the
18786 // node is not on a shared boundary with the iproc processor then we
18787 // need to send the info.
18788
18790 {
18791 // Send all the info. to create it
18792
18793 // Is the Node algebraic? If so, send its ref values and
18794 // an indication of its geometric objects if they are stored
18795 // in the algebraic mesh
18796 AlgebraicNode* alg_nod_pt = dynamic_cast<AlgebraicNode*>(nod_pt);
18797 if (alg_nod_pt != 0)
18798 {
18799 // The external mesh should be algebraic
18800 AlgebraicMesh* alg_mesh_pt = dynamic_cast<AlgebraicMesh*>(this);
18801
18802 // Get default node update function ID
18803 unsigned update_id = alg_nod_pt->node_update_fct_id();
18804 Flat_packed_unsigneds.push_back(update_id);
18805#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18806 Flat_packed_unsigneds_string.push_back("Alg Node update id");
18807#endif
18808
18809 // Get reference values at default...
18810 unsigned n_ref_val = alg_nod_pt->nref_value();
18811 Flat_packed_unsigneds.push_back(n_ref_val);
18812#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18813 Flat_packed_unsigneds_string.push_back("Alg Node n ref values");
18814#endif
18815 for (unsigned i_ref_val = 0; i_ref_val < n_ref_val; i_ref_val++)
18816 {
18817 Flat_packed_doubles.push_back(alg_nod_pt->ref_value(i_ref_val));
18818 }
18819
18820 // Access geometric objects at default...
18821 unsigned n_geom_obj = alg_nod_pt->ngeom_object();
18822 Flat_packed_unsigneds.push_back(n_geom_obj);
18823#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18824 Flat_packed_unsigneds_string.push_back("Alg Node n geom objects");
18825#endif
18826 for (unsigned i_geom = 0; i_geom < n_geom_obj; i_geom++)
18827 {
18828 GeomObject* geom_obj_pt = alg_nod_pt->geom_object_pt(i_geom);
18829
18830 // Check this against the stored geometric objects in mesh
18831 unsigned n_geom_list = alg_mesh_pt->ngeom_object_list_pt();
18832
18833 // Default found index to zero
18834 unsigned found_geom_object = 0;
18835 for (unsigned i_list = 0; i_list < n_geom_list; i_list++)
18836 {
18837 if (geom_obj_pt == alg_mesh_pt->geom_object_list_pt(i_list))
18838 {
18840 }
18841 }
18842 Flat_packed_unsigneds.push_back(found_geom_object);
18843#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18844 Flat_packed_unsigneds_string.push_back("Found geom object");
18845#endif
18846 }
18847 } // (if alg_nod_pt!=0)
18848
18849 // Is it a SolidNode?
18850 SolidNode* solid_nod_pt = dynamic_cast<SolidNode*>(nod_pt);
18851 if (solid_nod_pt != 0)
18852 {
18853 unsigned n_solid_val = solid_nod_pt->variable_position_pt()->nvalue();
18854 for (unsigned i_val = 0; i_val < n_solid_val; i_val++)
18855 {
18856 for (unsigned t = 0; t < n_prev; t++)
18857 {
18858 Flat_packed_doubles.push_back(
18859 solid_nod_pt->variable_position_pt()->value(t, i_val));
18860 }
18861 }
18862
18864 solid_nod_pt->add_values_to_vector(values_solid_node);
18865 const unsigned nvalues_solid_node = values_solid_node.size();
18866 Flat_packed_unsigneds.push_back(nvalues_solid_node);
18867#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18868 std::stringstream junk;
18869 junk << "Number of values solid node: " << nvalues_solid_node;
18870 Flat_packed_unsigneds_string.push_back(junk.str());
18871#endif
18872 for (unsigned i = 0; i < nvalues_solid_node; i++)
18873 {
18874 Flat_packed_doubles.push_back(values_solid_node[i]);
18875 }
18876 }
18877
18878 // Finally copy info required for all node types
18879 for (unsigned i_val = 0; i_val < n_val; i_val++)
18880 {
18881 for (unsigned t = 0; t < n_prev; t++)
18882 {
18883 Flat_packed_doubles.push_back(nod_pt->value(t, i_val));
18884 }
18885 }
18886
18887 // Now do positions
18888 for (unsigned idim = 0; idim < n_dim; idim++)
18889 {
18890 for (unsigned t = 0; t < n_prev; t++)
18891 {
18892 Flat_packed_doubles.push_back(nod_pt->x(t, idim));
18893 }
18894 }
18895
18896 } // if (!node_on_shared_boundary)
18897 }
18898
18899 //==========start of add_haloed_node_helper===============================
18900 /// Helper to add external haloed node that is not a master
18901 //========================================================================
18902 template<class ELEMENT>
18904 Node* nod_pt)
18905 {
18906 // Attempt to add this node as a haloed node
18907 const unsigned n_haloed_nod = this->nhaloed_node(iproc);
18908 const unsigned haloed_node_index =
18909 this->try_to_add_haloed_node_pt(iproc, nod_pt);
18910
18911 // If it was added then the new index should match the size of the storage
18913 {
18914 Flat_packed_unsigneds.push_back(1);
18915
18916#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18917 std::stringstream junk;
18918 junk << "Node needs to be constructed [size="
18919 << Flat_packed_unsigneds.size() << "]; last entry: "
18920 << Flat_packed_unsigneds[Flat_packed_unsigneds.size() - 1];
18921 Flat_packed_unsigneds_string.push_back(junk.str());
18922#endif
18923
18924 // This helper function gets all the required information for the
18925 // specified node and stores it into MPI-sendable information
18926 // so that a halo copy can be made on the receiving process
18927 get_required_nodal_information_helper(iproc, nod_pt);
18928 }
18929 else // It was already added
18930 {
18931 Flat_packed_unsigneds.push_back(0);
18932#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18933 std::stringstream junk;
18934 junk << "Node was already added [size=" << Flat_packed_unsigneds.size()
18935 << "]; last entry: "
18936 << Flat_packed_unsigneds[Flat_packed_unsigneds.size() - 1];
18937
18938 Flat_packed_unsigneds_string.push_back(junk.str());
18939#endif
18940
18941 // This node is already a haloed node, so tell
18942 // the other process its index in the equivalent halo storage
18943 Flat_packed_unsigneds.push_back(haloed_node_index);
18944#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18945 Flat_packed_unsigneds_string.push_back("haloed node index");
18946#endif
18947 }
18948 }
18949
18950 //================= send_and_receive_haloed_info =======================
18951 /// Send the information of the elements that will be created on the other
18952 /// processor
18953 //======================================================================
18954 template<class ELEMENT>
18956 int& send_proc, int& recv_proc)
18957 {
18958 // Get the communicator of the mesh
18959 OomphCommunicator* comm_pt = this->communicator_pt();
18960
18961 // Set MPI info
18964
18965 // Prepare vectors to receive information
18968
18969 // Send the double values associated with halo(ed) elements and nodes
18970 //-------------------------------------------------------------------
18971 unsigned send_count_double_values = Flat_packed_doubles.size();
18973 1,
18975 send_proc,
18976 1,
18977 comm_pt->mpi_comm(),
18978 &request);
18979
18980 unsigned receive_count_double_values = 0;
18982 1,
18984 recv_proc,
18985 1,
18986 comm_pt->mpi_comm(),
18987 &status);
18989
18990 if (send_count_double_values != 0)
18991 {
18992 MPI_Isend(&Flat_packed_doubles[0],
18994 MPI_DOUBLE,
18995 send_proc,
18996 2,
18997 comm_pt->mpi_comm(),
18998 &request);
18999 }
19001 {
19005 MPI_DOUBLE,
19006 recv_proc,
19007 2,
19008 comm_pt->mpi_comm(),
19009 &status);
19010 }
19011 if (send_count_double_values != 0)
19012 {
19014 }
19015
19016 // Now send unsigned values associated with halo(ed) elements and nodes
19017 //---------------------------------------------------------------------
19018 unsigned send_count_unsigned_values = Flat_packed_unsigneds.size();
19019#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19020 unsigned send_count_unsigned_string = Flat_packed_unsigneds_string.size();
19021#ifdef PARANOID
19023 {
19024 std::ostringstream error_message;
19025 error_message << "The number of unsigned values to send to processor ("
19026 << send_proc
19027 << ") is different from the\nnumber of annotated strings "
19028 << "for the communication\n\n";
19029 throw OomphLibError(
19030 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
19031 }
19032#endif // #ifdef PARANOID
19033#endif
19035 1,
19037 send_proc,
19038 14,
19039 comm_pt->mpi_comm(),
19040 &request);
19041
19042 unsigned receive_count_unsigned_values = 0;
19044 1,
19046 recv_proc,
19047 14,
19048 comm_pt->mpi_comm(),
19049 &status);
19050
19052
19054 {
19055 MPI_Isend(&Flat_packed_unsigneds[0],
19058 send_proc,
19059 15,
19060 comm_pt->mpi_comm(),
19061 &request);
19062#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19063 for (unsigned i = 0; i < send_count_unsigned_values; i++)
19064 {
19065 oomph_info << "Sent:" << i << " to orig_proc:" << send_proc << " "
19066 << Flat_packed_unsigneds_string[i] << ": "
19067 << Flat_packed_unsigneds[i] << std::endl;
19068 }
19069#endif
19070 }
19072 {
19077 recv_proc,
19078 15,
19079 comm_pt->mpi_comm(),
19080 &status);
19081 }
19082
19084 {
19086 }
19087
19088 // Copy across into original containers -- these can now
19089 //------------------------------------------------------
19090 // be processed by create_external_halo_elements() to generate
19091 //------------------------------------------------------------
19092 // external halo elements
19093 //------------------------
19094 Flat_packed_doubles.resize(receive_count_double_values);
19095 for (int ii = 0; ii < receive_count_double_values; ii++)
19096 {
19097 Flat_packed_doubles[ii] = received_double_values[ii];
19098 }
19099 Flat_packed_unsigneds.resize(receive_count_unsigned_values);
19100 for (int ii = 0; ii < receive_count_unsigned_values; ii++)
19101 {
19102 Flat_packed_unsigneds[ii] = received_unsigned_values[ii];
19103 }
19104 }
19105
19106 //=====================================================================
19107 /// Creates (halo) element on the loop process based on the
19108 /// information received from each processor
19109 //=====================================================================
19110 template<class ELEMENT>
19112 unsigned& iproc,
19114 Vector<Vector<Vector<std::map<unsigned, Node*>>>>&
19117 std::map<Vector<unsigned>, unsigned>& node_name_to_global_index,
19119 {
19120#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19121 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
19122 << " Bool: New element needs to be constructed "
19123 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19124 << std::endl;
19125#endif
19126
19127 if (Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++] == 1)
19128 {
19129 // Create a new element from the communicated values
19130 // and coords from the process that located zeta
19131 GeneralisedElement* new_el_pt = new ELEMENT;
19132
19133 // Add the element, it is a new element in the mesh
19134 this->add_element_pt(new_el_pt);
19135
19136 // Add halo element to this mesh
19137 this->add_root_halo_element_pt(iproc, new_el_pt);
19138
19139 // Cast to the FE pointer
19140 FiniteElement* f_el_pt = dynamic_cast<FiniteElement*>(new_el_pt);
19141
19142 // Check if new element is associated to any boundary
19143 this->add_halo_element_helper(iproc, f_el_pt);
19144
19145 // Now we add nodes to the new element
19146 unsigned n_node = f_el_pt->nnode();
19147
19148 for (unsigned j = 0; j < n_node; j++)
19149 {
19150 Node* new_nod_pt = 0;
19151
19152 // Call the add halo node helper function
19153 add_halo_node_helper(new_nod_pt,
19156 iproc,
19157 j,
19158 f_el_pt,
19162
19163 } // for (j<n_nod)
19164 }
19165 else // the element already exists as halo
19166 {
19167#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19168 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
19169 << " Index of existing halo element "
19170 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19171 << std::endl;
19172#endif
19173 // The index itself is in Flat_packed_unsigneds[...]
19174 unsigned halo_ele_index =
19175 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
19176
19177 // Use this index to get the element
19178 FiniteElement* f_el_pt = dynamic_cast<FiniteElement*>(
19179 this->root_halo_element_pt(iproc, halo_ele_index));
19180
19181 // If it's not a finite element die
19182 if (f_el_pt == 0)
19183 {
19184 throw OomphLibError("Halo element is not a FiniteElement\n",
19187 }
19188
19189 } // else the element already exists as halo
19190 }
19191
19192 //========start of add_halo_element_helper==============================
19193 /// Helper function to create (halo) elements on the loop
19194 /// process based on the info received in send_and_received_located_info
19195 /// This function is in charge of verify if the element is associated to
19196 /// a boundary
19197 //======================================================================
19198 template<class ELEMENT>
19200 unsigned& iproc, FiniteElement* ele_pt)
19201 {
19202#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19203 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
19204 << " Bool: Element is associated to an original boundary "
19205 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19206 << std::endl;
19207#endif
19208
19209 if (Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++] == 1)
19210 {
19211#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19212 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
19213 << " How many boundaries are associated with the element "
19214 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19215 << std::endl;
19216#endif
19217 const unsigned nassociated_boundaries =
19218 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
19219
19220 for (unsigned b = 0; b < nassociated_boundaries; b++)
19221 {
19222#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19223 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
19224 << " Boundary associated to the element "
19225 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19226 << std::endl;
19227#endif
19228 const unsigned bnd =
19229 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
19230
19231#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19232 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
19233 << " Face index of the element "
19234 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19235 << std::endl;
19236#endif
19237 const unsigned face_index =
19238 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
19239
19240 // Associate the element with the boundary and establish as many
19241 // face indexes it has
19242 this->Boundary_element_pt[bnd].push_back(ele_pt);
19243 this->Face_index_at_boundary[bnd].push_back(face_index);
19244
19245 } // (b < nassociated_boundaries)
19246
19247 // Here read the info. regarding the boundary-region of the element
19248#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19249 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
19250 << " Bool: Element is associated to a boundary-region "
19251 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19252 << std::endl;
19253#endif
19254
19255 if (Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++] == 1)
19256 {
19257#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19259 << "Rec:" << Counter_for_flat_packed_unsigneds
19260 << " How many boundaries-regions are associated with the element "
19261 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19262 << std::endl;
19263#endif
19264 const unsigned nassociated_boundaries_and_regions =
19265 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
19266
19267 for (unsigned br = 0; br < nassociated_boundaries_and_regions; br++)
19268 {
19269#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19270 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
19271 << " Boundary associated to the element "
19272 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19273 << std::endl;
19274#endif
19275 const unsigned bnd =
19276 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
19277
19278#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19279 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
19280 << " Region associated to the element "
19281 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19282 << std::endl;
19283#endif
19284 const unsigned region =
19285 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
19286
19287#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19288 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
19289 << " Face index of the element in boundary-region "
19290 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19291 << std::endl;
19292#endif
19293 const unsigned face_index =
19294 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
19295
19296 // Associate the element with the boundary-regions and establish
19297 // as many face indexes it has
19298 this->Boundary_region_element_pt[bnd][region].push_back(ele_pt);
19299 this->Face_index_region_at_boundary[bnd][region].push_back(
19300 face_index);
19301
19302 } // for (br < nassociated_boundaries_and_regions)
19303
19304 } // Is the element associated with a boundary-region?
19305 }
19306
19307 // Now check if the element is associated to a shared boundary
19308#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19309 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
19310 << " Bool: Element is associated to a shared boundary "
19311 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19312 << std::endl;
19313#endif
19314 if (Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++] == 3)
19315 {
19316#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19318 << "Rec:" << Counter_for_flat_packed_unsigneds
19319 << " How many shared boundaries are associated with the element "
19320 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19321 << std::endl;
19322#endif
19323 const unsigned nassociated_shared_boundaries =
19324 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
19325
19326 for (unsigned b = 0; b < nassociated_shared_boundaries; b++)
19327 {
19328#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19329 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
19330 << " Shared boundary associated to the element "
19331 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19332 << std::endl;
19333#endif
19334 const unsigned bnd =
19335 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
19336
19337#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19339 << "Rec:" << Counter_for_flat_packed_unsigneds
19340 << " Face index of the element associated to the shared boundary "
19341 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19342 << std::endl;
19343#endif
19344
19345 const unsigned face_index =
19346 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
19347
19348 this->add_shared_boundary_element(bnd, ele_pt);
19349 this->add_face_index_at_shared_boundary(bnd, face_index);
19350
19351 } // (b < nassociated_shared_boundaries)
19352
19353 } // The element is associted with a shared boundary
19354 }
19355
19356 //========start of add_halo_node_helper==========================
19357 /// Helper function to add halo node
19358 //===============================================================
19359 template<class ELEMENT>
19361 Node*& new_nod_pt,
19363 Vector<Vector<Vector<std::map<unsigned, Node*>>>>&
19365 unsigned& iproc,
19366 unsigned& node_index,
19367 FiniteElement* const& new_el_pt,
19369 std::map<Vector<unsigned>, unsigned>& node_name_to_global_index,
19371 {
19372 // Given the node, received information about them from process
19373 // iproc, construct them on the current process
19374#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19375 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
19376 << " Bool: New node needs to be constructed "
19377 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19378 << std::endl;
19379#endif
19380 if (Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++] == 1)
19381 {
19382 // Construct a new node based upon sent information, or copy a node
19383 // from one of the shared boundaries
19384 construct_new_halo_node_helper(new_nod_pt,
19387 iproc,
19388 node_index,
19389 new_el_pt,
19393 }
19394 else
19395 {
19396#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19397 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
19398 << " Index of existing halo node "
19399 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19400 << std::endl;
19401#endif
19402
19403 // Copy node from received location
19405 [Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++]];
19406
19408 }
19409 }
19410
19411 //========start of construct_new_halo_node_helper=================
19412 // Helper function which constructs a new external halo node (on new element)
19413 // with the required information sent from the haloed process
19414 //========================================================================
19415 template<class ELEMENT>
19417 Node*& new_nod_pt,
19419 Vector<Vector<Vector<std::map<unsigned, Node*>>>>&
19421 unsigned& iproc,
19422 unsigned& node_index,
19423 FiniteElement* const& new_el_pt,
19425 std::map<Vector<unsigned>, unsigned>& node_name_to_global_index,
19427 {
19428 // The first entry indicates the number of values at this new Node
19429 //(which may be different across the same element e.g. Lagrange
19430 // multipliers)
19431#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19432 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
19433 << " Number of values of external halo node "
19434 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19435 << std::endl;
19436#endif
19437 unsigned n_val = Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
19438
19439 // Null TimeStepper for now
19440 TimeStepper* time_stepper_pt = this->Time_stepper_pt;
19441 // Default number of previous values to 1
19442 unsigned n_prev = time_stepper_pt->ntstorage();
19443
19444 // ------------------------------------------------------
19445 // Check if the node is on an original boundary
19446#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19447 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
19448 << " Is the node on an original boundary "
19449 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19450 << std::endl;
19451#endif
19452
19453 // Flag to indicate if the node is on original boundaries
19454 const unsigned node_on_original_boundaries =
19455 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
19456
19457 // Store the original boundaries where the node is on
19459 // Store the zeta coordinates of the node on the original boundaries
19461 // Store the number of original boundaries the node is on
19463
19465 {
19466 // How many original boundaries does the node live on?
19467#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19468 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
19469 << " Number of boundaries the node is on: "
19470 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19471 << std::endl;
19472#endif
19474 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
19475
19476 // Resize the containers
19479
19480 for (unsigned i = 0; i < n_original_boundaries_node_is_on; i++)
19481 {
19482 // Boundary number
19483#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19484 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
19485 << " Node is on boundary "
19486 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19487 << std::endl;
19488#endif
19490 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
19492 Flat_packed_doubles[Counter_for_flat_packed_doubles++];
19493 }
19494
19495 } // if (node_on_original_boundaries==2)
19496#ifdef PARANOID
19497 else
19498 {
19500 {
19501 std::ostringstream error_message;
19502 error_message
19503 << "The current node is not on an original boundary, this should\n"
19504 << "be indicated by a zero flag. However, the read value for\n"
19505 << "that flag is (" << node_on_original_boundaries << ").\n\n";
19506 throw OomphLibError(
19507 error_message.str(),
19508 "RefineableTriangleMesh::construct_new_halo_node_helper()",
19510 } // if (node_on_original_boundaries != 0)
19511 }
19512#endif
19513
19514 // --------------------------------------------------------------
19515 // Check if the node was on a shared boundary with the iproc
19516 // processor
19517#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19518 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
19519 << " Is node on shared boundary? "
19520 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19521 << std::endl;
19522#endif
19523 const unsigned is_node_on_shared_boundary =
19524 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
19525 if (is_node_on_shared_boundary == 1)
19526 {
19527 // How many shared boundaries does the node live on?
19528#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19529 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
19530 << " Number of boundaries the node is on: "
19531 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19532 << std::endl;
19533#endif
19534 const unsigned n_shd_bnd_node_is_on =
19535 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
19537 for (unsigned i = 0; i < n_shd_bnd_node_is_on; i++)
19538 {
19539 // Shared boundary number
19540#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19541 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
19542 << " Node is on boundary "
19543 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19544 << std::endl;
19545#endif
19547 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
19548 }
19549
19550 // Get the index of the node on the shared boundary
19551#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19552 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
19553 << " Index of node on boundary "
19554 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19555 << std::endl;
19556#endif
19557 // Get the node index of the node on the shared boundary
19559 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
19560
19561 // Get the pointer to the node with the received info.
19562 new_nod_pt = this->sorted_shared_boundary_node_pt(
19564
19565 } // if (is_node_on_shared_boundary == 1)
19566#ifdef PARANOID
19567 else
19568 {
19569 if (is_node_on_shared_boundary != 0)
19570 {
19571 std::ostringstream error_message;
19572 error_message
19573 << "The current node is not on a shared boundary, this should\n"
19574 << "be indicated by a zero flag. However, the read value for\n"
19575 << "that flag is (" << is_node_on_shared_boundary << ").\n\n";
19576 throw OomphLibError(
19577 error_message.str(),
19578 "RefineableTriangleMesh::construct_new_halo_node_helper()",
19580 } // if (node_on_shared_boundary != 0)
19581 }
19582#endif
19583
19584 // ------------------------------------------------------------
19585 // Is the node on a shared boundary with other processor?
19586#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19587 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
19588 << " Is the node on shared boundaries with other processors "
19589 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19590 << std::endl;
19591#endif
19592
19593 // Is the node in shared boundaries no associated with the
19594 // receiver processor
19596 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
19597
19598 // The containers where to store the info.
19603
19604 // How many shared bounaries with other processors the node lives on
19606
19607 // Is the node on shared boundaries with other processors
19609 {
19610#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19611 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
19612 << " In how many shared boundaries with other "
19613 << "processors is the node "
19614 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19615 << std::endl;
19616#endif
19617
19618 // How many nodes on other shared boundaries were found
19620 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
19621
19622 // Resize the containers
19627
19628 for (unsigned i = 0; i < n_shd_bnd_with_other_procs_have_node; i++)
19629 {
19630#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19631 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
19632 << " Processor where the other shared boundary"
19633 << "has the node"
19634 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19635 << std::endl;
19636#endif
19637 // Read the other processor 1
19639 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
19640
19641#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19642 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
19643 << " Processor where the other shared boundary"
19644 << "has the node"
19645 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19646 << std::endl;
19647#endif
19648 // Read the other processor 2
19650 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
19651
19652#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19653 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
19654 << " Other shared boundary id where the node is on: "
19655 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19656 << std::endl;
19657#endif
19658
19659 // Read the other shared boundary id
19661 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
19662
19663#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19664 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
19665 << " Node index on the other shared boundary "
19666 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
19667 << std::endl;
19668#endif
19669
19670 // Read the node index on the other shared boundary
19671 other_indexes[i] =
19672 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
19673
19674 } // for (i < n_shd_bnd_with_other_procs_have_node)
19675
19676 } // if (is_the_node_in_shared_boundaries_with_other_processors == 4)
19677#ifdef PARANOID
19678 else
19679 {
19681 {
19682 std::ostringstream error_message;
19683 error_message
19684 << "The current node is not on a shared boundary with\n"
19685 << "other processors, this should be indicated by a zero flag.\n"
19686 << "However, the read value for that flag is ("
19688 throw OomphLibError(
19689 error_message.str(),
19690 "RefineableTriangleMesh::construct_new_halo_node_helper()",
19692 }
19693 }
19694#endif
19695
19696 // Now we have all the info. to decide whether the node should be
19697 // created or not
19698
19699 // First check if the node is a shared boundary node
19700 if (is_node_on_shared_boundary == 1)
19701 {
19702 // We already have the node, we do not need to create it
19703
19704 // Only check if we need to add boundary info. to the node
19706 {
19707 // The node is a boundary node, add the boundary info. before
19708 // adding it to the domain
19709
19710 // Associate the node to the given boundaries
19711 for (unsigned i = 0; i < n_original_boundaries_node_is_on; i++)
19712 {
19713 add_boundary_node(original_boundaries_node_is_on[i], new_nod_pt);
19714 // Establish the boundary coordinates for the node
19716 zeta[0] = zeta_coordinates[i];
19717 new_nod_pt->set_coordinates_on_boundary(
19719 }
19720
19721 } // if (node_on_original_boundaries==2)
19722
19723 // Add the node to the domain
19725
19726 // Add the node to the element
19728
19729 } // if (is_node_on_shared_boundary == 1)
19730
19731 // Now check if the node is on a shared boundary with another
19732 // processor, if that is the case try to find the node that may have
19733 // been already sent by the other processors
19734
19735 // This flags indicates if the node was found, and then decide if it
19736 // is required to create the node
19738 // Flag to indicate whether the node should be created as a boundary
19739 // node or not. If the node lies on a shared boundary with other
19740 // processor the we create it as a boundary node. The processor from
19741 // which we are receiving info. (iproc) may not know that the node
19742 // lies on an original boundary. If the node lies on an original
19743 // boundary then its info. will be sent by another processor, then
19744 // we can set its boundary info. since the node was constructed as a
19745 // boundary node
19746 bool build_node_as_boundary_node = false;
19747
19749 {
19750 // Build the node as a boundary node
19752
19753 // Try to get the node pointer in case that the node has been
19754 // already sent by the other processors
19755
19756 // Get the number of initial shared boundaries to correct the
19757 // index of the shared boundary
19758 const unsigned initial_shd_bnd_id = this->initial_shared_boundary_id();
19759
19760 // Add the found nodes in the container
19762
19763 // Now try to find the node in any of the other shared boundaries
19764 for (unsigned i = 0; i < n_shd_bnd_with_other_procs_have_node; i++)
19765 {
19766 // We always check with the lower processor number. The
19767 // info. is only stored in one direction. More importantly,
19768 // this is done with the hope that the info. has been already
19769 // received from the other processor given that its info. was
19770 // processed before the current processor (iproc). NOTE that
19771 // it is not always the case that this info. has been received
19772 // from the other processors since it may have not require to
19773 // send the elements (and nodes) on the shared boundary with
19774 // the current processor (iproc).
19775 unsigned oproc1 = other_processor_1[i];
19776 unsigned oproc2 = other_processor_2[i];
19778 {
19781 } // if (other_processor_1[i] > other_processor_2[i])
19782
19783 // Re-compute the shared boundary id between the other
19784 // processors
19785 const unsigned shd_bnd_id =
19787
19788 // Read the index
19789 const unsigned index = other_indexes[i];
19790
19791 // Check if there are nodes received from the other processor
19792 // and with the given shared boundary
19793 const unsigned n_nodes_on_other_processor =
19795
19797 {
19798 // Check if we can find the index of the node in that
19799 // other processor and shared boundary id
19800 std::map<unsigned, Node*>::iterator it =
19802
19803 // If the index exist then get the node pointer
19804 if (it !=
19806 {
19807 // Mark the node as found
19809 // Get the node pointer
19810 Node* tmp_node_pt = (*it).second;
19811
19812 // Push back the node pointer
19813 found_node_pt.push_back(tmp_node_pt);
19814
19815 } // if (it!=
19816 // other_proc_shd_bnd_node_pt[oproc1][oproc2][shd_bnd_id].end())
19817
19818 } // if (n_nodes_on_other_processor > 0)
19819
19820 } // for (i < n_shd_bnd_with_other_procs_have_node)
19821
19822 // If the node was found, then all their instances should be the
19823 // same but better check
19825 {
19826#ifdef PARANOID
19827 const unsigned n_times_node_found = found_node_pt.size();
19828 for (unsigned j = 1; j < n_times_node_found; j++)
19829 {
19830 if (found_node_pt[j - 1] != found_node_pt[j])
19831 {
19832 std::ostringstream error_message;
19833 error_message
19834 << "The instances of the node that was found on\n"
19835 << "shared boundaries with other processors (but not\n"
19836 << "on shared boundaries with this processor) are not\n"
19837 << "the same.\n"
19838 << "These are the coordinates of the instances of the\n"
19839 << "nodes:\n"
19840 << "(" << found_node_pt[j - 1]->x(0) << ", "
19841 << found_node_pt[j - 1]->x(1) << ")\n"
19842 << "(" << found_node_pt[j]->x(0) << ", " << found_node_pt[j]->x(1)
19843 << ")\n"
19844 << "Dont be surprised if they are the same since the "
19845 << "node is\nrepeated.\n";
19846 throw OomphLibError(error_message.str(),
19849
19850 } // if (found_node_pt[j-1] != found_node_pt[j])
19851
19852 } // for (j < ntimes_node_found)
19853#endif // #ifdef PARANOID
19854
19855 // Check if the node is a shared boundary node from the
19856 // current processor and the iproc processor, if that is the
19857 // case, and the node is also on a shared boundary with other
19858 // processor, then the pointer should be the same!!!
19859 if (is_node_on_shared_boundary == 1)
19860 {
19861 // const unsigned n_times_node_found = found_node_pt.size();
19862 // The pointer to the node is already assigned, it was
19863 // assigned when the node was found to be on a shared
19864 // boundary with the sending processor (iproc). Check that
19865 // any previous instances of the node have been copied
19866 // from the shared boundary, if that is not the case then
19867 // there is a problem
19868 if (found_node_pt[0] != new_nod_pt)
19869 {
19870 std::ostringstream error_message;
19871 error_message
19872 << "The pointer of the node that was found to be on a\n"
19873 << "shared boundary with other processor(s) and the pointer\n"
19874 << "of the node on shared boundary with the receiver\n"
19875 << "processor (iproc) are not the same. This means we have a\n"
19876 << "repeated node)\n"
19877 << "The coordinates for the nodes are:\n"
19878 << "(" << found_node_pt[0]->x(0) << ", " << found_node_pt[0]->x(1)
19879 << ")\n"
19880 << "(" << new_nod_pt->x(0) << ", " << new_nod_pt->x(1) << ")\n"
19881 << "Dont be surprised if they are the same since the "
19882 << "node is\nrepeated.\n";
19883 throw OomphLibError(error_message.str(),
19886
19887 } // if (found_node_pt[i] != new_nod_pt)
19888
19889 } // if (is_node_on_shared_boundary == 1)
19890 else
19891 {
19892 // Take the first instance of the node in case that it was
19893 // found and is not on a shared boundary with the iproc
19894 // processor (the processor from which we are receiving
19895 // the info.)
19897 }
19898
19899 } // if (found_node_in_other_shared_boundaries)
19900
19901 } // if (is_the_node_in_shared_boundaries_with_other_processors == 4)
19902
19903 // -----------------------------------------------------------------
19904 // Create the node or read the received info if the node is not on a
19905 // shared boundary with the iproc processor
19906 if (is_node_on_shared_boundary != 1)
19907 {
19908 // If the node is on a shared boundary with other processor we
19909 // need to read all the info. since the processor that sent the
19910 // info. did not know that the node is part of another shared
19911 // boundary
19912
19913 // If the node is not on a shared boundary (with any processor),
19914 // or if this is the first time that the info. of the node is
19915 // received from any of the processors with which it has a shared
19916 // boundary, then we create the node
19917
19918 // Is the node a boundary node or should it be build as a boundary
19919 // node because it is on a shared boundary with other processors
19921 {
19922 // Check if necessary to create the node, or if it has been
19923 // already found in shared boundaries with other processors
19925 {
19926 // Construct a boundary node
19927 if (time_stepper_pt != 0)
19928 {
19929 new_nod_pt =
19931 }
19932 else
19933 {
19935 }
19936
19937 } // if (!found_node_in_other_shared_boundaries)
19938 else
19939 {
19940 // If the node was found then assign the node to the element
19942
19943 } // else if (!found_node_in_other_shared_boundaries)
19944
19945 // Associate the node to the given boundaries
19946 for (unsigned i = 0; i < n_original_boundaries_node_is_on; i++)
19947 {
19948 add_boundary_node(original_boundaries_node_is_on[i], new_nod_pt);
19949 // Establish the boundary coordinates for the node
19951 zeta[0] = zeta_coordinates[i];
19952 new_nod_pt->set_coordinates_on_boundary(
19954 }
19955
19956 } // if (node is on an original boundary)
19957 else
19958 {
19959 // Check if necessary to create the node, or if it has been
19960 // already found in shared boundaries with other processors
19962 {
19963 // Construct an ordinary (non-boundary) node
19964 if (time_stepper_pt != 0)
19965 {
19967 }
19968 else
19969 {
19971 }
19972 } // if (!found_node_in_other_shared_boundaries)
19973 else
19974 {
19975 // If the node was found then assign the node to the element
19977 } // else if (!found_node_in_other_shared_boundaries)
19978
19979 } // else (the node is not a boundary node)
19980
19981 // ... and gather all its information
19982
19983 // If the node was found or not in other shared boundaries, this
19984 // is the first time the node is received from this processor
19985 // (iproc), therefore it is added to the vector of nodes received
19986 // from this processor (iproc)
19988
19989 // Check if necessary to state all the info. to the node if it has
19990 // been already found in shared boundaries with other processors
19992 {
19993 // Add the node to the general node storage
19994 this->add_node_pt(new_nod_pt);
19995 } // if (!found_node_in_other_shared_boundaries)
19996
19997 // Is the new constructed node Algebraic?
19999
20000 // If it is algebraic, its node update functions will
20001 // not yet have been set up properly
20002 if (new_alg_nod_pt != 0)
20003 {
20004 // The AlgebraicMesh is the external mesh
20005 AlgebraicMesh* alg_mesh_pt = dynamic_cast<AlgebraicMesh*>(this);
20006
20007 /// The first entry of All_alg_nodal_info contains
20008 /// the default node update id
20009 /// e.g. for the quarter circle there are
20010 /// "Upper_left_box", "Lower right box" etc...
20011#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
20012 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
20013 << " Alg node update id "
20014 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
20015 << std::endl;
20016#endif
20017
20018 unsigned update_id =
20019 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
20020
20021 Vector<double> ref_value;
20022
20023 // The size of this vector is in the next entry
20024 // of All_alg_nodal_info
20025#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
20026 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
20027 << " Alg node # of ref values "
20028 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
20029 << std::endl;
20030#endif
20031 unsigned n_ref_val =
20032 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
20033
20034 // The reference values themselves are in
20035 // All_alg_ref_value
20036 ref_value.resize(n_ref_val);
20037 for (unsigned i_ref = 0; i_ref < n_ref_val; i_ref++)
20038 {
20039 ref_value[i_ref] =
20040 Flat_packed_doubles[Counter_for_flat_packed_doubles++];
20041 }
20042
20043 Vector<GeomObject*> geom_object_pt;
20044 /// again we need the size of this vector as it varies
20045 /// between meshes; we also need some indication
20046 /// as to which geometric object should be used...
20047
20048 // The size of this vector is in the next entry
20049 // of All_alg_nodal_info
20050#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
20051 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
20052 << " Alg node # of geom objects "
20053 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
20054 << std::endl;
20055#endif
20056 unsigned n_geom_obj =
20057 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
20058
20059 // The remaining indices are in the rest of
20060 // All_alg_nodal_info
20061 geom_object_pt.resize(n_geom_obj);
20062 for (unsigned i_geom = 0; i_geom < n_geom_obj; i_geom++)
20063 {
20064#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
20065 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
20066 << " Alg node: geom object index "
20067 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
20068 << std::endl;
20069#endif
20070 unsigned geom_index =
20071 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
20072 // This index indicates which of the AlgebraicMesh's
20073 // stored geometric objects should be used
20074 // (0 is a null pointer; everything else should have
20075 // been filled in by the specific Mesh). If it
20076 // hasn't been filled in then the update_node_update
20077 // call should fix it
20078 geom_object_pt[i_geom] = alg_mesh_pt->geom_object_list_pt(geom_index);
20079 }
20080
20081 // Check if necessary to state all the info. to the node if it has
20082 // been already found in shared boundaries with other processors
20084 {
20085 /// For the received update_id, ref_value, geom_object
20086 /// call add_node_update_info
20087 new_alg_nod_pt->add_node_update_info(
20088 update_id, alg_mesh_pt, geom_object_pt, ref_value);
20089
20090 /// Now call update_node_update
20091 alg_mesh_pt->update_node_update(new_alg_nod_pt);
20092
20093 } // if (!found_node_in_other_shared_boundaries)
20094
20095 } // if (new_alg_nod_pt!=0)
20096
20097 // Check if necessary to state all the info. to the node if it has
20098 // been already found in shared boundaries with other processors
20100 {
20101 // Is the node a MacroElementNodeUpdateNode?
20103 dynamic_cast<MacroElementNodeUpdateNode*>(new_nod_pt);
20104
20105 if (macro_nod_pt != 0)
20106 {
20107 // Need to call set_node_update_info; this requires
20108 // a Vector<GeomObject*> (taken from the mesh)
20109 Vector<GeomObject*> geom_object_vector_pt;
20110
20111 // Access the required geom objects from the
20112 // MacroElementNodeUpdateMesh
20114 dynamic_cast<MacroElementNodeUpdateMesh*>(this);
20115 geom_object_vector_pt = macro_mesh_pt->geom_object_vector_pt();
20116
20117 // Get local coordinate of node in new element
20121
20122 // Set node update info for this node
20123 macro_nod_pt->set_node_update_info(
20124 new_el_pt, s_in_macro_node_update_element, geom_object_vector_pt);
20125 }
20126
20127 } // if (!found_node_in_other_shared_boundaries)
20128
20129 // If there are additional values, resize the node
20130 unsigned n_new_val = new_nod_pt->nvalue();
20131
20132 // Check if necessary to state all the info. to the node if it has
20133 // been already found in shared boundaries with other processors
20135 {
20136 if (n_val > n_new_val)
20137 {
20138 // If it has been necessary to resize then it may be becuse
20139 // the node is on a FSI boundary, if that is the case we need
20140 // to set a map for these external values
20141
20142 // Cast to a boundary node
20144 dynamic_cast<BoundaryNodeBase*>(new_nod_pt);
20145
20146 // Create storage, if it doesn't already exist, for the map
20147 // that will contain the position of the first entry of
20148 // this face element's additional values,
20149 if (bnod_pt->index_of_first_value_assigned_by_face_element_pt() == 0)
20150 {
20151 bnod_pt->index_of_first_value_assigned_by_face_element_pt() =
20152 new std::map<unsigned, unsigned>;
20153 }
20154
20155 // Get pointer to the map
20156 std::map<unsigned, unsigned>* map_pt =
20157 bnod_pt->index_of_first_value_assigned_by_face_element_pt();
20158
20159 // The id of the face to which this node belong in the bulk
20160 // element
20161 const unsigned id_face = 0;
20162 // We only resize the node values Vector if we haven't done it yet
20163 std::map<unsigned, unsigned>::const_iterator p =
20164 map_pt->find(id_face);
20165
20166 // If this node hasn't been resized for current id
20167 if (p == map_pt->end())
20168 {
20169 // assign the face element id and the position of the
20170 // first entry to the boundary node
20171 (*map_pt)[id_face] = n_new_val;
20172
20173 // resize the node vector of values
20174 new_nod_pt->resize(n_val);
20175 }
20176
20177 } // if (n_val>n_new_val)
20178
20179 } // if (!found_node_in_other_shared_boundaries)
20180
20181 // Is the new node a SolidNode?
20182 SolidNode* solid_nod_pt = dynamic_cast<SolidNode*>(new_nod_pt);
20183 if (solid_nod_pt != 0)
20184 {
20185 unsigned n_solid_val = solid_nod_pt->variable_position_pt()->nvalue();
20186 for (unsigned i_val = 0; i_val < n_solid_val; i_val++)
20187 {
20188 for (unsigned t = 0; t < n_prev; t++)
20189 {
20190 double read_data =
20191 Flat_packed_doubles[Counter_for_flat_packed_doubles++];
20192
20193 // Check if necessary to state all the info. to the node if it has
20194 // been already found in shared boundaries with other processors
20196 {
20197 solid_nod_pt->variable_position_pt()->set_value(
20198 t, i_val, read_data);
20199 } // if (!found_node_in_other_shared_boundaries)
20200 }
20201 }
20202
20203#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
20204 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
20205 << " Number of values solid node: "
20206 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
20207 << std::endl;
20208#endif
20209 const unsigned nvalues_solid_node =
20210 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
20212 for (unsigned i = 0; i < nvalues_solid_node; i++)
20213 {
20215 Flat_packed_doubles[Counter_for_flat_packed_doubles++];
20216 }
20217
20218 // Check if necessary to state all the info. to the node if it has
20219 // been already found in shared boundaries with other processors
20221 {
20222 unsigned index = 0;
20223 solid_nod_pt->read_values_from_vector(values_solid_node, index);
20224 }
20225 }
20226
20227 // Get copied history values
20228 // unsigned n_val=new_nod_pt->nvalue();
20229 for (unsigned i_val = 0; i_val < n_val; i_val++)
20230 {
20231 for (unsigned t = 0; t < n_prev; t++)
20232 {
20233 double read_data =
20234 Flat_packed_doubles[Counter_for_flat_packed_doubles++];
20235
20236 // Check if necessary to state all the info. to the node if it
20237 // has been already found in shared boundaries with other
20238 // processors
20240 {
20241 new_nod_pt->set_value(t, i_val, read_data);
20242 } // if (!found_node_in_other_shared_boundaries)
20243 }
20244 }
20245
20246 // Get copied history values for positions
20247 unsigned n_dim = new_nod_pt->ndim();
20248 for (unsigned idim = 0; idim < n_dim; idim++)
20249 {
20250 for (unsigned t = 0; t < n_prev; t++)
20251 {
20252 double read_data =
20253 Flat_packed_doubles[Counter_for_flat_packed_doubles++];
20254
20255 // Check if necessary to state all the info. to the node if it
20256 // has been already found in shared boundaries with other
20257 // processors
20259 {
20260 // Copy to coordinate
20261 new_nod_pt->x(t, idim) = read_data;
20262
20263 } // if (!found_node_in_other_shared_boundaries)
20264 }
20265 }
20266
20267 } // if (is_node_on_shared_boundary != 1)
20268
20269 // If the node was not found in other shared boundaries (possibly
20270 // because it is the first time the node has been sent) then copy
20271 // the node to the shared boundaries where it should be, use the
20272 // special container for this cases
20273 if (n_shd_bnd_with_other_procs_have_node > 0 && // The node is on
20274 // shared
20275 // boundaries with
20276 // other processors
20277 !found_node_in_other_shared_boundaries) // The node has not
20278 // been previously
20279 // set as
20280 // shared with
20281 // other processors
20282 // (first time)
20283 {
20284 // Update the node pointer in all the (references) of the node
20285 this->update_other_proc_shd_bnd_node_helper(new_nod_pt,
20294
20295 } // if (!found_node_in_other_shared_boundaries)
20296 }
20297
20298 //========start of update_other_proc_shd_bnd_node_helper=================
20299 // Helper function that assigns/updates the references to the node so
20300 // that it can be found with any other reference
20301 //========================================================================
20302 template<class ELEMENT>
20304 Node*& new_node_pt,
20305 Vector<Vector<Vector<std::map<unsigned, Node*>>>>&
20312 std::map<Vector<unsigned>, unsigned>& node_name_to_global_index,
20314 {
20315 // Get the number of initial shared boundaries to correct the index
20316 // of the shared boundary
20317 const unsigned initial_shd_bnd_id = this->initial_shared_boundary_id();
20318
20319#ifdef PARANOID
20320 // Get the number of instances of the node on other shared
20321 // boundaries with other processors
20322 const unsigned n_data = other_processor_1.size();
20323#endif // #ifdef PARANOID
20324
20325 // Create the first node name
20330 node_name[3] = other_indexes[0];
20331
20332#ifdef PARANOID
20333 // Get the global node index, and all the names of the node
20334 std::map<Vector<unsigned>, unsigned>::iterator it =
20336 if (it == node_name_to_global_index.end())
20337 {
20338 std::ostringstream error_stream;
20339 error_stream << "The node name does not exist in the global node names\n"
20340 << "This is the name of the node\n"
20341 << "Name: iproc, jproc, ishd_bnd, idx\n"
20342 << "Name: " << node_name[0] << ", " << node_name[1] << ", "
20343 << node_name[2] << ", " << node_name[3] << "\n";
20344 throw OomphLibError(
20346 } // if (it!=node_name_to_global_index.end())
20347#endif // #ifdef PARANOID
20348
20349 // Get the global node index
20351 // Add the node to the global shared node container
20353 // Get the names
20355 // Get the number of names of the node
20356 const unsigned n_names = inode_names.size();
20357
20358#ifdef PARANOID
20359 // Check that the received names of the node are part of the global
20360 // node names
20362 // loop over the input node names
20363 for (unsigned j = 0; j < n_data; j++)
20364 {
20365 // loop over the inode_names
20366 for (unsigned k = 0; k < n_names; k++)
20367 {
20368 // Is this input name part of the global node names?
20369 if (inode_names[k][0] == other_processor_1[j] &&
20370 inode_names[k][1] == other_processor_2[j] &&
20372 inode_names[k][3] == other_indexes[j])
20373 {
20374 // Increase the number of found input node names in the
20375 // global node names
20377 }
20378
20379 } // for (k < n_names)
20380
20381 } // for (j < n_data)
20382
20383 // Were all the input node names found on the global node names?
20385 {
20386 std::ostringstream error_stream;
20388 << "Not all the node names of the current node were found on the\n"
20389 << "global node names. This happened when adding the node pointer\n"
20390 << "to the data structure that keeps tracks of nodes on shared\n"
20391 << "boundaries with other processors\n\n"
20392 << "These are the names of the current node\n"
20393 << "Name k: iproc, jproc, ishd_bnd, idx\n";
20394 for (unsigned j = 0; j < n_data; j++)
20395 {
20396 error_stream << "Name(" << j << "): " << other_processor_1[j] << ", "
20397 << other_processor_2[j] << ", "
20399 << "\n";
20400 }
20401
20402 error_stream << "\n\nThese are the names of the global node\n"
20403 << "Name k: iproc, jproc, ishd_bnd, idx\n";
20404 for (unsigned k = 0; k < n_names; k++)
20405 {
20406 error_stream << "Name(" << k << "): " << inode_names[k][0] << ", "
20407 << inode_names[k][1] << ", " << inode_names[k][2] << ", "
20408 << inode_names[k][3] << "\n";
20409 }
20410
20411 throw OomphLibError(
20413 }
20414#endif // #ifdef PARANOID
20415
20416 // Set the node pointer in all of its names
20417 for (unsigned j = 0; j < n_names; j++)
20418 {
20419 // Get the j-th node name
20420 const unsigned iproc = inode_names[j][0];
20421 const unsigned jproc = inode_names[j][1];
20422 const unsigned ishd_bnd = inode_names[j][2] - initial_shd_bnd_id;
20423 const unsigned index = inode_names[j][3];
20424
20425 // The info. is stored only in one direction
20426 // Get the smallest processor number
20427 if (iproc < jproc)
20428 {
20430 }
20431 else
20432 {
20434 }
20435
20436 } // for (j < n_names)
20437 }
20438
20439 // *********************************************************************
20440 // End communication functions
20441 // *********************************************************************
20442
20443 // *********************************************************************
20444 // BEGIN: Methods to perform load balance
20445 // *********************************************************************
20446
20447 //======================================================================
20448 /// Performs the load balancing for unstructured meshes, the
20449 /// load balancing strategy is based on mesh migration
20450 //======================================================================
20451 template<class ELEMENT>
20454 {
20455 oomph_info << "Load balance (unstructured mesh) [BEGIN]" << std::endl;
20456
20457 // This method can only be called when the mesh has been already
20458 // distributed
20459 if (!this->is_mesh_distributed())
20460 {
20461 std::ostringstream warning_message;
20463 << "\n===============================================================\n"
20464 << "The load balancing can only be performed in distributed meshes,\n"
20465 << "your mesh has not been distributed.\n"
20466 << "==============================================================="
20467 "\n\n";
20471 // Return
20472 return;
20473 }
20474
20475 // Get the number of processors
20476 const unsigned nproc = this->communicator_pt()->nproc();
20477 // Get the rank of the current processors
20478 const unsigned my_rank = this->communicator_pt()->my_rank();
20479
20480 // Check that there are at least two processors
20481 if (nproc == 1)
20482 {
20483 std::ostringstream warning_message;
20485 << "\n===============================================================\n"
20486 << "The load balancing can only be performed when there are at least\n"
20487 << "two procesors, the current number of processors is one.\n"
20488 << "==============================================================="
20489 "\n\n";
20493 // Return
20494 return;
20495 }
20496
20497 // Get the time before load balance
20498 double t_start_overall_load_balance = 0.0;
20499 if (Print_timings_level_load_balance > 1)
20500 {
20502 }
20503
20504 // Get the number of elements in the mesh before load balance
20505 const unsigned nelement_before_load_balance = this->nelement();
20506
20507#ifdef PARANOID
20508 // The number of elements in the mesh and the number of target
20509 // domains for the local non halo elements in the mesh should match
20510 if (nnon_halo_element() != target_domain_for_local_non_halo_element.size())
20511 {
20512 std::ostringstream error_message;
20513 error_message << "The number of non halo elements in the current mesh ("
20514 << nnon_halo_element() << ") and the number\n"
20515 << "of target areas for the local non halo elements ("
20517 << ") is different\n\n";
20518 throw OomphLibError(
20519 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
20520 }
20521#endif
20522
20523 // Backup pointers to elements in this mesh
20525 for (unsigned e = 0; e < nelement_before_load_balance; e++)
20526 {
20527 backed_up_ele_pt[e] = this->finite_element_pt(e);
20528 }
20529
20530 // =====================================================================
20531 // BEGIN: GET THE DOMAINS FOR THE HALO ELEMENTS
20532 // =====================================================================
20533
20534 // Get the time to get the domains of halo elements
20536 if (Print_timings_level_load_balance > 1)
20537 {
20539 }
20540
20541 // Get the new domains for the halo elements
20542
20543 // Send the new domains for the current haloed elements, and receive
20544 // the new domains for the current halo elements
20545 // -- 1) On the current processor get the new domains for the
20546 // haloed elements
20547 // -- 2) Then send this info. to all the processor that have a
20548 // halo copy of the element
20549
20550 // The storing for the new domains of the haloed elements (sent to
20551 // other processors)
20553 // The storing for the new domains of the halo elements (received
20554 // from other processors)
20556
20557 // First resize the containers by getting the current number of
20558 // halo/haloed elements within each processor
20559 for (unsigned iproc = 0; iproc < nproc; iproc++)
20560 {
20561 // There are no halo/haloed elements with myself (my_rank
20562 // processor)
20563 if (iproc != my_rank)
20564 {
20565 // Get the number of halo elements with iproc processor
20566 const unsigned n_halo_iproc = this->nroot_halo_element(iproc);
20567 // Resize the container
20569
20570 // Get the number of haloed elements with iproc processor
20571 const unsigned n_haloed_iproc = this->nroot_haloed_element(iproc);
20572 // Resize the container
20574 } // if (iproc != my_rank)
20575 } // for (iproc < nproc)
20576
20577#ifdef PARANOID
20578 // Count the number of found haloed elements
20580#endif
20581
20582 // Go through all the haloed elements and find their new domain
20583
20584 // Get the haloed elements with in each processor and check if the
20585 // element is haloed with the processor
20586 for (unsigned iproc = 0; iproc < nproc; iproc++)
20587 {
20588 // There are no halo/haloed elements with myself (my_rank
20589 // processor)
20590 if (iproc != my_rank)
20591 {
20592 // Get the number of haloed elements with iproc processor
20593 const unsigned n_haloed_iproc = this->nroot_haloed_element(iproc);
20594
20595 // Loop over the haloed elements
20596 for (unsigned ihd = 0; ihd < n_haloed_iproc; ihd++)
20597 {
20598 // Get the ihd-th haloed element with "iproc" processor
20600 this->root_haloed_element_pt(iproc, ihd);
20601
20602 // The counter for the nonhalo elements
20603 unsigned nh_count4 = 0;
20604 // Find the element in the general elements container
20605 for (unsigned e = 0; e < nelement_before_load_balance; e++)
20606 {
20607 // Get the e-th element
20608 GeneralisedElement* ele_pt = this->element_pt(e);
20609 // Check if the element is a nonhalo element
20610 if (!ele_pt->is_halo())
20611 {
20612 // Increase the counter for nonhalo elements, in case the
20613 // haloed element is found get the (nh_count4-1) position
20614 // in the target domains vector
20615 nh_count4++;
20616
20617 if (ele_pt == haloed_ele_pt)
20618 {
20619 // Get the new domain for this element
20620 const unsigned element_domain =
20622 // Here decrease the counter ---------------------^
20623
20624 // Set the new domain for the haloed element in the
20625 // special container
20627#ifdef PARANOID
20628 // Increase the counter
20630#endif
20631 // ... and break the "for" with the general
20632 // elements. Continue with the next haloed element
20633 break;
20634
20635 } // if (ele_pt == haloed_ele_pt))
20636
20637 } // if (!ele_pt->is_halo())
20638
20639 } // for (e < nelement_before_load_balance)
20640
20641 } // for (ihd < n_haloed_iproc)
20642
20643 } // if (iproc != my_rank)
20644
20645 } // for (iproc < nproc)
20646
20647#ifdef PARANOID
20648 // Check that all the haloed elements with all processors have been
20649 // found
20650 for (unsigned iproc = 0; iproc < nproc; iproc++)
20651 {
20652 // There are no halo/haloed elements with myself (my_rank
20653 // processor)
20654 if (iproc != my_rank)
20655 {
20656 // Get the number of haloed elements with "iproc" processor
20657 const unsigned n_haloed_iproc = this->nroot_haloed_element(iproc);
20658
20659 // Compare the number of found haloed elements with the current
20660 // number of haloed elements
20662 {
20663 std::ostringstream error_message;
20664 error_message << "The independent counting of found haloed elements ("
20666 << ") with processor (" << iproc
20667 << ") is not equal to the number of haloed elements ("
20668 << n_haloed_iproc << ") with processor (" << iproc
20669 << ")\n";
20670 throw OomphLibError(error_message.str(),
20673 } // if (nhaloed_iproc == counter_for_found_haloed_elements[iproc])
20674
20675 } // if (iproc != my_rank)
20676
20677 } // for (iproc < nproc)
20678#endif
20679
20680 // Now we have the new domains for the haloed elements
20681
20682 // Send this info. to the processor with a halo copy of the haloed
20683 // elements and set the new domains in the halo copies
20684
20685 // First put all the info. in a flat package array
20687 // Put in a vector the number of haloed elements within each
20688 // processor
20690 for (unsigned iproc = 0; iproc < nproc; iproc++)
20691 {
20692 // There are no halo/haloed elements with myself (my_rank
20693 // processor)
20694 if (iproc != my_rank)
20695 {
20696 // Get the number of haloed elements with "iproc" processor
20697 const unsigned n_haloed_ele_iproc = this->nroot_haloed_element(iproc);
20698 // Copy the number of haloed elements with "iproc" processor
20700 // Copy the new domains of the haloed elements in the flat
20701 // package
20702 for (unsigned i = 0; i < n_haloed_ele_iproc; i++)
20703 {
20706 } // for (i < n_haloed_ele_iproc)
20707
20708 } // if (iproc != my_rank)
20709
20710 } // for (iproc < nproc)
20711
20712 // The offsets of the flat package within each processor
20715 for (unsigned ip = 1; ip < nproc; ip++)
20716 {
20717 // Compute the offset to send the values to each processor
20721 } // for (ip < nproc)
20722
20723 // Prepare to receive the data
20724
20725 // Compute the number of data (halo elements) to receive from each
20726 // processor and the displacements within each processor
20727
20728 // Counter for the total number of halo elements within all processors
20730
20731 // Put in a vector the number of halo elements expected to receive
20732 // from each processor
20734 // Compute the number of total halo elements of (my_rank) this
20735 // processor with all other processors
20736 for (unsigned iproc = 0; iproc < nproc; iproc++)
20737 {
20738 // There are no halo/haloed elements with myself (my_rank
20739 // processor)
20740 if (iproc != my_rank)
20741 {
20742 // Get the number of halo elements with "iproc" processor
20743 const unsigned n_halo_ele_iproc = this->nroot_halo_element(iproc);
20744 // Copy the number of halo elements with "iproc" processor
20746 // Add the number of elements with this processor
20748 } // if (iproc != my_rank)
20749
20750 } // for (iproc < nproc)
20751
20752 // The offsets of the flat package within each processor
20755 for (unsigned ip = 1; ip < nproc; ip++)
20756 {
20757 // Compute the offset to receive the values from each processor
20761 } // for (ip < nproc)
20762
20763 // The flat container to receive the new domains of the halo
20764 // elements in the current processor
20765
20766 // The flat package where all the info. will be gather from the
20767 // other processors (the halo flat package)
20770
20771 // Perform the sending and receiving of information to and from all
20772 // processors
20774 &nhaloed_elements_with_iproc[0], // int *sendcnts
20775 &offset_haloed_elements_with_iproc[0], // int *sdispls
20776 MPI_UNSIGNED, // MPI_Datatype sendtype
20777 &new_domains_halo_flat_unsigned[0], // void *recvbuf
20778 &nhalo_elements_with_iproc[0], // int *recvcnts
20779 &offset_halo_elements_with_iproc[0], // int *rdispls
20780 MPI_UNSIGNED, // MPI_Datatype recvtype
20781 this->communicator_pt()->mpi_comm()); // MPI_Comm comm
20782
20783 // Once received the new domains for the halo elements, copy the
20784 // domains back to an easier to handle container (from the flat
20785 // package to the one with the different halo elements domains
20786 // within each processor)
20787 unsigned counter_new_domains_halo_ele = 0;
20788 for (unsigned iproc = 0; iproc < nproc; iproc++)
20789 {
20790 // There are no halo/haloed elements with myself (my_rank
20791 // processor)
20792 if (iproc != my_rank)
20793 {
20794 // Get the number of halo elements with "iproc"
20795 const unsigned ntmp_halo_elements_with_iproc =
20797 // Loop over the number of halo elements within "iproc" and copy
20798 // the elements from the flat package
20799 for (unsigned i = 0; i < ntmp_halo_elements_with_iproc; i++)
20800 {
20801 // Copy the new domain of the halo elements from the flat
20802 // package to an easier to use container
20805 }
20806 } // if (iproc != my_rank)
20807 } // for (iproc < nproc)
20808
20809 // The time to get domains of halo elements
20810 if (Print_timings_level_load_balance > 1)
20811 {
20812 oomph_info << "CPU for getting domains halo elements (load balance) [1]: "
20814 << std::endl;
20815 }
20816
20817 // =====================================================================
20818 // END: GET THE DOMAINS FOR THE HALO ELEMENTS
20819 // =====================================================================
20820
20821 // =====================================================================
20822 // BEGIN: CREATE FINITE ELEMENT LOCAL VERSIONS OF THE HALO(ED)
20823 // ELEMENTS
20824 // =====================================================================
20825
20826 // Get the time to get FiniteElement versions from Generalised
20827 // halo(ed) elements
20829 if (Print_timings_level_load_balance > 1)
20830 {
20832 }
20833
20834 // The finite element storage for the halo elements
20836 // The finite element storage for the haloed elements
20838 // Loop over the processors
20839 for (unsigned iproc = 0; iproc < nproc; iproc++)
20840 {
20841 // There are no halo(ed) elements with myself
20842 if (iproc != my_rank)
20843 {
20844 // Get the number of halo elements with the "iproc" processor
20845 const unsigned nhalo_ele_iproc = this->nroot_halo_element(iproc);
20846 // Get the halo elements with the "iproc" processor
20848 this->root_halo_element_pt(iproc);
20849 // Resize the finite element container
20851 // Loop over the halo elements
20852 for (unsigned ih = 0; ih < nhalo_ele_iproc; ih++)
20853 {
20854 // Get the finite element
20856 dynamic_cast<FiniteElement*>(halo_element_pt_iproc[ih]);
20857 // Store the finite element version of the element
20859 } // for (ih < nhalo_ele_iproc)
20860
20861 // Get the number of haloed elements with the "iproc" processor
20862 const unsigned nhaloed_ele_iproc = this->nroot_haloed_element(iproc);
20863 // Get the haloed elements with the "iproc" processor
20865 this->root_haloed_element_pt(iproc);
20866 // Resize the finite element container
20868 // Loop over the haloed elements
20869 for (unsigned ihd = 0; ihd < nhaloed_ele_iproc; ihd++)
20870 {
20871 // Get the finite element
20873 dynamic_cast<FiniteElement*>(haloed_element_pt_iproc[ihd]);
20874 // Store the finite element version of the element
20876 } // for (ih < nhaloed_ele_iproc)
20877
20878 } // if (iproc != my_rank)
20879
20880 } // for (iproc < nproc)
20881
20882 // The time to get FiniteElement versions from Generalised halo(ed)
20883 // elements
20884 if (Print_timings_level_load_balance > 1)
20885 {
20886 oomph_info << "CPU for getting finite element versions from generalised "
20887 "halo(ed) elements (load balance) [2]: "
20890 << std::endl;
20891 }
20892
20893 // =====================================================================
20894 // END: CREATE FINITE ELEMENT LOCAL VERSIONS OF THE HALO(ED)
20895 // ELEMENTS
20896 // =====================================================================
20897
20898 // =====================================================================
20899 // BEGIN: 1) PREPARE THE ELEMENTS THAT WILL BE SENT TO OTHER PROCESSORS
20900 // ---- HALO ELEMENTS ARE NOT CONSIDERED FOR SENDING
20901 // 2) ASSOCIATE THE NODES WITH THE NEW DOMAIN OF THE ELEMENTS
20902 // ---- THE SAME IS PERFORMED FOR NODES IN HALO ELEMENTS
20903 // =====================================================================
20904
20905 // Get the time to prepare elements to send to other processors
20907 if (Print_timings_level_load_balance > 1)
20908 {
20910 }
20911
20912 // Store the elements that will be sent to other processors
20914
20915 // Associate the nodes of each element with the processor the
20916 // element will live on
20917 std::map<Data*, std::set<unsigned>>
20919
20920 // Compute the elements that will be sent to other processor and
20921 // associate the nodes with the processor the element will live on
20922 unsigned nh_count3 = 0;
20923 for (unsigned e = 0; e < nelement_before_load_balance; e++)
20924 {
20925 // Get the element
20926 FiniteElement* ele_pt = this->finite_element_pt(e);
20927 // Only work with nonhalo elements
20928 if (!(ele_pt->is_halo()))
20929 {
20930 // Get the new domain for the elment
20931 const unsigned element_domain =
20933
20934 // Include the element in the corresponding vector
20936
20937 // Get the number of nodes on the element
20938 const unsigned n_nodes = ele_pt->nnode();
20939 // Loop over the nodes
20940 for (unsigned j = 0; j < n_nodes; j++)
20941 {
20942 // Get each node of the element
20944 // ... and associate it with element domains
20947
20948 } // for (j < n_nodes)
20949
20950 } // if (!(ele_pt->is_halo()))
20951
20952 } // for (e < nelement_before_load_balance)
20953
20954 // ... do the same for the halo elements (but do not add them to the
20955 // sending container since only the processor with the haloed
20956 // counterparts is in charge of that). Associate the nodes of the
20957 // halo elements with the processor they will live on
20958 for (unsigned iproc = 0; iproc < nproc; iproc++)
20959 {
20960 // There is no halo elements with myself
20961 if (iproc != my_rank)
20962 {
20963 // Get the number of halo elements with the "iproc" processor
20964 const unsigned n_halo_ele_iproc = this->nroot_halo_element(iproc);
20965 // Get the halo elements with the "iproc" processor
20967 this->root_halo_element_pt(iproc);
20968 // Loop over the halo elements with iproc
20969 for (unsigned ih = 0; ih < n_halo_ele_iproc; ih++)
20970 {
20971 // Get the new domain for the halo element
20973
20974 // Get the finite element
20976 dynamic_cast<FiniteElement*>(halo_element_pt_iproc[ih]);
20977
20978 // Get the number of nodes on the halo element
20979 const unsigned n_nodes = ele_pt->nnode();
20980 // Loop over the nodes
20981 for (unsigned j = 0; j < n_nodes; j++)
20982 {
20983 // Get each node of the halo element
20985
20986 // ... and associate it with element domains
20989
20990 } // for (j < n_nodes)
20991
20992 } // for (ih < nhalo_ele_iproc)
20993
20994 } // if (iproc != my_rank)
20995
20996 } // for (iproc < nproc)
20997
20998 // The time to prepare elements to send to other processors
20999 if (Print_timings_level_load_balance > 1)
21000 {
21001 oomph_info << "CPU for preparing elements to send to other processors "
21002 "(load balance) [3]: "
21004 << std::endl;
21005 }
21006
21007 // Now all the nodes are associated with the processor where the
21008 // element will live on. This is performed for the nonhalo and halo
21009 // elements
21010
21011 // =====================================================================
21012 // END: 1) PREPARE THE ELEMENTS THAT WILL BE SENT TO OTHER PROCESSORS
21013 // ---- HALO ELEMENTS ARE NOT CONSIDERED FOR SENDING
21014 // 2) ASSOCIATE THE NODES WITH THE NEW DOMAIN OF THE ELEMENTS
21015 // ---- THE SAME IS PERFORMED FOR NODES IN HALO ELEMENTS
21016 // =====================================================================
21017
21018 // =====================================================================
21019 // BEGIN: COMPUTE THE NEW LOCAL HALO ELEMENTS OF ALL PROCESSORS IN THE
21020 // CURRENT PROCESSOR
21021 // ----- FOR NONHALO ELEMENTS AND FOR HALO ELEMENTS
21022 // =====================================================================
21023
21024 // Get the time to compute new local halo elements within all
21025 // processors
21027 if (Print_timings_level_load_balance > 1)
21028 {
21030 }
21031
21032 // Before sending the elements across compute the new local
21033 // halo/haloed elements of each processor. Each processor could have
21034 // elements that will be part of the new halo/haloed elements of
21035 // another processors, then these processors need to compute the
21036 // relations that may happen among these other processors
21037
21038 // Example:
21039 // Processor 1 may have elements that will be sent to processor 3
21040 // and 4. These processors need to know about the new halo elements
21041 // betweeen them but at this moment only processor 1 can compute that
21042 // info., since it is the only one that currently has that info.
21043
21044 // Store the new local-halo elements of each processor, the HALOED
21045 // elements are also stored in the container, only needs to INVERT
21046 // the indexes. For example, the HALO elements of processor 2 with
21047 // processor 3 are stored in new_local_halo_element_pt[2][3], and
21048 // the HALOED elements of processor 2 with processor 3 are stored in
21049 // new_local_halo_element_pt[3][2]. Notice that these are also the
21050 // halo elements of processor 3 with 2
21051
21052 // How to identify the new local halo/haloed element: 1) Loop over
21053 // the element; 2) Only work with nonhalo elements; 3) If the
21054 // element is not assigned to the current processor (iproc) then
21055 // check; 4) Is one of its nodes assiociated to the iproc processor?
21056 // 5) If yes the element is a halo in the iproc processor whose
21057 // nonhalo counter part (haloed) lives in the domain assigned to the
21058 // element
21060
21061 // Loop over the processors
21062 for (unsigned iproc = 0; iproc < nproc; iproc++)
21063 {
21064 // Resize the container
21066
21067 // Boolean to know which elements have been already added to the
21068 // new local halo scheme in "iproc"
21070 nproc);
21071
21072 // Go through all the elements and identify the new local halo
21073 // elements of "iproc"
21074 unsigned nh_count5 = 0;
21075 for (unsigned e = 0; e < nelement_before_load_balance; e++)
21076 {
21077 // Get the element
21078 FiniteElement* ele_pt = this->finite_element_pt(e);
21079 // Only work with nonhalo elements
21080 if (!(ele_pt->is_halo()))
21081 {
21082 // Get the domain to which the current element is associated
21083 const unsigned ele_domain =
21085 // If the current element is not associated to the "iproc"
21086 // processor then it could be a halo element
21087 if (ele_domain != iproc)
21088 {
21089 // Get the number of nodes
21090 const unsigned nnodes = ele_pt->nnode();
21091 // Loop over the nodes
21092 for (unsigned j = 0; j < nnodes; j++)
21093 {
21095 // Check if the node is associated with the current
21096 // "iproc" processor
21097 std::set<unsigned>::iterator it =
21099 .find(iproc);
21100 // If it is found then the element is a halo-element
21101 if (it !=
21103 .end())
21104 {
21105 // Add the element as new local-halo element with the
21106 // "ele_domain" processor. The non-halo counterpart will
21107 // be located on "ele_domain" processor after sending
21108 // elements across
21110 {
21111 // The element is a halo element on "iproc" with
21112 // "ele_domain"
21114 ele_pt);
21115 // Mark as done
21117 } // if (!new_local_halo_already_added[ele_domain][ele_pt])
21118 } // One of the nodes lies on an element on the current
21119 // "iproc" processor
21120 } // for (j < nnodes)
21121 } // if (ele_domain != iproc)
21122 } // if (!(ele_pt->is_halo()))
21123 } // for (e < nelement_before_load_balance)
21124
21125 // Now do the same with the halo elements, we need to find those
21126 // halo elements that continue being halo elements but possibly
21127 // with/on another processor. The pair of processors where a
21128 // possible shared boundary is created needs to be notified.
21129
21130 // Example
21131 //
21132 // ---------------* *---------------
21133 // | |* *| |
21134 // | |* *| |
21135 // | New domain |* *| New domain | * Mark the position
21136 // | proc 1 |* *| proc 3 | of halo elements
21137 // | |* *| |
21138 // | |* *| |
21139 // ---------------* *---------------
21140 // Proc 1 Proc 2
21141
21142 // Processor 1: The halo elements on processor 1 continue being halo ON
21143 // PROCESSOR 1, but now WITH PROCESSOR 3
21144
21145 // Processor 2: The halo elements on processor 2 continue being
21146 // halo BUT now ON PROCESSOR 3 WITH PROCESSOR 1
21147
21148 // The current processor (my_rank) also needs to consider the halo
21149 // elements that will be halo elements of other processor with
21150 // another processor. The case of processor 2
21151
21152 // Loop over all the halo elements in the current processor and
21153 // check if they will be halo with the "iproc" processor
21154 for (unsigned jproc = 0; jproc < nproc; jproc++)
21155 {
21156 // There are no halo elements with myself (the old halo elements
21157 // were halo in the "my_rank" processor)
21158 if (jproc != my_rank)
21159 {
21160 // Get the number of halo elements with the "jproc" processor
21161 const unsigned n_halo_ele_jproc = this->nroot_halo_element(jproc);
21162 // Get the halo elements with the "jproc" processor
21164 this->root_halo_element_pt(jproc);
21165 // ... and check if any of those elements is a new halo
21166 // element with the "iproc" processor
21167 for (unsigned jh = 0; jh < n_halo_ele_jproc; jh++)
21168 {
21169 // Get the new domain for the halo element
21170 const unsigned ele_domain = new_domains_halo_elements[jproc][jh];
21171
21172 // If the current element is not associated to the "iproc"
21173 // processor then it could be a halo element on "iproc" with
21174 // "ele_domain".
21175
21176 // NOTE OUTDATE: Check if the halo element is going to be
21177 // sent to this processor (my_rank), if that is the case
21178 // then we don't need to add it to the set of new halo
21179 // elements with any other processor since any possible
21180 // shared boundary will be created when checking for the
21181 // intersection of the sent and received elements
21182
21183 // if (ele_domain != iproc && ele_domain != my_rank)
21184
21185 // NOTE UPDATE: Only check if the halo element is not going
21186 // to be part of the iproc processor, not required to avoid
21187 // those halo elements whose domain is the current rank
21188 // (my_rank). When the shared boundaries are computed, these
21189 // last elements can not create a shared boundary since no
21190 // haloed elements (that shared an edge) are found for
21191 // them. By considering also those halo elements whose new
21192 // domain is the current one (commenting "ele_domain !=
21193 // my_rank") the current processor can compute shared
21194 // boundaries with the iproc processor with help of its old
21195 // halo elements but that will become nonhalo elements, in
21196 // fact they will become haloed elements The halo element is
21197 // not sent to the "element_domain" processor and is not
21198 // passed to the array used to create the new shared
21199 // boundaries "new_shared_boundary_element_pt" because of
21200 // its halo condition
21201 if (ele_domain != iproc)
21202 {
21203 // Get the finite element
21205 dynamic_cast<FiniteElement*>(halo_element_pt_jproc[jh]);
21206 // Get the number of nodes on the halo element
21207 const unsigned nnodes = ele_pt->nnode();
21208 // Loop over the nodes
21209 for (unsigned j = 0; j < nnodes; j++)
21210 {
21211 // Get each node of the halo element
21213
21214 // Check if the node is associated with the "iproc"
21215 // processor
21216 std::set<unsigned>::iterator it =
21218 .find(iproc);
21219 // If it is found then the element is a halo-element
21220 if (it !=
21222 .end())
21223 {
21224 // Add the element as new local-halo element with
21225 // the "ele_domain" processor. The non-halo
21226 // counterpart will be located on "ele_domain"
21227 // processor. Because this is a old-halo element it
21228 // will not be sent to the "element_domain" processor
21230 {
21231 // The element is a halo element on "iproc" with
21232 // "ele_domain"
21234 ele_pt);
21236
21237 // Break the for of the nodes, the element has been
21238 // already added to the new_local_halo_element_pt
21239 // structure
21240 break;
21241
21242 } // if (!new_local_halo_already_added[ele_domain][ele_pt])
21243
21244 } // One of the nodes lies on an element belonging to
21245 // "iproc" processor
21246
21247 } // for (j < nnodes)
21248
21249 } // if (ele_domain != iproc)
21250
21251 } // for (jh < n_halo_ele_jproc)
21252
21253 } // if (jproc != my_rank) // The old halo elements are halo
21254 // with other processors except with "my_rank"
21255
21256 } // for (jproc < nproc): This is the one that goes for the halo
21257 // elements in the current processor to find the new halo
21258 // elements
21259
21260 } // for (iproc < nproc)
21261
21262 // Get the time to compute new local halo elements within all
21263 // processors
21264 if (Print_timings_level_load_balance > 1)
21265 {
21267 << "CPU for computing new local halo elements (load balance) [4]: "
21269 << std::endl;
21270 }
21271
21272 // =====================================================================
21273 // END: COMPUTE THE NEW LOCAL HALO ELEMENTS OF ALL PROCESSORS IN THE
21274 // CURRENT PROCESSOR
21275 // ----- FOR NONHALO ELEMENTS AND FOR HALO ELEMENTS
21276 // =====================================================================
21277
21278 // =====================================================================
21279 // BEGIN: COMPUTE THE NEW LOCAL SHARED BOUNDARY ELEMENTS AND THE
21280 // FACE ELEMENTS. THE SUBSET OF THE ELEMENTS TO SENT THAT ARE PART
21281 // OF THE NEW LOCAL SHARED BOUNDARY ELEMENTS ARE IDENTIFIED TO BE
21282 // MARKED AS HALOED ELEMENTS AND BELONGING TO THE SHARED BOUNDARY
21283 // ELEMENTS IN THE RECEIVED PROCESSOR
21284 // =====================================================================
21285
21286 // Get the time to compute new local shared boundary elements
21288 if (Print_timings_level_load_balance > 1)
21289 {
21291 }
21292
21293 // Store the new local-shared boundary elements and the face indexes
21294 // The halo elements and halo face indexes
21299
21300 // Allocate enough memory for the containers
21301 for (unsigned iproc = 0; iproc < nproc; iproc++)
21302 {
21305 } // for (iproc < nproc)
21306
21307 // Get the elements that create the new local-halo-shared
21308 // boundaries, mark them and identify the face that lies on the
21309 // shared boundary. The new local-halo-shared boundary elements are
21310 // actually a sub-set of the halo elements of each processor with in
21311 // each processor
21312 for (unsigned iproc = 0; iproc < nproc; iproc++)
21313 {
21314 // Star from jproc = iproc + 1 to avoid double creation of shared
21315 // boundary elements, any shared boundary element identified
21316 // between processor "iproc" and "jproc" is also established as
21317 // shared boundary element between processor "jproc" and "iproc"
21318 for (unsigned jproc = iproc + 1; jproc < nproc; jproc++)
21319 {
21320 this->get_shared_boundary_elements_and_face_indexes(
21327 } // for (jproc < nproc)
21328 } // for (iproc < nproc)
21329
21330 // The time to compute new local shared boundary elements
21331 if (Print_timings_level_load_balance > 1)
21332 {
21333 oomph_info << "CPU for computing new local shared boundary elements "
21334 "(load balance) [5]: "
21337 << std::endl;
21338 }
21339
21340 // =====================================================================
21341 // END: COMPUTE THE NEW LOCAL SHARED BOUNDARY ELEMENTS AND THE FACE
21342 // ELEMENTS. THE SUBSET OF THE ELEMENTS TO SENT THAT ARE PART OF THE
21343 // NEW LOCAL SHARED BOUNDARY ELEMENTS ARE IDENTIFIED TO BE MARKED AS
21344 // HALOED ELEMENTS AND BELONGING TO THE SHARED BOUNDARY ELEMENTS IN
21345 // THE RECEIVED PROCESSOR
21346 // =====================================================================
21347
21348 // =====================================================================
21349 // BEGIN: SEND THE ELEMENTS AND IDENTIFY THOSE THAT ARE PART OF THE
21350 // SHARED BOUNDARIES AND HALOED WITH OTHER PROCESSORS
21351 // =====================================================================
21352
21353 // Get the time to send the elements to their new processor in
21354 // charge
21356 if (Print_timings_level_load_balance > 1)
21357 {
21359 }
21360
21361 // Sort the nodes on shared boundaries so that they have the same
21362 // order on all the shared boundaries, this is required to know the
21363 // possible shared nodes among processors
21364 this->sort_nodes_on_shared_boundaries();
21365
21366 // Store the received elements from each processor
21368
21369 // The haloed elements and haloed face indexes, these store the
21370 // haloed elements received from "iproc" but that are haloed with
21371 // "jproc". The elements are received from "iproc" which was the
21372 // processor that computed the haloed relation of the "my_rank"
21373 // processor with "jproc"
21378
21379 // Container where to store the nodes on shared boundaries not
21380 // associated with the processor that receives the elements/nodes
21381 // other_proc_shd_bnd_node_pt[iproc][jproc][shd_bnd_id][index]
21384 // Resize the container
21385 for (unsigned iproc = 0; iproc < nproc; iproc++)
21386 {
21387 // Resize the container
21389 for (unsigned jproc = 0; jproc < nproc; jproc++)
21390 {
21391 // Get the number of shared boundaries (OLD shared boundaries)
21392 const unsigned initial_shd_bnd_id = this->initial_shared_boundary_id();
21393 const unsigned final_shd_bnd_id = this->final_shared_boundary_id();
21396 } // for (jproc < nproc)
21397
21398 } // for (iproc < nproc)
21399
21400 // Store the global node names
21401 // global_node_name[x][ ][ ] Global node number
21402 // global_node_name[ ][x][ ] Global node names
21403 // global_node_name[ ][ ][x] Global node info.
21405
21406 // Creates a map between the node name and the index of the global
21407 // node so we can access all its node names
21408 std::map<Vector<unsigned>, unsigned> node_name_to_global_index;
21409
21410 // Store the global shared nodes pointers
21412
21413 // Compute all the names of the nodes and fill in the
21414 // "other_proc_shd_bnd_node_pt" structure with the nodes that live
21415 // on this processor (my_rank) by looking over all their names
21416 compute_global_node_names_and_shared_nodes(other_proc_shd_bnd_node_pt,
21420
21421 // From the elements received from each processor, store the haloed
21422 // information of the element, it means, the processor with which it
21423 // is haloed and the haloed index with that processor
21426 // [x][][] : The receiver processor (the original processor)
21427 // [][x][] : The processor with which the receiver processor has
21428 // haloed elements
21429 // [][][x]: The haloed element number
21430
21431 // Resize the container
21432 for (unsigned iproc = 0; iproc < nproc; iproc++)
21433 {
21435 } // for (iproc < nproc)
21436
21437 // Go through all processors and send the corresponding elements to
21438 // each one
21439 for (unsigned iproc = 0; iproc < nproc; iproc++)
21440 {
21441 if (iproc != my_rank)
21442 {
21443 // -----------------------------------------------------------
21444 // Send (package) information of the elements
21445 // -----------------------------------------------------------
21446
21447 // Keep track of the currently sent elements
21449 // Keep track of the currently sent nodes to the iproc processor
21451
21452 // Clear send and receive buffers
21453 Flat_packed_unsigneds.clear();
21454 Flat_packed_doubles.clear();
21455#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
21456 Flat_packed_unsigneds_string.clear();
21457#endif
21458
21459 // Get the number of elements to send to iproc processor
21460 const unsigned nelements_to_send = elements_to_send_pt[iproc].size();
21461
21462 // The very first data of the flat package sent to processor
21463 // iproc is the number of elements that will be sent, this data
21464 // is used by the receiver processor to loop over the number of
21465 // expected elements to receive
21466 Flat_packed_unsigneds.push_back(nelements_to_send);
21467#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
21468 std::stringstream junk;
21469 junk << "Number of elements to send from processor " << my_rank
21470 << " to processor " << iproc << ": (" << nelements_to_send << ")";
21471 Flat_packed_unsigneds_string.push_back(junk.str());
21472#endif
21473
21474 // Loop over the elements to sent
21475 for (unsigned e = 0; e < nelements_to_send; e++)
21476 {
21477 // Get the element to send
21479
21480 // Get the current number of sent elements
21481 const unsigned ncurrently_sent_elements =
21483
21484 // Try to add the element
21485 const unsigned index_ele = try_to_add_element_pt_load_balance(
21487
21488 // Element needs to be added
21490 {
21491 Flat_packed_unsigneds.push_back(1);
21492#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
21493 Flat_packed_unsigneds_string.push_back(
21494 "Element needs to be constructed");
21495#endif
21496
21497 // Get required info. related with the element
21498 get_required_elemental_information_load_balance_helper(
21500
21501 // Get the number of nodes in the element
21502 const unsigned nnodes = send_ele_pt->nnode();
21503
21504 // Loop over the nodes in the element
21505 for (unsigned j = 0; j < nnodes; j++)
21506 {
21508
21509 // Package the info. of the nodes
21510 add_node_load_balance_helper(iproc, // The destination process
21513 node_pt);
21514
21515 } // for (j < nnodes)
21516
21517 } // if (index_ele == ncurrently_sent_elements)
21518 else
21519 {
21520 Flat_packed_unsigneds.push_back(0);
21521#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
21522 Flat_packed_unsigneds_string.push_back("Element already exists");
21523#endif
21524 Flat_packed_unsigneds.push_back(index_ele);
21525#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
21526 Flat_packed_unsigneds_string.push_back("Index of existing element");
21527#endif
21528 } // else if (index_ele == ncurrently_sent_elements)
21529
21530 } // for (e < nelements_to_send)
21531
21532 // After storing the info. of the elements identify the indexes
21533 // of the "new_local_halo_shared_boundary_elements" in the
21534 // "currently_send_elements" vector, these elements will be
21535 // identified as "new_received_haloed_shared_boundary_elements"
21536 // on the "receiver" processor
21537
21538 // Each processor has information of every other processor so we
21539 // need to send all the corresponding info. to the other
21540 // processors. Processor 1 may have information of the relation
21541 // (halo elements) between processor 3 and 4 say, so processor 1
21542 // needs to let know processor 3 and 4 what this relation is
21543 // (which are the shared-elements among these processors)
21544
21545 for (unsigned jproc = 0; jproc < nproc; jproc++)
21546 {
21547 // Get the number of new local-halo shared boundary elements
21548 // between processor "jproc" and "iproc" (we invert the index
21549 // since we really want the haloed elements, those elements
21550 // that we have just sent)
21553
21554 // The vector with the info. of the indexes
21556
21557 // The number of found shared boundary elements in the sent
21558 // container (only consider the nonhalo elements)
21560 // The number of nonhalo elements in the new local halo shared
21561 // boundary elements
21563
21564 // Loop over the local halo shared boundary elements between
21565 // processor jproc and iproc
21566 for (unsigned e = 0;
21568 e++)
21569 {
21570 // Get the shared boundary element
21573
21574 // Only consider the nonhalo elements since the halo
21575 // elements were no considered for sending
21576 if (!shared_ele_pt->is_halo())
21577 {
21579
21580 // Now find the index on the currently sent elements
21581
21582 // Get the current number of sent elements
21583 const unsigned ncurrently_sent_elements =
21585 // Loop over the sent elements
21586 for (unsigned ics = 0; ics < ncurrently_sent_elements; ics++)
21587 {
21590
21591 // Is this the element?
21593 {
21594 // Store the index on the sent elements of the local
21595 // halo shared boundary element
21597 // Increase the number of found new local halo shared
21598 // bound element index
21600 // We have found it, no need to further search
21601 break;
21602 } // if (currently_sent_ele_pt == shared_ele_pt)
21603
21604 } // for (ics < ncurrently_sent_elements)
21605
21606 } // if (!shared_ele_pt->is_halo())
21607
21608 } // for (e < niproc_new_local_halo_shared_boundary_ele)
21609
21610#ifdef PARANOID
21613 {
21614 std::ostringstream error_message;
21615 error_message << "Was only possible to identify ("
21617 << ") of ("
21619 << ") shared "
21620 << "elements between\nprocessor (" << iproc
21621 << ") and (" << jproc << ") "
21622 << "when sending elements to processor (" << iproc
21623 << ")\n\n";
21624 throw OomphLibError(error_message.str(),
21627 }
21628#endif
21629
21630 // Send a flag for synchronisation issues
21631 Flat_packed_unsigneds.push_back(9999);
21632#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
21633 std::stringstream junk;
21634 junk << "Flag for synchronisation 9999";
21635 Flat_packed_unsigneds_string.push_back(junk.str());
21636#endif
21637
21638 // Send the number of nonhalo new local-shared boundary
21639 // elements of processor "iproc" with processor "jproc"
21640 Flat_packed_unsigneds.push_back(
21642#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
21643 std::stringstream junk2;
21644 junk2 << "Number of new local halo shared boundary elements "
21646 Flat_packed_unsigneds_string.push_back(junk2.str());
21647#endif
21648
21649 // Send the indexes and the face indexes of the shared
21650 // boundary elements
21651 unsigned counter_nonhalo_sent = 0;
21652 // Loop over the local halo shared boundary elements between
21653 // processor jproc and iproc
21654 for (unsigned e = 0;
21656 e++)
21657 {
21658 // Get the shared boundary element
21661
21662 // Only consider the nonhalo elements since the halo
21663 // elements were no considered for sending
21664 if (!shared_ele_pt->is_halo())
21665 {
21666 // Get the index on the sent elements of the current
21667 // nonhalo shared boundary element
21668 const unsigned ele_index =
21671 // ... and get the face index
21672 const unsigned face_index =
21674 [e];
21675
21676 // Send the index on the sent elements of the new local
21677 // halo shared boundary element
21678 Flat_packed_unsigneds.push_back(ele_index);
21679#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
21680 std::stringstream junk;
21681 junk << "The index of the halo shared boundary element "
21682 << ele_index;
21683 Flat_packed_unsigneds_string.push_back(junk.str());
21684#endif
21685
21686 // Send the face index of the new local halo shared boundary
21687 // element
21688 Flat_packed_unsigneds.push_back(face_index);
21689#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
21690 std::stringstream junk2;
21691 junk2 << "The face index of the halo shared boundary element "
21692 << face_index;
21693 Flat_packed_unsigneds_string.push_back(junk2.str());
21694#endif
21695
21696 } // if (!shared_ele_pt->is_halo())
21697
21698 } // for (e < niproc_new_local_halo_shared_boundary_ele)
21699
21700 } // for (jproc < nproc)
21701
21702 // ----------------------------------------------------------
21703 // Send the info. perform the communications
21704 // ----------------------------------------------------------
21705 // Processor to which send the info.
21706 int send_proc = static_cast<int>(iproc);
21707 // Processor from which receive the info.
21708 int recv_proc = static_cast<int>(iproc);
21709 send_and_receive_elements_nodes_info(send_proc, recv_proc);
21710
21711 // ----------------------------------------------------------
21712 // Receive (unpackage) the info of the elements
21713 // ----------------------------------------------------------
21714
21715 // Keep track of the currently created elements
21717 // Keep track of the currently created nodes
21719
21720 // Reset the counters
21721 Counter_for_flat_packed_doubles = 0;
21722 Counter_for_flat_packed_unsigneds = 0;
21723
21724#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
21725 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
21726 << " Number of elements need to be constructed "
21727 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
21728 << std::endl;
21729#endif
21730
21731 // Read the number of elements that need to be created
21732 const unsigned nelements_to_create =
21733 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
21734
21735 for (unsigned e = 0; e < nelements_to_create; e++)
21736 {
21737 // Create the element from received info. of "iproc"
21738 // processor on the current processor
21739 create_element_load_balance_helper(iproc,
21748 }
21749
21750 // Copy the received elements from "iproc" processor
21751
21752 // Number of received elements
21755 for (unsigned e = 0; e < nreceived_elements; e++)
21756 {
21758 }
21759
21760 // Go for the haloed elements received from processor "iproc"
21761 // but haloed with "jproc"
21762
21763 // Allocate memory for the containers
21766 nproc);
21767
21768 // Loop over the processors
21769 for (unsigned jproc = 0; jproc < nproc; jproc++)
21770 {
21771 // Read the synchronisation flag
21772 const unsigned synchronisation_flag =
21773 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
21774
21775 if (synchronisation_flag != 9999)
21776 {
21777 std::ostringstream error_message;
21778 error_message << "The synchronisation flag was not read, the\n"
21779 << "information sent between processor (" << my_rank
21780 << ") "
21781 << "and (" << iproc
21782 << ")\nis no longer synchronised\n\n";
21783 throw OomphLibError(error_message.str(),
21786 }
21787
21788 // Read the number of elements that will be part of the new
21789 // received haloed shared boundary elements received from "iproc"
21790 // and haloed with "jproc"
21792 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
21793
21794 // Loop over the new received haloed shared boundary elements
21795 for (unsigned e = 0;
21797 e++)
21798 {
21799 // Read the index of the new received haloed shared boundary
21800 // ele with "jproc"
21801 const unsigned ele_index =
21802 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
21803 // Read the face index for the new received haloed shared
21804 // boundary element
21805 const unsigned face_index =
21806 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
21807
21808 // Get the element
21811
21812 // Add the element to the new received-haloed shared
21813 // boundary elements. Received from "iproc" but haloed with
21814 // "jproc" processor
21816 .push_back(shared_ele_pt);
21817 // Store the face index
21819 .push_back(face_index);
21820
21821 } // for (e < niproc_jproc_read_new_local_shared_boundary_ele)
21822
21823 } // for (jproc < nproc)
21824
21825 } // if (iproc != my_rank)
21826
21827 } // for (iproc < nproc)
21828
21829 // The time to send the elements to their new processor in charge
21830 if (Print_timings_level_load_balance > 1)
21831 {
21832 oomph_info << "CPU for sending elements to their new processors (load "
21833 "balance) [6]: "
21836 << std::endl;
21837 }
21838
21839 // =====================================================================
21840 // END: SEND THE ELEMENTS AND IDENTIFY THOSE THAT ARE PART OF THE
21841 // SHARED BOUNDARIES AND HALOED WITH OTHER PROCESSORS
21842 // =====================================================================
21843
21844 // =====================================================================
21845 // BEGIN: GET ANY ADDITIONAL SHARED BOUNDARY BY THE INTERSECTION OF
21846 // THE ELEMENTS SENT TO PROCESSOR "IPROC" AND THE ELEMENTS RECEIVED
21847 // FROM PROCESSOR "IPROC". IF ANY NEW SHARED BOUNDARY IS FOUND, IT
21848 // IS CREATED BY THE OLD HALO ELEMENTS (RECEIVED ELEMENTS) THAT HAVE
21849 // NOW BECOME PART OF THE DOMAIN AND THE OLD HALOED ELEMENTS (SENT
21850 // ELEMENTS)
21851 // =====================================================================
21852
21853 // Get the time to compute any additional shared boundary
21855 if (Print_timings_level_load_balance > 1)
21856 {
21858 }
21859
21860 // Store any additional elements that may create a shared boundary,
21861 // after sending elements from one to other processor check for any
21862 // new possible shared boundaries
21865 nproc);
21868 nproc);
21869
21870 // Compute any additional shared boundaries by checking the
21871 // intersection between the received elements from each processor
21872 // and the elements just sent to that processor, the lowest
21873 // processors number loops over its received elements and the
21874 // highest loops over its sent elements (halo elements that have
21875 // become part of the domain now can create shared boundaries with
21876 // other processor)
21877
21878 // Note: These additional shared boundaries may be created by the
21879 // elements that previously were halo but now have become part of
21880 // the processor (the received elements), and the elements that were
21881 // previously part of the processor but now have become halo (a
21882 // subset of the sent-elements)
21883
21884 // Then these new shared boundaries come from the intersection of
21885 // the new-haloed elements (received elements) and the new-halo
21886 // elements (sent elements). These could be computed previously (in
21887 // the computing of the local new-halo and local new-haloed elements
21888 // usign the info. of the new domains for the old halo elements),
21889 // however, it was decided to perform the computation here in order to
21890 // avoid the identification of the old halo element that was part of a
21891 // shared boundary in the set of just received elements
21892 for (unsigned iproc = 0; iproc < nproc; iproc++)
21893 {
21894 if (my_rank < iproc)
21895 {
21896 // Lowest processor loops over the received elements
21897 this->get_shared_boundary_elements_and_face_indexes(
21904
21905 } // if (my_rank < iproc)
21906 else if (my_rank > iproc)
21907 {
21908 // Highest processor loops over the sent elements
21909 this->get_shared_boundary_elements_and_face_indexes(
21916
21917 } // else if (my_rank > iproc)
21918
21919 } // for (iproc < nproc)
21920
21921 // The time to compute any additional shared boundary
21922 if (Print_timings_level_load_balance > 1)
21923 {
21925 << "CPU for computing additional shared boundaries (load balance) [7]: "
21928 << std::endl;
21929 }
21930
21931 // =====================================================================
21932 // END: GET ANY ADDITIONAL SHARED BOUNDARY BY THE INTERSECTION OF
21933 // THE ELEMENTS SENT TO PROCESSOR "IPROC" AND THE ELEMENTS RECEIVED
21934 // FROM PROCESSOR "IPROC". IF ANY NEW SHARED BOUNDARY IS FOUND, IT
21935 // IS CREATED BY THE OLD HALO ELEMENTS (RECEIVED ELEMENTS) THAT HAVE
21936 // NOW BECOME PART OF THE DOMAIN AND THE OLD HALOED ELEMENTS (SENT
21937 // ELEMENTS)
21938 // =====================================================================
21939
21940 // =====================================================================
21941 // BEGIN: SORT THE SHARED BOUNDARIES SO THAT THEY ARE CREATED IN THE
21942 // SAME ORDER IN THE INVOLVED PROCESSORS (A PAIR OF PROCESSORS)
21943 // =====================================================================
21944
21945 // Get the time to sort shared boundaries
21947 if (Print_timings_level_load_balance > 1)
21948 {
21950 }
21951
21952 // Once computed the elements that create the shared boundaries,
21953 // sort them so that the shared boundaries are created at the same
21954 // order in both processors that define the shared boundary
21955
21956 // The order is like this
21957
21958 // Lowest processors
21959 // 1) Shared boundary elements received from processors (local in
21960 // other processors)
21961 // 2) Local shared boundary elements (do not include halo elements)
21962 // 3) Shared boundary elements by intersection (already sorted)
21963
21964 // Highest processors
21965 // 1) Local shared boundary elements (do not include halo elements)
21966 // 2) Shared boundary elements received from processors (local in
21967 // other processors)
21968 // 3) Shared boundary elements by intersection (already sorted)
21969
21972 for (unsigned iproc = 0; iproc < nproc; iproc++)
21973 {
21974 // Lower processor
21975 if (my_rank < iproc)
21976 {
21977 // Copy the elements received from processor "jproc" but that
21978 // are haloed with "iproc" processor
21979 for (unsigned jproc = 0; jproc < nproc; jproc++)
21980 {
21981 // Can not receive elements from itself
21982 if (jproc != my_rank)
21983 {
21984 // Get the number of elements to copy from received processors
21987 .size();
21988 for (unsigned e = 0; e < nrecvd_haloed_shared_bound_ele_jproc_iproc;
21989 e++)
21990 {
21991 // Get the element
21994 // Get the face index
21995 const unsigned face_index =
21997 [iproc]
21998 [e];
21999
22000 // Add the elements to the containers
22003 face_index);
22004
22005 } // for (e < nrecvd_haloed_shared_bound_ele_iproc_jproc)
22006
22007 } // if (jproc != my_rank)
22008
22009 } // for (jproc < nproc)
22010
22011 // Then the local shared haloed (invert the indexes to get the
22012 // haloed elements)
22016 e++)
22017 {
22018 // Get the element
22021 // Get the face index
22022 const unsigned face_index =
22024 [e];
22025
22026 // Only include the element if it is nonhalo (this may be an
22027 // old halo element that helped to indentify a shared boundary
22028 // with iproc)
22029 if (!ele_pt->is_halo())
22030 {
22031 // Add the elements to the containers
22033 new_shared_boundary_element_face_index[iproc].push_back(face_index);
22034 } // if (!ele_pt->is_halo())
22035
22036 } // for (e < nlocal_haloed_shared_bound_ele_iproc_my_rank)
22037
22038 // ... and finally any additional shared boundary elements from
22039 // tmp_group1
22040 const unsigned ntmp_group1_shared_bound_ele_iproc =
22042 for (unsigned e = 0; e < ntmp_group1_shared_bound_ele_iproc; e++)
22043 {
22044 // Get the element
22047 // Get the face index
22048 const unsigned face_index =
22050
22051 // Add the elements to the containers
22053 new_shared_boundary_element_face_index[iproc].push_back(face_index);
22054
22055 } // for (e < ntmp_group1_shared_bound_ele_iproc)
22056
22057 } // if (my_rank < iproc)
22058 // Highest processor
22059 else if (my_rank > iproc)
22060 {
22061 // Get the haloed elements first and then the elements received
22062 // from processor "jproc" but that are haloed with "iproc"
22063 // processor
22064
22065 // Get the number of elements to copy from local elements
22066 // (invert the indexes to get the haloed elements)
22070 e++)
22071 {
22072 // Get the element
22075 // Get the face index
22076 const unsigned face_index =
22078 [e];
22079
22080 // Only include the element if it is nonhalo (this may be an
22081 // old halo element that helped to indentify a shared boundary
22082 // with iproc)
22083 if (!ele_pt->is_halo())
22084 {
22085 // Add the elements to the containers
22087 new_shared_boundary_element_face_index[iproc].push_back(face_index);
22088 } // if (!ele_pt->is_halo())
22089
22090 } // for (e < nlocal_haloed_shared_bound_ele_iproc_my_rank)
22091
22092 for (unsigned jproc = 0; jproc < nproc; jproc++)
22093 {
22094 // Can not receive elements from itself
22095 if (jproc != my_rank)
22096 {
22097 // Then the received shared elements from "jproc" but haloed
22098 // with "iproc"
22101 .size();
22102 for (unsigned e = 0; e < nrecvd_haloed_shared_bound_ele_jproc_iproc;
22103 e++)
22104 {
22105 // Get the element
22108 // Get the face index
22109 const unsigned face_index =
22111 [iproc]
22112 [e];
22113
22114 // Add the elements to the containers
22117 face_index);
22118
22119 } // for (e < nrecvd_haloed_shared_bound_ele_iproc)
22120
22121 } // if (jproc != my_rank)
22122
22123 } // for (jproc < nproc)
22124
22125 // ... and finally any additional shared boundary elements from
22126 // tmp_group2
22127 const unsigned ntmp_group2_shared_bound_ele_iproc =
22129 for (unsigned e = 0; e < ntmp_group2_shared_bound_ele_iproc; e++)
22130 {
22131 // Get the element
22134 // Get the face index
22135 const unsigned face_index =
22137
22138 // Add the elements to the containers
22140 new_shared_boundary_element_face_index[iproc].push_back(face_index);
22141
22142 } // for (e < ntmp_group2_shared_bound_ele_iproc)
22143
22144 } // else if (my_rank > iproc)
22145
22146 } // for (iproc < nproc)
22147
22148 // The time to sort shared boundaries
22149 if (Print_timings_level_load_balance > 1)
22150 {
22151 oomph_info << "CPU for sorting shared boundaries (load balance) [8]: "
22153 << std::endl;
22154 }
22155
22156 // =====================================================================
22157 // END: SORT THE SHARED BOUNDARIES SO THAT THEY ARE CREATED IN THE
22158 // SAME ORDER IN THE INVOLVED PROCESSORS (A PAIR OF PROCESSORS)
22159 // =====================================================================
22160
22161 // =====================================================================
22162 // BEGIN: CREATE THE NEW SHARED BOUNDARIES. BEFORE THE GENERATION OF
22163 // THE SHARED BOUNDARIES PUT IN A CONTAINER THOSE NONHALO ELEMENTS
22164 // THAT WILL REMAIN IN THE CURRENT PROCESSOR (BECAUSE THEIR RANK IS
22165 // THE SAME AS THE CURRENT PROCESSOR), AND THOSE ELEMENTS RECEIVED
22166 // FROM OTHER PROCESSORS. THESE SET OF ELEMENTS WILL BE USED TO
22167 // CHECK FOR POSSIBLE CONNECTIONS OF THE NEW SHARED BOUNDARIES WITH
22168 // THE ORIGINAL BOUNDARIES
22169 // =====================================================================
22170 // Finally, create the new shared boundaries
22171
22172 // Get the time to create the new shared boundaries
22174 if (Print_timings_level_load_balance > 1)
22175 {
22177 }
22178
22179 // Compute the elements that will remain after deletion in the
22180 // curent processor. This is required to check if the new shared
22181 // boundaries crete a connection with any node of the elements in
22182 // the boundaries
22183
22184 // Try to use as much information as possible
22185
22186 // Storage for the elements in the processor
22187 std::set<FiniteElement*> element_in_processor_pt;
22188
22189 // Loop over the old elements, those before sending/received
22190 // elements to/from other processors
22191 unsigned nh_count6 = 0;
22192 for (unsigned e = 0; e < nelement_before_load_balance; e++)
22193 {
22194 // Get the element
22196 // Only work with nonhalo elements
22197 if (!(ele_pt->is_halo()))
22198 {
22199 // Is the element part of the new domain
22201 {
22202 // Add the element to the set of elements in the processor
22204 }
22205
22206 } // if (!(ele_pt->is_halo()))
22207
22208 } // for (e < nelement_before_load_balance)
22209
22210 // Now include the received elements from the other processors
22211 // Loop over the processors
22212 for (unsigned iproc = 0; iproc < nproc; iproc++)
22213 {
22214 // No elements received from myself
22215 if (iproc != my_rank)
22216 {
22217 // Get the number of received elements with the "iproc"
22218 // processor
22219 const unsigned n_received_ele = received_elements_pt[iproc].size();
22220 for (unsigned ie = 0; ie < n_received_ele; ie++)
22221 {
22222 // Get the ie-th received element from processor iproc
22224
22225 // Include it in the set of elements in the processor
22227
22228 } // for (ie < nreceived_ele)
22229
22230 } // if (iproc != my_rank)
22231
22232 } // for (iproc < nproc)
22233
22234 // Now create the shared boundaries
22235 create_new_shared_boundaries(element_in_processor_pt,
22238
22239 // The time to create the new shared boundaries
22240 if (Print_timings_level_load_balance > 1)
22241 {
22243 << "CPU for creating new shared boundaries (load balance) [9]: "
22245 << std::endl;
22246 }
22247
22248 // =====================================================================
22249 // END: CREATE THE NEW SHARED BOUNDARIES. BEFORE THE GENERATION OF
22250 // THE SHARED BOUNDARIES PUT IN A CONTAINER THOSE NONHALO ELEMENTS
22251 // THAT WILL REMAIN IN THE CURRENT PROCESSOR (BECAUSE THEIR RANK IS
22252 // THE SAME AS THE CURRENT PROCESSOR), AND THOSE ELEMENTS RECEIVED
22253 // FROM OTHER PROCESSORS. THESE SET OF ELEMENTS WILL BE USED TO
22254 // CHECK FOR POSSIBLE CONNECTIONS OF THE NEW SHARED BOUNDARIES WITH
22255 // THE ORIGINAL BOUNDARIES
22256 // =====================================================================
22257
22258 // =====================================================================
22259 // BEGIN: DELETE THE ELEMENTS NO LONGER BELONGING TO THE DOMAIN,
22260 // INCLUDING HALO ELEMENTS. ADD THE KEPT ELEMENTS TO THE MESH AND
22261 // THE RECEIVED ELEMENTS FROM OTHER PROCESSORS
22262 // =====================================================================
22263
22264 // Get the time to delete elements no longer belonging to the
22265 // processor
22266 double tt_start_delete_elements = 0.0;
22267 if (Print_timings_level_load_balance > 1)
22268 {
22270 }
22271
22272 // Once computed the new shared boundaries delete the elements that
22273 // no longer belong to the processor (including the old halo
22274 // elements)
22275
22276 // The procedure is similar to the one performed at the distribution
22277 // stage (src/generic/mesh.cc -- distribute() method)
22278
22279 // Clean the storage for halo(ed) elements/nodes
22280 this->Halo_node_pt.clear();
22281 this->Root_halo_element_pt.clear();
22282
22283 this->Haloed_node_pt.clear();
22284 this->Root_haloed_element_pt.clear();
22285
22286 // Mark all the nodes as obsolete
22287 const unsigned nnodes = this->nnode();
22288 for (unsigned j = 0; j < nnodes; j++)
22289 {
22290 this->node_pt(j)->set_obsolete();
22291 }
22292
22293 // Flush the mesh storage
22294 this->flush_element_storage();
22295
22296 // Delete any storage of external elements and nodes
22297 this->delete_all_external_storage();
22298
22299 // Clear external storage
22300 this->External_halo_node_pt.clear();
22301 this->External_halo_element_pt.clear();
22302
22303 this->External_haloed_node_pt.clear();
22304 this->External_haloed_element_pt.clear();
22305
22306 // Keep track of the deleted elements
22308
22309 // Delete the elements that no longer belong to the processor
22310 unsigned nh_count7 = 0;
22311 for (unsigned e = 0; e < nelement_before_load_balance; e++)
22312 {
22314 // Only work with nonhalo elements
22315 if (!(ele_pt->is_halo()))
22316 {
22318 {
22319 // Add the element to the mesh
22320 this->add_element_pt(ele_pt);
22321 // Get the number of nodes on the element
22322 const unsigned nele_nodes = ele_pt->nnode();
22323 // Loop over the nodes of the element
22324 for (unsigned j = 0; j < nele_nodes; j++)
22325 {
22326 // Mark the node as non-obsolete
22328 } // for (j < nele_nodes)
22329
22330 } // The element belongs to the domain
22331 else
22332 {
22333 // Delete the element, but keep track of it
22334 deleted_elements.push_back(ele_pt);
22335 // Delete and point to null
22336 delete ele_pt;
22337 ele_pt = 0;
22338 }
22339
22340 } // if (!(ele_pt->is_halo()))
22341 else
22342 {
22343 // If the element is halo, delete if but keep track of it
22344 deleted_elements.push_back(ele_pt);
22345 // Delete and point to null
22346 delete ele_pt;
22347 ele_pt = 0;
22348 }
22349
22350 } // for (e < nelement_before_load_balance)
22351
22352 // Now add the received elements from each processor
22353 for (unsigned iproc = 0; iproc < nproc; iproc++)
22354 {
22355 if (iproc != my_rank)
22356 {
22357 // Get the number of received elements with the "iproc"
22358 // processor
22359 const unsigned nreceived_ele = received_elements_pt[iproc].size();
22360 for (unsigned ie = 0; ie < nreceived_ele; ie++)
22361 {
22362 // Get the element and add it to the mesh
22364 // Add the element to the mesh
22365 this->add_element_pt(ele_pt);
22366 // Get the number of nodes on the element
22367 const unsigned nele_nodes = ele_pt->nnode();
22368 // Loop over the nodes of the element
22369 for (unsigned j = 0; j < nele_nodes; j++)
22370 {
22371 // Mark the node as non-obsolete
22373 } // for (j < nele_nodes)
22374
22375 } // for (ie < nreceived_ele)
22376
22377 } // if (iproc != my_rank)
22378
22379 } // for (iproc < nproc)
22380
22381 // Now remove the obsolete nodes
22382 this->prune_dead_nodes();
22383
22384 // The time to delete elements no longer belonging to the processor
22385 if (Print_timings_level_load_balance > 1)
22386 {
22387 oomph_info << "CPU for deleting elements no longer belonging to this "
22388 "processor (load balance) [10]: "
22390 << std::endl;
22391 }
22392
22393 // =====================================================================
22394 // END: DELETE THE ELEMENTS NO LONGER BELONGING TO THE DOMAIN,
22395 // INCLUDING HALO ELEMENTS. ADD THE KEPT ELEMENTS TO THE MESH AND
22396 // THE RECEIVED ELEMENTS FROM OTHER PROCESSORS
22397 // =====================================================================
22398
22399 // =====================================================================
22400 // BEGIN: REESTABLISH THE HALO(ED) SCHEME, ATTACH HALO ELEMENTS
22401 // (HALO NODES INCLUDED) TO THE NEW MESH (AFTER LOAD BALANCING)
22402 // RESTORE THE BOUNDARY ELEMENTS SCHEME AND THE NUMBER OF SEGMENTS
22403 // ON EACH BOUNDARY
22404 // =====================================================================
22405
22406 // Get the time to re-establish the halo(ed) information
22408 if (Print_timings_level_load_balance > 1)
22409 {
22411 }
22412
22413 // Prepare the data to re-establish the halo(ed) scheme
22414
22415 // Sort the nodes on the new shared boundaries so that they have the
22416 // same order on all processors
22417 this->sort_nodes_on_shared_boundaries();
22418
22419 // Before re-establish the halo and haloed elements save the number
22420 // of current elements in the boundaries, this will be useful to
22421 // re-establish the boundary elements. Notice that there may be
22422 // boundary elements with null pointers, since the element may no
22423 // longer belong to the current processor
22424 const unsigned tmp_nboundary = this->nboundary();
22426
22427 // If there are regions, save the number of boundary-region elements
22429 // Are there regions?
22430 const unsigned n_regions = this->nregion();
22431
22432 // Loop over the boundaries
22433 for (unsigned ib = 0; ib < tmp_nboundary; ib++)
22434 {
22435 // Get the number of boundary elements
22436 ntmp_boundary_elements[ib] = this->nboundary_element(ib);
22437
22438 // Resize the container
22440
22441 // Loop over the regions
22442 for (unsigned rr = 0; rr < n_regions; rr++)
22443 {
22444 // Get the region id
22445 const unsigned region_id =
22446 static_cast<unsigned>(this->region_attribute(rr));
22447
22448 // Store the number of element in the region (notice we are
22449 // using the region index not the region id to refer to the
22450 // region)
22452 this->nboundary_element_in_region(ib, region_id);
22453
22454 } // for (rr < n_regions)
22455
22456 } // for (ib < tmp_nboundary)
22457
22458 // Re-establish the halo(ed) scheme
22459 this->reset_halo_haloed_scheme();
22460
22461 // Get the number of elements in the mesh after load balance
22462 const unsigned nelement_after_load_balance = this->nelement();
22463
22464 // We need to reset boundary elements because we need to get rid of
22465 // the old boundary elements and stay only with the new ones
22466 this->reset_boundary_element_info(ntmp_boundary_elements,
22469
22470 // There is no need to re-set boundary coordinates since the
22471 // load-balanced mesh already has the correct information (the
22472 // boundary coordinate for each node was sent with the node
22473 // information)
22474
22475 // We need to re-compute the number of segments on each boundary
22476 // after load balance. It may be possible that the boundary is now
22477 // split in more segments, or that previous gaps between the
22478 // segments have now dissapeared because the received elements
22479 // filled those gaps
22480
22481 // In order to re-set the number of segments it is required to get
22482 // the face elements, attach them to create a contiguous
22483 // representation of the boundary (in segments possibly) and then
22484 // counter the number of segments. This can only be done after
22485 // restoring the boundary elements scheme (which has been done
22486 // above)
22487
22488 // Set the number of segments for the boundaries with geom objects
22489 // associated. The correct value is not on the original mesh since
22490 // it is computed only when calling then
22491 // setup_boundary_coordinates() method (called only for those
22492 // boundaries with no geom object associated)
22493 for (unsigned b = 0; b < tmp_nboundary; b++)
22494 {
22495 if (this->boundary_geom_object_pt(b) != 0)
22496 {
22497 // Clear the boundary segment nodes storage
22498 this->flush_boundary_segment_node(b);
22499
22500 // Dummy vector of nodes on segments
22502
22503 // Compute the new number of segments in the boundary
22504 get_boundary_segment_nodes_helper(b, dummy_segment_node_pt);
22505
22506 // Get the number of segments from the vector of nodes
22507 const unsigned nsegments = dummy_segment_node_pt.size();
22508
22509 // Set the number of segments for the storing of the nodes
22510 // associated to the segments
22511 this->set_nboundary_segment_node(b, nsegments);
22512 } // if (this->boundary_geom_object_pt(b)!=0)
22513
22514 } // for (b < n_boundary)
22515
22516 // The time to re-establish the halo(ed) information
22517 if (Print_timings_level_load_balance > 1)
22518 {
22520 << "CPU for re-establishing halo(ed) information (load balance) [11]: "
22522 << std::endl;
22523 }
22524
22525 // =====================================================================
22526 // END: REESTABLISH THE HALO(ED) SCHEME, ATTACH HALO ELEMENTS (HALO
22527 // NODES INCLUDED) TO THE NEW MESH (AFTER LOAD BALANCING) RESTORE
22528 // THE BOUNDARY ELEMENTS SCHEME AND THE NUMBER OF SEGMENTS ON EACH
22529 // BOUNDARY
22530 // =====================================================================
22531
22532 if (Print_timings_level_load_balance > 1)
22533 {
22534 oomph_info << "CPU for load balance [n_ele_before="
22536 << ", n_ele_after=" << nelement_after_load_balance << "]: "
22538 << std::endl;
22539 }
22540
22541 oomph_info << "Load balance (unstructured mesh) [END]" << std::endl;
22542 }
22543
22544 //======================================================================
22545 /// Use the first and second group of elements to find the
22546 /// intersection between them to get the shared boundary
22547 /// elements from the first and second group
22548 //======================================================================
22549 template<class ELEMENT>
22558 {
22559 // 1) Compare their faces (nodes) and if they match then they are
22560 // part of a shared boundary
22561 // 2) Save the first and second group of elements that give rise to
22562 // the shared boundary, also include the face index
22563
22564 // Get the number of elements on the first group
22565 const unsigned nfirst_element = first_element_pt.size();
22566 // Loop over the elements in the first group
22567 for (unsigned ef = 0; ef < nfirst_element; ef++)
22568 {
22569 // Get the element
22571 // Check if the element is halo
22572 bool first_ele_is_halo = false;
22573 if (fele_pt->is_halo())
22574 {
22575 first_ele_is_halo = true;
22576 }
22577 // Get each of the faces
22578 for (unsigned ifface = 0; ifface < 3; ifface++)
22579 {
22581 if (ifface == 0)
22582 {
22583 first_face[0] = fele_pt->node_pt(1);
22584 first_face[1] = fele_pt->node_pt(2);
22585 }
22586 else if (ifface == 1)
22587 {
22588 first_face[0] = fele_pt->node_pt(2);
22589 first_face[1] = fele_pt->node_pt(0);
22590 }
22591 else if (ifface == 2)
22592 {
22593 first_face[0] = fele_pt->node_pt(0);
22594 first_face[1] = fele_pt->node_pt(1);
22595 }
22596
22597 // Now check each of the faces with the faces on the second
22598 // elements
22599
22600 // Get the number of elements on the second group
22601 const unsigned nsecond_element = second_element_pt.size();
22602 // Loop over the elements in the second group
22603 for (unsigned es = 0; es < nsecond_element; es++)
22604 {
22605 // Get the element
22607 // Check if the element is halo
22608 bool second_ele_is_halo = false;
22609 if (sele_pt->is_halo())
22610 {
22611 second_ele_is_halo = true;
22612 }
22613 // Now check whether both elements are halo, if that is the
22614 // case then we go for the next elements. We can not look for
22615 // shared boundaries between halo elements since other
22616 // processors, those with the nonhalo counterpart of the
22617 // elements, are in charge of creating those shared boundaries
22619 {
22620 // Get each of the faces
22621 for (unsigned isface = 0; isface < 3; isface++)
22622 {
22624 if (isface == 0)
22625 {
22626 second_face[0] = sele_pt->node_pt(1);
22627 second_face[1] = sele_pt->node_pt(2);
22628 }
22629 else if (isface == 1)
22630 {
22631 second_face[0] = sele_pt->node_pt(2);
22632 second_face[1] = sele_pt->node_pt(0);
22633 }
22634 else if (isface == 2)
22635 {
22636 second_face[0] = sele_pt->node_pt(0);
22637 second_face[1] = sele_pt->node_pt(1);
22638 }
22639
22640 // Now check for any intersection among first and second
22641 // faces
22642 if (first_face[0] == second_face[0] &&
22643 first_face[1] == second_face[1])
22644 {
22645 // Save the elements on the corresponding containers
22647 // .. and the face index
22649
22650 // Save the elements on the corresponding containers
22652 // .. and the face index
22654
22655 // Break the loop over the faces of the first elements
22656 // and the first elements, we need to continue looking
22657 // on the next face of the first elements
22658
22659 // Increase the indexes to force breaking the loop
22660 isface = 3;
22662 }
22663 // Check for intersection with the reversed case too
22664 else if (first_face[0] == second_face[1] &&
22665 first_face[1] == second_face[0])
22666 {
22667 // Save the elements on the corresponding containers
22669 // .. and the face index
22671
22672 // Save the elements on the corresponding containers
22674 // .. and the face index
22676
22677 // Break the loop over the faces of the first elements
22678 // and the first elements, we need to continue looking
22679 // on the next face of the first elements
22680
22681 // Increase the indexes to force breaking the loop
22682 isface = 3;
22684 }
22685
22686 } // for (isface < 3)
22687
22688 } // if (!(first_ele_is_halo && second_ele_is_halo))
22689
22690 } // for (es < nsecond_element)
22691
22692 } // for (ifface < 3)
22693
22694 } // for (ef < nfirst_element)
22695 }
22696
22697 //======================================================================
22698 /// Creates the new shared boundaries, this method is also in
22699 /// charge of computing the shared boundaries ids of each processor
22700 /// and send that info. to all the processors
22701 //======================================================================
22702 template<class ELEMENT>
22704 std::set<FiniteElement*>& element_in_processor_pt,
22707 {
22708 // Get the number of processors
22709 const unsigned nproc = this->communicator_pt()->nproc();
22710 // Get the rank of the current processor
22711 const unsigned my_rank = this->communicator_pt()->my_rank();
22712
22713 // ================================================================
22714 // BEGIN: GET THE SHARED BOUNDARY FACE ELEMENTS FROM THE SHARED
22715 // BOUNDARY ELEMENTS, AND ASSIGN A ROOT EDGE TO EACH FACE
22716 // ELEMENT. AVOID THE CREATION OF FACE ELEMENTS THAT REPRESENT THE
22717 // SAME EDGE (INTERNAL BOUNDARIES)
22718 // ================================================================
22719
22720 // Get the time to get edges from shared boundary face elements
22722 if (Print_timings_level_load_balance > 2)
22723 {
22725 }
22726
22727 // Face elements that create the shared boundaries (unsorted)
22729 // The elements from where the face element was created
22731 // The face index of the bulk element from where was created the
22732 // face element
22734
22735 // Store the current edges lying on boundaries (this will help for
22736 // any edge of a shared boundary lying on an internal boundary)
22737 std::map<std::pair<Node*, Node*>, unsigned> elements_edges_on_boundary;
22738
22739 // Compute the edges on the other boundaries
22740 this->get_element_edges_on_boundary(elements_edges_on_boundary);
22741
22742 // Mark those edges (pair of nodes overlapped by a shared boundary)
22743 std::map<std::pair<Node*, Node*>, bool> overlapped_edge;
22744
22745 // Associate every found edge (face element) on the shared boundary
22746 // with an original boundary only if the edge (face element) lies
22747 // (overlaps) on an original boundary, it may happen only for
22748 // internal boundaries
22750
22751 // Get the face elements from the shared boundary elements with in
22752 // each processor
22753 for (unsigned iproc = 0; iproc < nproc; iproc++)
22754 {
22755 // There are no shared boundary elements with myself
22756 if (iproc != my_rank)
22757 {
22758 // Get the number of shared boundary elements with in "iproc"
22759 // processor
22760 const unsigned n_shared_bound_ele =
22762
22763 // Avoid to create repeated face elements, compare the nodes on
22764 // the edges of the face elements
22766
22767 // Count the number of repeated faces
22768 unsigned nrepeated_faces = 0;
22769
22770 // Loop over the shared boundary elements with the iproc
22771 // processor
22772 for (unsigned iele = 0; iele < n_shared_bound_ele; iele++)
22773 {
22774 // Get the bulk element
22777
22778 // Get the face index
22779 int face_index = static_cast<int>(
22781
22782 // Create the face element
22784 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
22785
22786 // Before adding the face element to the vector check that is
22787 // not has been previously created
22788 bool done_face = false;
22789
22790 // Get the number of nodes on the face element and get the first
22791 // and last node
22792 const unsigned nnode_face_ele = tmp_ele_pt->nnode();
22795
22796 // Get the number of already done face elements
22797 const unsigned ndone_faces = done_faces.size();
22798 // Loop over the already visited face elements
22799 for (unsigned n = 0; n < ndone_faces; n++)
22800 {
22805 {
22806 done_face = true;
22808 break;
22809 }
22810 // Check for the reversed case
22813 {
22814 done_face = true;
22816 break;
22817 }
22818
22819 } // for (n < ndone_faces)
22820
22821 // Only include the faces that are not repeated
22822 if (!done_face)
22823 {
22824 // Add the face element in the vector
22826 // Add the bulk element to the vector
22828 // Add the face index to the vector
22829 tmp_unsorted_face_index_ele[iproc].push_back(face_index);
22830 // Include the nodes in the done nodes vector
22831 std::pair<Node*, Node*> tmp_edge =
22832 std::make_pair(first_face_node_pt, last_face_node_pt);
22833 // Push the edge
22834 done_faces.push_back(tmp_edge);
22835
22836 // Associate the face element with a boundary (if that is
22837 // the case)
22838 int edge_boundary_id = -1;
22839 std::map<std::pair<Node*, Node*>, unsigned>::iterator it;
22841 // If the edges lie on a boundary then get the boundary id
22842 // on which the edges lie
22843 if (it != elements_edges_on_boundary.end())
22844 {
22845 // Assign the internal boundary id associated with the
22846 // edge
22847 edge_boundary_id = (*it).second;
22848 // Mark the edge as overlapped
22849 overlapped_edge[tmp_edge] = true;
22850 // Also include the reversed version of the edge
22851 std::pair<Node*, Node*> rev_tmp_edge =
22852 std::make_pair(last_face_node_pt, first_face_node_pt);
22853 // Mark the reversed version of the edge as overlapped
22855 }
22856 else
22857 {
22858 // Look for the reversed version
22859 std::pair<Node*, Node*> rtmp_edge =
22860 std::make_pair(last_face_node_pt, first_face_node_pt);
22862 if (it != elements_edges_on_boundary.end())
22863 {
22864 // Assign the internal boundary id associated with the
22865 // edge
22866 edge_boundary_id = (*it).second;
22867 // Mark the edge as overlapped
22868 overlapped_edge[rtmp_edge] = true;
22869 // Mark the reversed version (normal) of the edge as
22870 // overlapped
22871 overlapped_edge[tmp_edge] = true;
22872 }
22873 }
22874 // Associate the edge with a boundary
22876 } // if (!done_face)
22877 else
22878 {
22879 // Delete the repeated face elements
22880 delete tmp_ele_pt;
22881 tmp_ele_pt = 0;
22882 }
22883
22884 } // for (iele < n_shared_bound_ele)
22885
22886 } // if (iproc != my_rank)
22887
22888 } // for (iproc < nproc)
22889
22890 // The time to get edges from shared boundary face elements
22891 if (Print_timings_level_load_balance > 2)
22892 {
22893 oomph_info << "CPU for getting edges from shared boundary face elements "
22894 "(load balance) [9.1]: "
22897 << std::endl;
22898 }
22899
22900 // ================================================================
22901 // END: GET THE SHARED BOUNDARY FACE ELEMENTS FROM THE SHARED
22902 // BOUNDARY ELEMENTS, AND ASSIGN A ROOT EDGE TO EACH FACE
22903 // ELEMENT. AVOID THE CREATION OF FACE ELEMENTS THAT REPRESENT THE
22904 // SAME EDGE (INTERNAL BOUNDARIES)
22905 // ================================================================
22906
22907 // ================================================================
22908 // BEGIN: BEFORE SORTING THE SHARED FACE ELEMENTS AND ITS ASSOCIATED
22909 // DATA, WE NEED TO ENSURE THAT THEY APPEAR (OR ARE STORED) IN THE
22910 // SAME ORDER IN BOTH OF THE PROCESSORS THAT CREATED THEM. WE USE
22911 // THE BOTTOM-LEFT NODE OF EACH FACE ELEMENT TO STORE THEM IN THE
22912 // SAME ORDER IN BOTH PROCESSORS. ALSO ENSURE THAT THE FACE ELEMENTS
22913 // AGREE WITH THE FIRST AND LAST NODE IN ALL PROCESSORS
22914 // ================================================================
22915
22916 // Get the time to sort shared face elements
22918 if (Print_timings_level_load_balance > 2)
22919 {
22921 }
22922
22923 // -----------------------------------------------------------------
22924 // Before continuing we need to ensured that the face elements are
22925 // stored in the same order in all processors. Sort them starting
22926 // from the face element with the bottom-left node coordinate
22927
22928 // Face elements that create the shared boundaries (unsorted)
22930 // The elements from where the face element was created
22932 // The face index of the bulk element from where was created the
22933 // face element
22935 // Associate every found edge on the shared boundary with an
22936 // original boundary only if the edge lies on an original boundary,
22937 // it may happen only for internal boundaries
22938 Vector<Vector<int>> edge_boundary(nproc);
22939
22940 // For each face element, mark if the element should be considered
22941 // in its inverted way to fullfill with the bottom-left node to be
22942 // the first (left) node. First get the status of each element and
22943 // when they get sorted copy the values across
22944 std::vector<std::vector<bool>> tmp_treat_as_inverted(nproc);
22945 // Vector to store the status of the sorted face elements based on
22946 // the bottom-left condition
22947 std::vector<std::vector<bool>> treat_as_inverted(nproc);
22948
22949 // Get the bottom-left node of each face element and sort them
22950 // starting from the face element with the bottom-left node
22951
22952 // Loop over the processors
22953 for (unsigned iproc = 0; iproc < nproc; iproc++)
22954 {
22955 // There are no shared face elements with myself
22956 if (iproc != my_rank)
22957 {
22958 // Get the number of unsorted face elements
22959 const unsigned n_face_ele = tmp_unsorted_face_ele_pt[iproc].size();
22960 // Store the centroid of the face element. Perform the sorting
22961 // based on the bottom-left centroid of each face element
22963
22964 // Resize the storage for the treating as inverted face element
22965 // storage
22967
22968 // Loop over the face elements associated with the iproc
22969 // processor
22970 for (unsigned e = 0; e < n_face_ele; e++)
22971 {
22972 // Get the face element
22974 // Get the number of nodes of the face element
22975 const unsigned n_node = face_ele_pt->nnode();
22977 // Assign as the bottom-left node the first node
22978 // Get the node
22980 bottom_left[0] = node_pt->x(0);
22981 bottom_left[1] = node_pt->x(1);
22982 // Set as not treat as inverted element
22983 tmp_treat_as_inverted[iproc][e] = false;
22984 // Loop over the nodes to get the bottom-left vertex of all
22985 // the nodes
22986 for (unsigned n = 1; n < n_node; n++)
22987 {
22988 // Get the node
22990 if (node_pt->x(1) < bottom_left[1])
22991 {
22992 bottom_left[0] = node_pt->x(0);
22993 bottom_left[1] = node_pt->x(1);
22994 // The first node is no longer the bottom-left node, we
22995 // need to treat the element as inverted
22996 tmp_treat_as_inverted[iproc][e] = true;
22997 } // if (node_pt->x(1) < bottom_left[1])
22998 else if (node_pt->x(1) == bottom_left[1])
22999 {
23000 if (node_pt->x(0) < bottom_left[0])
23001 {
23002 bottom_left[0] = node_pt->x(0);
23003 bottom_left[1] = node_pt->x(1);
23004 // The first node is no longer the bottom-left node, we
23005 // need to treat the element as inverted
23006 tmp_treat_as_inverted[iproc][e] = true;
23007 } // if (node_pt->x(0) < bottom_left[0])
23008 } // else if (node_pt->x(1) == bottom_left[1])
23009
23010 } // for (n < n_node
23011
23012 // Resize the container
23013 centroid_vertices[e].resize(2);
23014 // Add the centroid of the face element
23015 centroid_vertices[e][0] = (face_ele_pt->node_pt(0)->x(0) +
23016 face_ele_pt->node_pt(n_node - 1)->x(0)) *
23017 0.5;
23018 centroid_vertices[e][1] = (face_ele_pt->node_pt(0)->x(1) +
23019 face_ele_pt->node_pt(n_node - 1)->x(1)) *
23020 0.5;
23021
23022 } // for (e < n_face_ele)
23023
23024 // Sort the face elements based on their bottom-left node
23025 unsigned n_sorted_bottom_left = 0;
23026 // Keep track of the already sorted face elements
23027 std::vector<bool> done_face(n_face_ele, false);
23028
23029 // Loop until all face elements have been sorted
23031 {
23032 // The index of the next bottom-left face element
23033 unsigned index = 0;
23035 for (unsigned e = 0; e < n_face_ele; e++)
23036 {
23037 // Get the first not done face element
23038 if (!done_face[e])
23039 {
23040 // Store the first not done
23043 // Set the index
23044 index = e;
23045 // Break
23046 break;
23047 } // if (!done_face[e])
23048
23049 } // for (e < n_face_ele)
23050
23051 // Loop over all the other nondone face elements
23052 for (unsigned e = index + 1; e < n_face_ele; e++)
23053 {
23054 // Get the first not done face element
23055 if (!done_face[e])
23056 {
23058 {
23059 // Re-set the current bottom left vertex
23062 // Re-assign the index
23063 index = e;
23064 } // if (centroid_vertices[e][1] < current_bottom_left[1])
23065 else if (centroid_vertices[e][1] == current_bottom_left[1])
23066 {
23068 {
23069 // Re-set the current bottom left vertex
23072 // Re-assign the index
23073 index = e;
23074 } // if (centroid_vertices[e][0] < current_bottom_left[0])
23075
23076 } // else if (centroid_vertices[e][1] == current_bottom_left[1])
23077
23078 } // if (!done_face[e])
23079
23080 } // for (e < n_face_ele)
23081
23082 // The face element
23083 unsorted_face_ele_pt[iproc].push_back(
23085 // The boundary element
23086 unsorted_ele_pt[iproc].push_back(tmp_unsorted_ele_pt[iproc][index]);
23087 // The face index
23088 unsorted_face_index_ele[iproc].push_back(
23090 // The edge boundary associated to the face element
23091 edge_boundary[iproc].push_back(tmp_edge_boundary[iproc][index]);
23092 // The treat as inverted condition
23093 treat_as_inverted[iproc].push_back(
23095
23096 // Mark the face element as sorted (done or visited)
23097 done_face[index] = true;
23098
23099 // Increase the number of sorted bottom-left face elements
23101
23102 } // while (n_sorted_bottom_left < n_face_ele)
23103
23104#ifdef PARANOID
23105 // Get the number of face elements sorted with the bottom-left
23106 // condition
23107 const unsigned tmp_n_face_ele = unsorted_face_ele_pt[iproc].size();
23108
23110 {
23111 std::ostringstream error_stream;
23113 << "The number of face elements before sorting them starting\n"
23114 << "from their bottom-left vertex is different from the number\n"
23115 << "of face elements after the sorting\n"
23116 << "N. ele before sorting: (" << n_face_ele << ")\n"
23117 << "N. ele after sorting: (" << tmp_n_face_ele << ")\n";
23118 throw OomphLibError(
23119 error_stream.str(),
23120 "RefineableTriangleMesh::create_new_shared_boundaries()",
23122 }
23123#endif
23124
23125 } // if (iproc != my_rank)
23126
23127 } // for (iproc < nproc)
23128
23129 // The time to sort shared face elements
23130 if (Print_timings_level_load_balance > 2)
23131 {
23132 oomph_info << "CPU for sorting shared boundary face elements (load "
23133 "balance) [9.2]: "
23135 << std::endl;
23136 }
23137
23138 // ================================================================
23139 // END: SORTING THE SHARED FACE ELEMENTS AND ITS ASSOCIATED DATA, WE
23140 // NEED TO ENSURE THAT THEY APPEAR (OR ARE STORED) IN THE SAME ORDER
23141 // IN BOTH OF THE PROCESSORS THAT CREATED THEM. WE USE THE
23142 // BOTTOM-LEFT NODE OF EACH FACE ELEMENT TO STORE THEM IN THE SAME
23143 // ORDER IN BOTH PROCESSORS. ALSO ENSURE THAT THE FACE ELEMENTS
23144 // AGREE WITH THE FIRST AND LAST NODE IN ALL PROCESSORS
23145 // ================================================================
23146
23147 // ================================================================
23148 // BEGIN: COMPUTE THE GLOBAL DEGREE (VALENCY OF EACH NODE). THE
23149 // DEGREE OF THE NODES IN THE CURRENT SHARED BOUNDARIES IS COMPUTED
23150 // FIRST, THEN THIS INFO. IS SENT TO A ROOT PROCESSOR WHICH IS IN
23151 // CHARGE OF IDENTIFY AND RE-ASSIGN THE DEGREE OF THE NODES (IF THAT
23152 // IS THE CASE)
23153 // ================================================================
23154
23155 // Get the time to compute the valency of each node
23157 if (Print_timings_level_load_balance > 2)
23158 {
23160 }
23161
23162 // Stores the global-degree of each node
23163 std::map<Node*, unsigned> global_node_degree;
23164
23165 // Get the global degree (valency) of each node
23166 compute_shared_node_degree_helper(unsorted_face_ele_pt, global_node_degree);
23167
23168 // The time to compute the valency of each node
23169 if (Print_timings_level_load_balance > 2)
23170 {
23172 << "CPU for computing the valency of nodes (load balance) [9.3]: "
23174 << std::endl;
23175 }
23176
23177 // ================================================================
23178 // END: COMPUTE THE GLOBAL DEGREE (VALENCY OF EACH NODE). THE
23179 // DEGREE OF THE NODES IN THE CURRENT SHARED BOUNDARIES IS COMPUTED
23180 // FIRST, THEN THIS INFO. IS SENT TO A ROOT PROCESSOR WHICH IS IN
23181 // CHARGE OF IDENTIFY AND RE-ASSIGN THE DEGREE OF THE NODES (IF THAT
23182 // IS THE CASE)
23183 // ================================================================
23184
23185 // ================================================================
23186 // BEGIN: IDENTIFY THE NODES LYING ON EDGES NOT OVERLAPED BY SHARED
23187 // BOUNDARIES, IDENTIFY THE BOUNDARY TO WHICH THE EDGE CORRESPOND
23188 // ================================================================
23189
23190 // Get the time to compute nodes on non overlapped shared boundaries
23192 if (Print_timings_level_load_balance > 2)
23193 {
23195 }
23196
23197 // Mark the nodes on original boundaries not overlapped by shared
23198 // boundaries
23199 std::map<unsigned, std::map<Node*, bool>>
23201
23202 // Loop over the edges of the original boundaries
23203 for (std::map<std::pair<Node*, Node*>, unsigned>::iterator it_map =
23206 it_map++)
23207 {
23208 // Get the edge
23209 std::pair<Node*, Node*> edge_pair = (*it_map).first;
23210 // Is the edge overlaped by a shared boundary
23212 {
23213 // Mark the nodes of the edge as being on an edge not overlaped
23214 // by a shared boundary on the boundary the edge is
23215 unsigned b = (*it_map).second;
23216
23217 // Get the left node
23218 Node* left_node_pt = edge_pair.first;
23220
23221 // Get the right node
23222 Node* right_node_pt = edge_pair.second;
23224
23225 } // if (!overlapped_edge[edge_pair])
23226
23227 } // Loop over edges to mark those nodes on overlaped edge by
23228 // shared boundaries
23229
23230 // The time to compute nodes on non overlapped shared boundaries
23231 if (Print_timings_level_load_balance > 2)
23232 {
23233 oomph_info << "CPU for computing nodes on non overlapped shared "
23234 "boundaries (load balance) [9.4]: "
23237 << std::endl;
23238 }
23239
23240 // ================================================================
23241 // END: IDENTIFY THE NODES LYING ON EDGES NOT OVERLAPED BY SHARED
23242 // BOUNDARIES, IDENTIFY THE BOUNDARY TO WHICH THE EDGE CORRESPOND
23243 // ================================================================
23244
23245 // ==================================================================
23246 // BEGIN: SORT THE SHARED BOUNDARY FACE ELEMENTS, ADD FACE ELEMENTS
23247 // TO THE LEFT OR RIGHT OF THE ROOT FACE ELEMENT. STOP ADDING WHEN
23248 // THE MOST LEFT OR MOST RIGHT ELEMENT (NODE) IS ALREADY PART OF
23249 // ANOTHER BOUNDARY (THIS MEANS THAT THE SHARED BOUNDARY THAT IS
23250 // BEING CREATED HAS A CONNECTION). ALSO REMEMBER TO CHECK FOR THE
23251 // CASE WHEN THE MOST LEFT OR MOST RIGHT NODE IS A BOUNDARY NODE OF
23252 // A BOUNDARY THAT NO LONGER EXIST IN THE DOMAIN. AT THE END OF THIS
23253 // SECTION WE WILL HAVE THE NUMBER OF SHARED BOUNDARIES OF THIS
23254 // PROCESSOR WITH OTHERS BUT NOT THE GLOBAL SHARED BOUNDARY ID
23255 // ==================================================================
23256
23257 // Get the time to sort shared boundaries face elements to create a
23258 // continuous representation of the boundary
23259 double tt_start_join_shd_bnd_face_ele = 0.0;
23260 if (Print_timings_level_load_balance > 2)
23261 {
23263 }
23264
23265 // Face elements that create the shared boundaries (sorted)
23267
23268 // Bulk elements that create the shared boundaries (sorted)
23270
23271 // Face indexes of the bulk elements that create the shared
23272 // boundaries (sorted)
23274
23275 // Store the edge boundary id associated with a shared boundary (if
23276 // any, this apply for shared boundaries lying on internal
23277 // boundaries, then the shared boundary is marked as overlaping an
23278 // internal boundary)
23280
23281 // Store the connection information obtained when joining the face
23282 // elements (used for connection purposes only)
23284
23285 // Store the local shared boundary id associated to the elements
23286 // that will give rise to the shared boundaries (used to compute the
23287 // global shared boundary id from the local shared boundary id)
23289
23290 // Map that associates the local shared boundary id with the list of
23291 // nodes that create it
23292 std::map<unsigned, std::list<Node*>>
23294
23295 // Local shared bouonday id (used to locally identify the lists of
23296 // nodes that create shared boundaries, it is also useful to
23297 // identify connections with shared boundaries)
23298 unsigned local_shd_bnd_id = this->Initial_shared_boundary_id;
23299
23300 // Sort the face elements, using the nodes at its ends
23301
23302 // Mark the done elements
23303 std::map<FiniteElement*, bool> done_ele;
23304
23305 // Mark the inverted elements
23306 std::map<FiniteElement*, bool> is_inverted;
23307
23308 // Sort the face elements to get the number of shared boundaries
23309 // with in each processor
23310 for (unsigned iproc = 0; iproc < nproc; iproc++)
23311 {
23312 // No face elements with myself
23313 if (iproc != my_rank)
23314 {
23315 // Get the number of unsorted face elements with the iproc
23316 // processor
23318 // Count the number of sorted face elements
23319 unsigned nsorted_face_ele = 0;
23320
23321 // Iterate until all the face elements have been sorted
23323 {
23324 // Take the first nonsorted element an use it as root element,
23325 // add elements to the left and right until no more elements
23326 // left or until a stop condition is reached (connection,
23327 // boundary node)
23328
23329#ifdef PARANOID
23330 // Flag to indicate if a root element was found
23331 bool found_root_element = false;
23332#endif
23333
23334 // Index of the found root element
23335 unsigned root_index = 0;
23336
23337 // List that contains the sorted face elements
23338 std::list<FiniteElement*> tmp_sorted_face_ele_pt;
23339
23340 // List that contains the sorted elements
23341 std::list<FiniteElement*> tmp_sorted_ele_pt;
23342
23343 // List that contains the sorted face indexes of the bulk
23344 // elements
23345 std::list<int> tmp_sorted_face_index_ele;
23346
23347 // Storing for the sorting nodes extracted from the face
23348 // elements. The sorted nodes are used to identify connections
23349 // among new shared boundaries or original boundaries
23350 std::list<Node*> tmp_sorted_nodes_pt;
23351 // Clear the storage (just in case)
23352 tmp_sorted_nodes_pt.clear();
23353
23354 // The initial and final nodes
23355 Node* initial_node_pt = 0;
23356 Node* final_node_pt = 0;
23357
23358 // Store the original boundary id related with the root face
23359 // element (if there is one)
23360 int root_edge_bound_id = -1;
23361
23362 // Loop over the unsorted face elements until a root element
23363 // is found
23364 for (unsigned e = 0; e < nunsorted_face_ele; e++)
23365 {
23366 // Get a root element
23368 // Is the element already done?
23369 if (!done_ele[root_ele_pt])
23370 {
23371 // Get the edge boundary id associated with the edge (if
23372 // there is one)
23373 root_edge_bound_id = edge_boundary[iproc][e];
23374 // Add the face element to the list of sorted face
23375 // elements
23377 // Add the bulk element to the list of sorted elements
23379 // Add the face index to the list of sorted face index
23380 // elements
23381 tmp_sorted_face_index_ele.push_back(
23383
23384 // Get the nodes and state them as initial and final
23385 const unsigned nnodes = root_ele_pt->nnode();
23386 // Check if the face element should be treated as inverted
23387 if (!treat_as_inverted[iproc][e])
23388 {
23391 }
23392 else
23393 {
23396 }
23397 // Add both nodes to the list of sorted nodes
23400
23401 // Mark the element as done
23402 done_ele[root_ele_pt] = true;
23403 // Check if the face element should be treated as inverted
23404 if (!treat_as_inverted[iproc][e])
23405 {
23406 // Mark the element as not inverted
23407 is_inverted[root_ele_pt] = false;
23408 }
23409 else
23410 {
23411 // Mark the element as inverted
23412 is_inverted[root_ele_pt] = true;
23413 }
23414 // Increase the counter for sorted face elements
23416 // Set the root index
23417 root_index = e;
23418#ifdef PARANOID
23419 // Set the flag of found root element
23420 found_root_element = true;
23421#endif
23422 // Break the loop
23423 break;
23424
23425 } // if (!done_ele[root_ele_pt])
23426
23427 } // for (e < nunsorted_face_ele)
23428
23429#ifdef PARANOID
23430 if (!found_root_element)
23431 {
23432 std::ostringstream error_stream;
23434 << "It was not possible the found the root element\n\n";
23435 throw OomphLibError(error_stream.str(),
23438 }
23439#endif
23440
23441 // New element added. Continue adding elements -- or nodes --
23442 // to the list of shared boundary elements while a new element
23443 // has been added to the list (we have just added the root
23444 // element)
23445 bool new_element_added = true;
23446
23447 // Similarly that in the
23448 // "create_polylines_from_halo_elements_helper() method, we
23449 // extract the nodes (in order) that will create the shared
23450 // polyline, and also check for connections with the just
23451 // added face elements (nodes)
23452
23453 // Flags to indicate at which end (of the sorted list of
23454 // boundary elements) the element was added (left or right)
23455 bool element_added_to_the_left = false;
23456 bool element_added_to_the_right = false;
23457
23458 // Flag to indicate that the "left" node of the element added
23459 // to the left was found to be shared with another boundary
23460 bool connection_to_the_left = false;
23461
23462 // Flag to indicate that the "right" node of the element added
23463 // to the right was found to be shared with another boundary
23464 bool connection_to_the_right = false;
23465
23466 // Flag to stop the adding of elements (and nodes) to the
23467 // current shared boundary (because there are connections at
23468 // both ends)
23470
23471 // Store the boundary ids of the polylines to connect (only
23472 // used when the polyline was found to have a connection)
23473 // -1: Indicates no connection
23474 // -2: Indicates connection with itself
23475 // -3: Indicates no connection BUT STOP adding elements
23476 // -because the node is a boundary node whose boundary is no
23477 // -currently part of the domain. Think in one of the corner
23478 // -nodes of a triangle touchin a boundary that does no longer
23479 // -exist
23480 // Any other value: Boundary id to connect
23483
23484 // Get the global degree of the node (notice the local degree
23485 // has been updated to global degree)
23486 const unsigned initial_node_degree =
23488
23489 // Flag to indicate we are calling the method from a load
23490 // balance sub-rutine
23491 const bool called_for_load_balance = true;
23492
23493 // Check if the nodes of the root element have connections
23494 // ... to the left
23496 this->check_connections_of_polyline_nodes(
23506
23507 // If there is a stop condition then set the corresponding
23508 // flag
23510 {
23512 } // if (bound_id_connection_to_the_left != -1)
23513
23514 // Get the global degree of the node (notice the local degree
23515 // has been updated to global degree)
23517
23518 // ... and to the right
23520 this->check_connections_of_polyline_nodes(
23530
23531 // If there is a stop condition then set the corresponding
23532 // flag
23534 {
23536 } // if (bound_id_connection_to_the_right != -1)
23537
23538 // If the current shared boundary has connections at both ends
23539 // then stop the adding of elements (and nodes)
23541 {
23543 }
23544
23545 // Continue searching for more elements to add if
23546 // 1) A new element was added at the left or right of the list
23547 // 2) There are more possible elements to add
23548 // 3) The nodes at the edges of the added element (left or
23549 // right) are not part of any other previous shared
23550 // boundary
23553 {
23554 // Loop over the remaining elements and try to create a
23555 // contiguous set of face elements, start looking from the
23556 // root index. Any previous element should have been already
23557 // visited
23558 for (unsigned e = root_index; e < nunsorted_face_ele; e++)
23559 {
23560 // Reset the flags for added elements, to the left and right
23561 new_element_added = false;
23564
23565 // Get the "e"-th element on the vector
23567 // Get the boundary id associated with the edge (if any)
23568 const int edge_bound_id = edge_boundary[iproc][e];
23569 // Check if the element has been already sorted and the
23570 // related edge bound id is the same as the root edge (if
23571 // any)
23572 if (!done_ele[tmp_ele_pt] &&
23574 {
23575 // Get the number of nodes on the current element
23576 const unsigned nnodes = tmp_ele_pt->nnode();
23577 // Get the first and last node of the element
23578 // Check if the face element should be treated as inverted
23579 Node* first_node_pt = 0;
23580 Node* last_node_pt = 0;
23581 if (!treat_as_inverted[iproc][e])
23582 {
23585 }
23586 else
23587 {
23590 }
23591
23592 // A pointer to the node at the left or right of the
23593 // just added element, the most left or the most right
23594 // node
23596
23597 // Check if the element goes to the left
23599 {
23600 // Update the initial node and the just added node
23602 // Add the most left node
23604 // Add the face element to the list of sorted face
23605 // elements
23607 // Add the bulk element to the list of sorted elements
23609 // Add the face index to the list of sorted face index
23610 // elements
23611 tmp_sorted_face_index_ele.push_front(
23613 if (!treat_as_inverted[iproc][e])
23614 {
23615 // Mark the element as not inverted
23616 is_inverted[tmp_ele_pt] = false;
23617 }
23618 else
23619 {
23620 // Mark the element as inverted
23621 is_inverted[tmp_ele_pt] = true;
23622 }
23623 // Set the flag to indicate a new element was added
23624 new_element_added = true;
23625 // Set the flag to indicate the element was added to
23626 // the left
23628 }
23629 // Check if the element goes to the left (but inverted)
23630 else if (initial_node_pt == first_node_pt &&
23632 {
23633 // Update the initial node and the just added node
23635 // Add the most left node
23637 // Add the face element to the list of sorted face
23638 // elements
23640 // Add the bulk element to the list of sorted elements
23642 // Add the face index to the list of sorted face index
23643 // elements
23644 tmp_sorted_face_index_ele.push_front(
23646 if (!treat_as_inverted[iproc][e])
23647 {
23648 // Mark the element as inverted
23649 is_inverted[tmp_ele_pt] = true;
23650 }
23651 else
23652 {
23653 // Mark the element as not inverted
23654 is_inverted[tmp_ele_pt] = false;
23655 }
23656 // Set the flag to indicate a new element was added
23657 new_element_added = true;
23658 // Set the flag to indicate the element was added to
23659 // the left
23661 }
23662 // Check if the elements goes to the right
23663 else if (final_node_pt == first_node_pt &&
23665 {
23666 // Update the final node and the just added node
23668 // Add the most right node
23670 // Add the face element to the list of sorted face
23671 // elements
23673 // Add the bulk element to the list of sorted elements
23675 // Add the face index to the list of sorted face index
23676 // elements
23677 tmp_sorted_face_index_ele.push_back(
23679 if (!treat_as_inverted[iproc][e])
23680 {
23681 // Mark the element as not inverted
23682 is_inverted[tmp_ele_pt] = false;
23683 }
23684 else
23685 {
23686 // Mark the element as inverted
23687 is_inverted[tmp_ele_pt] = true;
23688 }
23689 // Set the flag to indicate a new element was added
23690 new_element_added = true;
23691 // Set the flag to indicate the element was added to
23692 // the right
23694 }
23695 // Check if the elements goes to the right (but inverted)
23696 else if (final_node_pt == last_node_pt &&
23698 {
23699 // Update the final node and the just added node
23701 // Add the most right node
23703 // Add the face element to the list of sorted face
23704 // elements
23706 // Add the bulk element to the list of sorted elements
23708 // Add the face index to the list of sorted face index
23709 // elements
23710 tmp_sorted_face_index_ele.push_back(
23712 if (!treat_as_inverted[iproc][e])
23713 {
23714 // Mark the element as inverted
23715 is_inverted[tmp_ele_pt] = true;
23716 }
23717 else
23718 {
23719 // Mark the element as not inverted
23720 is_inverted[tmp_ele_pt] = false;
23721 }
23722 // Set the flag to indicate a new elements was added
23723 new_element_added = true;
23724 // Set the flag to indicate the element was added to
23725 // the right
23727 }
23728
23729 // Do additional stuff if the element was added
23731 {
23732 // Mark the element as done
23733 done_ele[tmp_ele_pt] = true;
23734 // Increase the counter for sorted face elements
23736
23737 // Get the global degree of the node (notice the
23738 // local degree has been updated to global degree)
23739 const unsigned new_added_node_degree =
23741
23742 // Based on which side the element was added, look for
23743 // connections on that side
23744
23745 // Verify for connections to the left (we need to
23746 // check for the connection variable too, since
23747 // after a connection has been done we no longer
23748 // need to verify for this condition)
23750 {
23751 // Check for connection
23753 this->check_connections_of_polyline_nodes(
23763
23764 // If there is a stop condition then set the
23765 // corresponding flag
23767 {
23769 } // if (bound_id_connection_to_the_left != -1)
23770
23771 } // if (node_added_to_the_left &&
23772 // !connection_to_the_left)
23773
23774 // Verify for connections to the right (we need to
23775 // check for the connection variable too, since
23776 // after a connection has been done we no longer
23777 // need to verify for this condition)
23779 {
23780 // Check for connection
23782 this->check_connections_of_polyline_nodes(
23792
23793 // If there is a stop condition then set the
23794 // corresponding flag
23796 {
23798 } // if (bound_id_connection_to_the_right != -1)
23799
23800 } // if (node_added_to_the_right &&
23801 // !connection_to_the_right)
23802
23803 // If the current shared boundary has connections at
23804 // both ends then stop the adding of elements (and
23805 // nodes)
23807 {
23809 }
23810
23811 // Break the for (looping over unsorted face
23812 // elements) and re-start looking for more elements
23813 // that fit to the left or right
23814 break;
23815
23816 } // if (new_element_added)
23817
23818 } // if (!done_ele[tmp_ele_pt])
23819
23820 } // for (e < nunsorted_face_ele)
23821
23822 } // while(new_element_added &&
23823 // (nsorted_face_ele < nunsorted_face_ele)
23824 // && !current_polyline_has_connections_at_both_ends)
23825
23826 // ------------------------------------------------------------
23827 // Before assigning a local shared boundary id to the list of
23828 // nodes and boundary elements, check for any loop that the
23829 // shared boundary may be creating
23830
23831 // The vector of the elements
23833 // Store the list of elements on a vector of elements
23834 for (std::list<FiniteElement*>::iterator it =
23835 tmp_sorted_ele_pt.begin();
23836 it != tmp_sorted_ele_pt.end();
23837 it++)
23838 {
23839 tmp_vector_sorted_ele_pt.push_back((*it));
23840 }
23841
23842 // The vector of the face elements
23844 // Store the list of face elements on a vector of face
23845 // elements
23846 for (std::list<FiniteElement*>::iterator it =
23847 tmp_sorted_face_ele_pt.begin();
23848 it != tmp_sorted_face_ele_pt.end();
23849 it++)
23850 {
23851 tmp_vector_sorted_face_ele_pt.push_back((*it));
23852 }
23853
23854 // The vector of the face indexes
23856 // Store the list of elements on a vector of elements
23857 for (std::list<int>::iterator it = tmp_sorted_face_index_ele.begin();
23859 it++)
23860 {
23862 }
23863
23864 // Store the nodes for the new shared polylines without loops
23866 // Store the boundary elements of the shared polyline without
23867 // loops
23869 // Store the boundary face elements of the shared polyline
23870 // without loops
23872 // Face indexes of the boundary elements without loops
23874 // Connection flags (to the left) of the shared boundaries
23875 // without loops
23877 // Connection flags (to the right) of the shared boundaries
23878 // without loops
23880
23881 // Break any possible loop created by the shared polyline
23882 this->break_loops_on_shared_polyline_load_balance_helper(
23896
23897 // Get the number of final sorted nodes
23899
23900 // Loop over the list of final sorted nodes
23901 for (unsigned i = 0; i < n_final_sorted_nodes; i++)
23902 {
23903 // Store the list of nodes that gave rise to the shared
23904 // boundary
23907
23908 // Store the local shared boundary id assigned to the
23909 // elements that will create the shared boundary
23911
23912 // Increase the shared boundary id (note that this is only
23913 // used to keep track of the list of nodes that create the
23914 // shared boundaries in the current processor)
23916
23917 // Include the vector of elements to the sorted vector
23919
23920 // Include the vector of face elements to the sorted vector
23921 sorted_face_ele_pt[iproc].push_back(
23923
23924 // Include the vector of elements to the sorted vector
23926
23927 // Include the possible associated boundary id to the vector
23929
23930 // Include the connection information associated with the
23931 // current set of face elements (that will give rise to a
23932 // shared polyline
23933 // The temporal storage for the boundary connections ids
23938
23939 } // for (i < n_final_sorted_nodes)
23940
23941 } // while (nsorted_face_ele < nunsorted_face_ele)
23942
23943 } // if (iproc != my_rank)
23944
23945 } // for (iproc < nproc)
23946
23947 // The time to sort shared boundaries face elements to create a
23948 // continuous representation of the boundary
23949 if (Print_timings_level_load_balance > 2)
23950 {
23951 oomph_info << "CPU for joining shared boundary face elements (load "
23952 "balance) [9.5]: "
23954 << std::endl;
23955 }
23956
23957 // ==================================================================
23958 // END: SORT THE SHARED BOUNDARY FACE ELEMENTS, ADD FACE ELEMENTS TO
23959 // THE LEFT OR RIGHT OF THE ROOT FACE ELEMENT. STOP ADDING WHEN THE
23960 // MOST LEFT OR MOST RIGHT ELEMENT (NODE) IS ALREADY PART OF ANOTHER
23961 // BOUNDARY (THIS MEANS THAT THE SHARED BOUNDARY THAT IS BEING
23962 // CREATED HAS A CONNECTION). ALSO REMEMBER TO CHECK FOR THE CASE
23963 // WHEN THE MOST LEFT OR MOST RIGHT NODE IS A BOUNDARY NODE OF A
23964 // BOUNDARY THAT NO LONGER EXIST IN THE DOMAIN. AT THE END OF THIS
23965 // SECTION WE WILL HAVE THE NUMBER OF SHARED BOUNDARIES OF THIS
23966 // PROCESSOR WITH OTHERS BUT NOT THE GLOBAL SHARED BOUNDARY ID
23967 // ==================================================================
23968
23969 // ==================================================================
23970 // BEGIN: COMPUTE THE GLOBAL SHARED BOUNDARIES IDS. GATHER THE
23971 // NUMBER OF SHARED BOUNDARIES OF EACH PROCESSOR, THEN A ROOT
23972 // PROCESSOR IS IN CHARGE OF VERIFYING THAT THE SAME NUMBER OF
23973 // SHARED BOUNDARIES HAVE BEEN CREATED BY A PAIR OF PROCESSORS. THE
23974 // ROOT PROCESSOR COMPUTES THE INITIAL GLOBAL SHARED BOUNDARY ID
23975 // BETWEEN EACH PAIR OR PROCESSORS AND SENDS THESE INFO. TO ALL
23976 // PROCESSORS. THE GLOBAL INITIAL AND FINAL SHARED BOUNDARY ID ARE
23977 // ALSO COMPUTED
23978 // ==================================================================
23979
23980 // Get the time to compute new shared boundaries ids
23982 if (Print_timings_level_load_balance > 2)
23983 {
23985 }
23986
23987 // Get the number of shared boundaries with in each processor
23989 // Loop over the processors
23990 for (unsigned iproc = 0; iproc < nproc; iproc++)
23991 {
23992 // No shared boundaries with myself
23993 if (iproc != my_rank)
23994 {
23995 // Store the number of shared boundaries of the current
23996 // processor (my_rank) with the iproc processor
23999
24000 } // if (iproc != my_rank)
24001
24002 } // for (iproc < nproc)
24003
24004 // Each processor sends the number of shared boundaries that it has
24005 // with in each other processor to the "root_processor" which will
24006 // be in charge of checking and computing the global shared
24007 // boundaries ids
24008 const unsigned root_processor = 0;
24009
24010 // Get the communicator of the mesh
24011 OomphCommunicator* comm_pt = this->communicator_pt();
24012
24013 // Container where to store the info. received from other processor
24014 // in root. It receives from all processors the number of shared
24015 // boundaries that each one has with any other processor
24017
24018 // Gather the info. in the "root_processor"
24019 MPI_Gather(&nshared_boundaries_with_processor[0], // Info. sent from
24020 // each processor
24021 nproc, // Total number of data to send from each
24022 // processor
24024 &flat_unsigned_root_received_data[0], // Container where
24025 // to receive the
24026 // info. from all
24027 // the processors
24028 nproc, // Number of data to receive from each processor
24030 root_processor, // The processor that receives all the
24031 // info.
24032 comm_pt->mpi_comm());
24033
24034 // Container where root store the info. that will be sent back to
24035 // all processor, because root performs a Broadcast operation then
24036 // the info. is received in the same container
24038
24039 // Compute the new initial and final shared boundary id (they are
24040 // based on the global number of shared boundaries)
24041 unsigned new_initial_shared_boundary_id = 0;
24042 unsigned new_final_shared_boundary_id = 0;
24043
24044 // Compute the boundaries ids for the shared boundaries
24045 if (my_rank == root_processor)
24046 {
24047 // Change the representation of the data received from all
24048 // processors to a matrix representation for ease access
24050 // Loop over the processors and get the number of shared
24051 // boundaries of processor iproc with jproc
24052 for (unsigned iproc = 0; iproc < nproc; iproc++)
24053 {
24054 // Resize the vector to store the data
24056 // Loop over the processors and get the number of shared
24057 // boundaries of processor iproc with jproc
24058 for (unsigned jproc = 0; jproc < nproc; jproc++)
24059 {
24062
24063 } // for (jproc < nproc)
24064
24065 } // for (iproc < nproc)
24066
24067#ifdef PARANOID
24068 // Check that the same number of boundaries are shared by two
24069 // specific processors
24070 for (unsigned iproc = 0; iproc < nproc; iproc++)
24071 {
24072 for (unsigned jproc = 0; jproc < iproc; jproc++)
24073 {
24076 {
24077 std::ostringstream error_stream;
24079 << "ROOT PROCESSOR ERROR\n\n"
24080 << "The number of shared boundaries between processor (" << iproc
24081 << ") and (" << jproc << ") is not the same:\n"
24082 << "Shared boundaries of processor (" << iproc
24083 << ") with processor (" << jproc << "): ("
24085 << "Shared boundaries of processor (" << jproc
24086 << ") with processor (" << iproc << "): ("
24088 throw OomphLibError(error_stream.str(),
24091
24092 } // The number of shared boundaries between processors
24093 // "iproc" and "jproc" is not the same
24094
24095 } // for (jproc < iproc)
24096
24097 } // for (iproc < nproc)
24098#endif
24099
24100 // The enumeration of the shared boundaries starts from the lowest
24101 // processor number to the highest processor number
24102
24103 // Two processors share the same boundaries ids, the lowest
24104 // processor number is the one in charge of computing the shared
24105 // boundaries ids
24107 // Resize the vector, we can not do it when storing the
24108 // info. because of the strategy to save the info.
24109 for (unsigned iproc = 0; iproc < nproc; iproc++)
24110 {
24112 }
24113
24114 // The shared boundaries ids start from the current number of
24115 // original boundaries
24116 unsigned shared_bound_id = this->nboundary();
24117
24118 // Set the new initial shared boundary id
24120
24121 // Assign the global shared boundary id for the shared boundaries
24122 for (unsigned iproc = 0; iproc < nproc; iproc++)
24123 {
24124 for (unsigned jproc = iproc + 1; jproc < nproc; jproc++)
24125 {
24126 // Are there shared boundaries between the pair of processors
24128 {
24129 // Set the start boundary id of processor "iproc" with
24130 // processor "jproc" and viceversa
24135 // Increase the shared boundary id counter with as many
24136 // shared boundaries there are between the iproc and jproc
24137 // processor
24139 } // if (root_nshared_bound_proc_with_proc[iproc][jproc] > 0)
24140
24141 } // for (jproc < iproc)
24142
24143 } // for (iproc < nproc)
24144
24145 // Set the new final shared boundary id
24147
24148 // Prepare the info. to send back to each processor
24150
24151 // Copy the info. to the storage to send the info. back to other
24152 // processors
24153 for (unsigned iproc = 0; iproc < nproc; iproc++)
24154 {
24155 for (unsigned jproc = 0; jproc < nproc; jproc++)
24156 {
24157 // Get the initial shared boundary id between each pair of
24158 // processors (iproc, jproc)
24159 const unsigned initial_shd_bnd_id =
24162
24163 // .. then copy the number of shared boundaries that there are
24164 // between processor iproc and jproc
24165 const unsigned nshared_bnd_iproc_jproc =
24169
24170 } // for (jproc < nproc)
24171
24172 } // for (iproc < nproc)
24173
24174 // .. at the end of the data to send include the global initial
24175 // shared boundary id
24178
24179 // ... and the global final shared boundary id
24182
24183 } // if (my_rank == root_processor)
24184
24185 // Send the initial shared boundaries ids and the number of shared
24186 // boundaries between all procesors to all processors. All
24187 // processors need to know this info.
24188
24189 // The number of data that will be sent by root to other processors
24190 // and the number of data that other processors receive from root,
24191 // it is the same because it is performed via a Broadcast
24194
24195 MPI_Bcast(&root_ndata_sent_to_all_proc, // Data to send
24196 1,
24199 comm_pt->mpi_comm());
24200
24201 // Resize the container if this is a processor that receives data
24202 if (my_rank != root_processor)
24203 {
24205 }
24206
24207 // Send back the start boundaries ids for the shared boundaries
24208 // Scatter the info. from the "root_processor"
24210 // each
24211 // processor
24212 root_ndata_sent_to_all_proc, // Total number of data to
24213 // send to each processor
24215 root_processor, // The processor that sends all the info.
24216 comm_pt->mpi_comm());
24217
24218 // The container to store the initial shared boundaries ids between
24219 // each pair of processors
24221
24222 // All processors need to know how many shared boundaries there are
24223 // between each pair of processors
24224
24225 // The number of shared boundaries between each pair of processors
24227
24228 unsigned iflat_counter = 0;
24229 // Fill the containers with the received info. from root processor
24230 for (unsigned iproc = 0; iproc < nproc; iproc++)
24231 {
24232 // Resize the containers
24235
24236 // Loop over the processors
24237 for (unsigned jproc = 0; jproc < nproc; jproc++)
24238 {
24239 // Get the initial shared boundary id between each pair of
24240 // processors (iproc, jproc)
24243
24244 // .. and copy the number of shared boundaries that there are
24245 // between processor iproc and jproc
24248
24249 } // for (jproc < nproc)
24250
24251 } // for (iproc < nproc)
24252
24253 // Read the new initial shared boundary id
24256
24257 // Read the new final shared boundary id
24260
24261 // The time to compute new shared boundaries ids
24262 if (Print_timings_level_load_balance > 2)
24263 {
24265 << "CPU for computing new shared boundaries ids (load balance) [9.6]: "
24267 << std::endl;
24268 }
24269
24270 // ==================================================================
24271 // END: COMPUTE THE GLOBAL SHARED BOUNDARIES IDS. GATHER THE NUMBER
24272 // OF SHARED BOUNDARIES OF EACH PROCESSOR, THEN A ROOT PROCESSOR IS
24273 // IN CHARGE OF VERIFYING THAT THE SAME NUMBER OF SHARED BOUNDARIES
24274 // HAVE BEEN CREATED BY A PAIR OF PROCESSORS. THE ROOT PROCESSOR
24275 // COMPUTES THE INITIAL GLOBAL SHARED BOUNDARY ID BETWEEN EACH PAIR
24276 // OR PROCESSORS AND SENDS THESE INFO. TO ALL PROCESSORS. THE GLOBAL
24277 // INITIAL AND FINAL SHARED BOUNDARY ID ARE ALSO COMPUTED
24278 // ==================================================================
24279
24280 // ==================================================================
24281 // BEGIN: CREATE THE NEW SHARED BOUNDARIES. DELETE THE OLD SHARED
24282 // BOUNDARIES INFORMATION. FILL THE DATA STRUCTURES WITH THE NEW
24283 // SHARED BOUNDARIES INFO.
24284 // ==================================================================
24285
24286 // Get the time to create new shared boundaries representations
24288 if (Print_timings_level_load_balance > 2)
24289 {
24291 }
24292
24293 // Create the shared boundaries and establish all the related info.
24294 // - Create Polylines
24295 // - Store shared boundary elements
24296 // - Fill data structures to know which shared boundaries belong to
24297 // which processor
24298
24299 // Resize the shared polylines container
24300 this->flush_shared_boundary_polyline_pt();
24301 this->Shared_boundary_polyline_pt.resize(nproc);
24302
24303 // Resize for the boundaries ids shared with all processors
24304 this->Shared_boundaries_ids.clear();
24305 this->Shared_boundaries_ids.resize(nproc);
24306 for (unsigned iproc = 0; iproc < nproc; iproc++)
24307 {
24308 this->Shared_boundaries_ids[iproc].clear();
24309 this->Shared_boundaries_ids[iproc].resize(nproc);
24310 } // for (iproc < nproc)
24311
24312 // Clear data
24313 this->Shared_boundary_from_processors.clear();
24314 this->Shared_boundary_overlaps_internal_boundary.clear();
24315 this->Boundary_was_splitted.clear();
24316 this->Boundary_subpolylines.clear();
24317 this->Boundary_marked_as_shared_boundary.clear();
24318
24319 // Flush data
24320 this->flush_shared_boundary_element();
24321 this->flush_face_index_at_shared_boundary();
24322 this->flush_shared_boundary_node();
24323 this->flush_sorted_shared_boundary_node();
24324
24325 // Store the old local inital shared boundary id (used to map from
24326 // local shared boundary id to global shared boundary id)
24327 const unsigned old_local_shd_bnd_id = this->Initial_shared_boundary_id;
24328
24329 // Update the initial and final shared boundary id
24330 this->Initial_shared_boundary_id = new_initial_shared_boundary_id;
24331 this->Final_shared_boundary_id = new_final_shared_boundary_id;
24332
24333 // Storage for the new created polylines between the current
24334 // processor (my_rank) and the other processors, unsorted polylines
24336
24337 // Map to get the global shared boundary id from the local shared
24338 // boundary id. Note that this is only used to get the global shared
24339 // boundary id when the shared boundary that is being created has
24340 // connections
24341 std::map<unsigned, unsigned> local_to_global_shd_bnd_id;
24342
24343 // Each processor knows the boundaries ids for each of the shared
24344 // boundaries it has, establish that info. in the proper containers
24345 // Additionally, store the shared boundaries of ALL processors with
24346 // ALL processors, but only create the shared boundaries (and their
24347 // respective polylines) of the current processor (my_rank)
24348 for (unsigned iproc = 0; iproc < nproc; iproc++)
24349 {
24350 // Avoid creating double shared boundaries, the shared boundaries
24351 // created between processor "iproc" and processor "jproc" are the
24352 // same than those created between processor "jproc" and processor
24353 // "iproc"
24354 for (unsigned jproc = iproc + 1; jproc < nproc; jproc++)
24355 {
24356 // If we are working with the current processor (my_rank) then
24357 // create the shared boundaries, if that is not the case then
24358 // only fill the info. on the proper containers
24359 if (iproc == my_rank || jproc == my_rank)
24360 {
24361 // Check the condition that made it get here
24362 unsigned ref_proc = 0;
24363 if (iproc == my_rank)
24364 {
24365 ref_proc = jproc;
24366 }
24367 else if (jproc == my_rank)
24368 {
24369 ref_proc = iproc;
24370 }
24371
24372 // Get the number of shared boundaries between processor iproc
24373 // and processor jproc
24374 const unsigned nshared_bound_iproc_jproc =
24376
24377 // Loop over the number of shared boundaries
24378 for (unsigned counter = 0; counter < nshared_bound_iproc_jproc;
24379 counter++)
24380 {
24381 // Compute the shared boundary id for the shared boundary
24382 const unsigned shd_bnd_id =
24384 // Set up the shared boundaries between "iproc" (my_rank)
24385 // and "jproc"
24386 this->Shared_boundaries_ids[iproc][jproc].push_back(shd_bnd_id);
24387 this->Shared_boundaries_ids[jproc][iproc].push_back(shd_bnd_id);
24388
24389 // Specify the processors involved for the creation of the
24390 // shared boundary
24392 processors[0] = iproc;
24393 processors[1] = jproc;
24394 this->Shared_boundary_from_processors[shd_bnd_id] = processors;
24395
24396 // Get the possible root edge id associated to the shared
24397 // boundary (useful when the shared boundary overlaps an
24398 // original boundary)
24400 // Check if the shared boundary is overlapping (or is part)
24401 // of an internal boundary
24402 if (root_edge_bound_id != -1)
24403 {
24404 // If the shared boundary is part of an internal boundary then
24405 // mark the shared boundary
24406 this->Shared_boundary_overlaps_internal_boundary[shd_bnd_id] =
24407 static_cast<unsigned>(root_edge_bound_id);
24408 } // if (root_edge_bound_id != -1)
24409
24410 // Storing for the nodes of the polyline (these are different
24411 // from the nodes on the face elements -- it is actually a
24412 // sub-set -- since the polyline is created from the first and
24413 // last nodes on the face elements)
24415
24416 // Add the first node for the very first face element. In
24417 // the loop we will only add the last node of the face
24418 // element
24421
24422 // Get the number of nodes on the first face element
24423 const unsigned first_face_ele_nnodes = first_face_ele_pt->nnode();
24425 {
24426 // Get the first node
24428 // Add the node to create the polyline
24430 // Add the first node to the shared boundary
24431 this->add_shared_boundary_node(shd_bnd_id, first_node_pt);
24432 }
24433 else
24434 {
24435 // Get the first node in the inverted face element
24438 // Add the node to create the polyline
24440 // Add the first node to the shared boundary
24441 this->add_shared_boundary_node(shd_bnd_id, first_node_pt);
24442 }
24443
24444 // ... and extract only the last nodes of the face elements
24445 // in the next loop and add them in the vector of nodes to
24446 // create polylines (node_pt_to_create_shared_polyline)
24447
24448 // Get the number of elements
24449 const unsigned nshared_boundary_elements =
24451
24452 // Store the shared boundary elements, nodes and get the
24453 // sorted nodes to create the polyline
24454 for (unsigned ie = 0; ie < nshared_boundary_elements; ie++)
24455 {
24456 // Get the bulk element version of the face element
24458
24459 // Add the shared boundary element and associate it to the
24460 // "shd_bnd_id"
24461 this->add_shared_boundary_element(shd_bnd_id, bulk_ele_pt);
24462
24463 // Get the face index from which the face element was
24464 // created from the bulk element
24465 const int face_index =
24467
24468 // Add the face index to the face indexes of the shared
24469 // boundary
24470 this->add_face_index_at_shared_boundary(shd_bnd_id, face_index);
24471
24472 // Get the face element to obtain the last node
24475
24476 // Get the number of nodes
24477 const unsigned nnodes = face_ele_pt->nnode();
24479 {
24480 // We have already added the first node, then start from
24481 // the second one
24482 for (unsigned n = 1; n < nnodes; n++)
24483 {
24484 // Get the node to be added
24486 // Add the node and associate it to the shared boundary
24487 this->add_shared_boundary_node(shd_bnd_id, node_pt);
24488 } // for (n < nnodes)
24489
24490 // Add the last node of the face element to the vector of
24491 // nodes to create the polyline
24492 // Get the last node
24495 } // if (!is_inverted[face_ele_pt])
24496 else
24497 {
24498 // We have already added the first node, then start from
24499 // the second one (in reverse order)
24500 for (int n = nnodes - 2; n >= 0; n--)
24501 {
24502 // Get the node to be added
24504 // Add the node and associate it to the shared boundary
24505 this->add_shared_boundary_node(shd_bnd_id, node_pt);
24506 } // for (n < nnodes)
24507
24508 // Add the last node of the face element to the vector of
24509 // nodes to create the polyline
24510 // Get the last node
24513
24514 } // else if (!is_inverted[face_ele_pt])
24515
24516 } // for (ie < nshared_boundary_elements)
24517
24518 // The number of nodes for the shared boundary polyline
24519 const unsigned nnodes_to_create_shared_boundary =
24521
24522 // Get the vertices that create the shared boundary polyline
24524 for (unsigned n = 0; n < nnodes_to_create_shared_boundary; n++)
24525 {
24526 vertices[n].resize(2);
24527 // Get the node
24529 // Get the vertices
24530 vertices[n][0] = tmp_node_pt->x(0);
24531 vertices[n][1] = tmp_node_pt->x(1);
24532 } // for (n < nnodes_to_create_shared_boundary)
24533
24534 // Create the polyline
24535 TriangleMeshPolyLine* polyline_pt =
24537
24538 // Updates bnd_id<--->curve section map
24539 this->Boundary_curve_section_pt[shd_bnd_id] = polyline_pt;
24540
24541 // Add the new created polyline to the list of unsorted
24542 // polylines
24543 unsorted_polylines_pt.push_back(polyline_pt);
24544
24545 // Mark the polyline for deletion (when calling destructor)
24546 this->Free_curve_section_pt.insert(polyline_pt);
24547
24548 // Now assign the connection information
24549 // ---------------------------------------------------------
24550 // Get the local shared boundary id associated to the
24551 // elements that gave rise to this shared boundary
24552 const unsigned local_shd_bnd_id =
24554
24555 // Associate the local shared boundary to the global shared
24556 // boundary
24558
24559 // Get the correct shared boundaries ids, from the local
24560 // shared boundaries ids established at the identification
24561 // of the conections
24562
24563 // Get the local bnd id for the connection to the left
24566 // Get the local bnd id for the connection to the right
24569
24570 // The global shared boundaries ids for connections to the
24571 // left or right
24574
24575 // To the left
24576 // --------------
24577
24578 // If the connection is with the same shared boundary then
24579 // set the current boundary id
24581 {
24582 // Set the current shared boundary id
24584 } // if (tmp_bnd_id_connection_to_the_left == -2)
24585
24586 // Check if the connection was a stop adding nodes condition
24588 {
24589 // Set as no connected
24591 } // if (tmp_bnd_id_connection_to_the_left == -3)
24592
24593 // There is a connection with another boundary, check if it
24594 // is a shared boundary or an original boundary
24596 static_cast<int>(old_local_shd_bnd_id))
24597 {
24598 // The connection is with a shared boundary, get the
24599 // global shared boundary id and set the connection
24600#ifdef PARANOID
24601 std::map<unsigned, unsigned>::iterator it =
24603 static_cast<unsigned>(tmp_bnd_id_connection_to_the_left));
24604 // If the global shared boundary id was not found we
24605 // are in trouble
24606 if (it == local_to_global_shd_bnd_id.end())
24607 {
24608 std::stringstream error_message;
24609 error_message
24610 << "The global shared boundary id was not found for\n"
24611 << "the local shared boundary shared with processor ("
24612 << ref_proc << ").\n"
24613 << "This processor: (" << my_rank << ")\n"
24614 << "Boundary shared with processor: (" << ref_proc << ")\n"
24615 << "Local shared boundary: ("
24617 throw OomphLibError(error_message.str(),
24620 } // if (it==local_to_global_shd_bnd_id.end())
24621#endif
24622
24623 // Get the global shared boundary id
24625 local_to_global_shd_bnd_id[static_cast<unsigned>(
24627 }
24628 else
24629 {
24630 // The connection is with an original boundary, copy
24631 // the boundary id
24633
24634 } // else (connection with a shared boundary)
24635
24636 // To the right
24637 // --------------
24638
24639 // If the connection is with the same shared boundary then
24640 // set the current boundary id
24642 {
24643 // Set the current shared boundary id
24645 } // if (tmp_bnd_id_connection_to_the_right == -2)
24646
24647 // Check if the connection was a stop adding nodes condition
24649 {
24650 // Set as no connected
24652 } // if (tmp_bnd_id_connection_to_the_right == -3)
24653
24654 // There is a connection with another boundary, check if it
24655 // is a shared boundary or an original boundary
24657 static_cast<int>(old_local_shd_bnd_id))
24658 {
24659 // The connection is with a shared boundary, get the
24660 // global shared boundary id and set the connection
24661#ifdef PARANOID
24662 std::map<unsigned, unsigned>::iterator it =
24664 static_cast<unsigned>(tmp_bnd_id_connection_to_the_right));
24665 // If the global shared boundary id was not found we
24666 // are in trouble
24667 if (it == local_to_global_shd_bnd_id.end())
24668 {
24669 std::stringstream error_message;
24670 error_message
24671 << "The global shared boundary id was not found for\n"
24672 << "the local shared boundary shared with processor ("
24673 << ref_proc << ").\n"
24674 << "This processor: (" << my_rank << ")\n"
24675 << "Boundary shared with processor: (" << ref_proc << ")\n"
24676 << "Local shared boundary: ("
24678 throw OomphLibError(error_message.str(),
24681 } // if (it==local_to_global_shd_bnd_id.end())
24682#endif
24683 // Get the global shared boundary id
24685 local_to_global_shd_bnd_id[static_cast<unsigned>(
24687 }
24688 else
24689 {
24690 // The connection is with an original boundary, copy the
24691 // boundary id
24694
24695 } // else (connection with a shared boundary)
24696
24697 // --------------------------------
24698 // Set the connection to the left
24700 {
24701 // Get the unsigned version of the boundary id to the left
24702 const unsigned ubnd_id_connection_to_the_left =
24703 static_cast<unsigned>(bnd_id_connection_to_the_left);
24704 // Set the initial vertex as connected
24705 polyline_pt->set_initial_vertex_connected();
24706 // Set the initial vertex connected boundary id
24707 polyline_pt->initial_vertex_connected_bnd_id() =
24709 // Set the chunk number to zero
24710 polyline_pt->initial_vertex_connected_n_chunk() = 0;
24711
24712 } // if (bnd_id_connection_to_the_left != -1)
24713
24714 // ---------------------------------
24715 // Set the connection to the right
24717 {
24718 // Get the unsigned version of the boundary id to the
24719 // right
24720 const unsigned ubnd_id_connection_to_the_right =
24721 static_cast<unsigned>(bnd_id_connection_to_the_right);
24722 // Set the final vertex as connected
24723 polyline_pt->set_final_vertex_connected();
24724 // Set the final vertex connected boundary id
24725 polyline_pt->final_vertex_connected_bnd_id() =
24727 // Set the chunk number to zero
24728 polyline_pt->final_vertex_connected_n_chunk() = 0;
24729
24730 } // if (bnd_id_connection_to_the_right != -1)
24731
24732 } // for (counter < nshared_bound_iproc_jproc)
24733
24734 } // if (iproc == my_rank || jproc == my_rank)
24735 else
24736 {
24737 // We are not working with the current processor, then we only
24738 // need to fill the containers
24739
24740 // Get the number of shared boundaries between processor iproc
24741 // and processor jproc
24742 const unsigned nshared_bound_iproc_jproc =
24744 // Loop over the number of shared boundaries
24745 for (unsigned counter = 0; counter < nshared_bound_iproc_jproc;
24746 counter++)
24747 {
24748 // Compute the shared boundary id for the shared boundary
24749 const unsigned shd_bnd_id =
24751
24752 // Set up the shared boundaries between "iproc" and "jproc"
24753 this->Shared_boundaries_ids[iproc][jproc].push_back(shd_bnd_id);
24754 this->Shared_boundaries_ids[jproc][iproc].push_back(shd_bnd_id);
24755
24756 // Specify the processors involved for the creation of the
24757 // shared boundary
24759 processors[0] = iproc;
24760 processors[1] = jproc;
24761 this->Shared_boundary_from_processors[shd_bnd_id] = processors;
24762
24763 } // for (counter < nshared_bound_iproc_jproc)
24764
24765 } // else if (iproc == my_rank || jproc == my_rank)
24766
24767 } // for (jproc < nproc)
24768
24769 } // for (iproc < nproc)
24770
24771 // Get the time to create new shared boundaries representations
24772 if (Print_timings_level_load_balance > 2)
24773 {
24774 oomph_info << "CPU for creating new shared boundaries representations "
24775 "(load balance) [9.7]: "
24778 << std::endl;
24779 }
24780
24781 // ==================================================================
24782 // END: CREATE THE NEW SHARED BOUNDARIES. DELETE THE OLD SHARED
24783 // BOUNDARIES INFORMATION. FILL THE DATA STRUCTURES WITH THE NEW
24784 // SHARED BOUNDARIES INFO.
24785 // ==================================================================
24786
24787 // ==================================================================
24788 // BEGIN: SORT THE SHARED BOUNDARIES AND CREATE SHARED CURVES (A SET
24789 // OF CONTIGUOUS SHARED POLYLINES). STORE THEM IN THE GLOBAL
24790 // CONTAINER FOR SHARED BOUNDARIES. FREE MEMORY BY DELETING FACE
24791 // ELEMENTS
24792 // ==================================================================
24793
24794 // Get the time to create the new shared curves
24796 if (Print_timings_level_load_balance > 2)
24797 {
24799 }
24800
24801 // Sort the polylines and find if they create a contiguous open
24802 // curve
24803 if (unsorted_polylines_pt.size() > 0)
24804 {
24805 // Now that we have all the new unsorted polylines on "my_rank"x
24806 // processor it is time to sort them so they be all contiguous
24807 this->sort_polylines_helper(unsorted_polylines_pt,
24808 this->Shared_boundary_polyline_pt[my_rank]);
24809 }
24810
24811 // Free the memory allocated for the face elements
24812 for (unsigned iproc = 0; iproc < nproc; iproc++)
24813 {
24814 const unsigned nface_ele = unsorted_face_ele_pt[iproc].size();
24815 for (unsigned e = 0; e < nface_ele; e++)
24816 {
24817 delete unsorted_face_ele_pt[iproc][e];
24819 } // for (e < nface_ele)
24820
24821 } // for (iproc < nproc)
24822
24823 // The time to create the new shared curves
24824 if (Print_timings_level_load_balance > 2)
24825 {
24827 << "CPU for creating the new shared curves (load balance) [9.8]: "
24829 << std::endl;
24830 }
24831
24832 // ==================================================================
24833 // END: SORT THE SHARED BOUNDARIES AND CREATE SHARED CURVES (A SET
24834 // OF CONTIGUOUS SHARED POLYLINES). STORE THEM IN THE GLOBAL
24835 // CONTAINER FOR SHARED BOUNDARIES. FREE MEMORY BY DELETING FACE
24836 // ELEMENTS
24837 // ==================================================================
24838 }
24839
24840 //======================================================================
24841 // Computes the degree of the nodes on the shared boundaries, the
24842 // degree of the node is computed from the global graph created by the
24843 // shared boundaries of all processors
24844 //======================================================================
24845 template<class ELEMENT>
24848 std::map<Node*, unsigned>& global_node_degree)
24849 {
24850 // Get the rank and number of processors
24851 const unsigned nproc = this->communicator_pt()->nproc();
24852 const unsigned my_rank = this->communicator_pt()->my_rank();
24853
24854 // Store a temporary sorting of the nodes, starting from the
24855 // lower-left position
24857
24858 // Store the alias of the node, it may be shared by more than two
24859 // processors, they should know that the node is the same
24860 // [0] iproc, processor with which the current processor shared the node
24861 // [1] node #, number of node in the number of nodes shared with iproc
24862 // processor
24863 std::map<Node*, Vector<Vector<unsigned>>> node_alias;
24864
24865 // Stores the local adjacency matrix
24866 // (nproc*n_shared_nodes*n_shared_nodes)
24868
24869 // Sort the nodes and create the adjacency matrix of each sub-graph
24870 // created by the shared edges
24871 create_adjacency_matrix_new_shared_edges_helper(unsorted_face_ele_pt,
24873 node_alias,
24875
24876 // Prepare the info. to be sent to the root processor, which will be
24877 // in charge of updating the nodes degree by combining the info. of
24878 // all the processors
24879
24880 // The flat package with the info. to send to root
24882
24883 // Encode the info. that will be sent to the root processor
24884
24885 // Loop over the temporary sorted nodes between each pair of
24886 // processors
24887 for (unsigned iproc = 0; iproc < nproc; iproc++)
24888 {
24889 // Send the processor index
24891
24892 // Get the number of nodes shared between the processors
24893 const unsigned n_nodes = tmp_sorted_shared_node_pt[iproc].size();
24894
24895 // Send the number of nodes shared with the iproc processor
24897
24898 // Loop over the nodes
24899 for (unsigned ishd = 0; ishd < n_nodes; ishd++)
24900 {
24901 // Get the node
24903
24904 // Get the alias info.
24906
24907 // Get the number of alias for the node
24908 const unsigned n_alias = alias_node_info.size();
24909
24910 // Send the number of alias assigned to the node
24912
24913 // Loop over the alias to include them in the package
24914 for (unsigned i = 0; i < n_alias; i++)
24915 {
24916 // Send the alias info.
24917 // The current processor
24919 // The prociesso with which is shared
24921 // The index of the node
24923 } // for (i < n_alias)
24924
24925 } // for (ishd < n_nodes)
24926
24927 // Now send the adjacency matrix
24928 for (unsigned i = 0; i < n_nodes; i++)
24929 {
24930 for (unsigned j = 0; j < n_nodes; j++)
24931 {
24932 // Package the adjacency matrix
24935
24936 } // for (j < n_nodes)
24937
24938 } // for (i < n_nodes)
24939
24940 } // for (iproc < nproc)
24941
24942 // Define the root processor
24943 const unsigned root_processor = 0;
24944
24945 // Get the communicator of the mesh
24946 OomphCommunicator* comm_pt = this->communicator_pt();
24947
24948 // Number of data send. from this processor to root processor
24951
24952 // Store the number of data to receive from each processor in root
24954
24955 // Send the number of data that each processor will send to root
24956 // Gather the info. in the "root_processor"
24957 MPI_Gather(&n_unsigned_data_send_to_root, // Info. sent from
24958 // each processor
24959 1, // Total number of data to send from each processor
24961 &n_unsigned_data_received_in_root[0], // Container where
24962 // to receive the
24963 // info. from all
24964 // the processors
24965 1, // Number of data to receive from each processor
24967 root_processor, // The processor that receives all the
24968 // info.
24969 comm_pt->mpi_comm());
24970
24971 // Compute the total number of data to receive from all processors
24973 for (unsigned iproc = 0; iproc < nproc; iproc++)
24974 {
24975 // Add the number of data to receive from each processor
24978 }
24979
24980 // Compute the offsets from each processor
24983 for (unsigned iproc = 1; iproc < nproc; iproc++)
24984 {
24985 // Compute the offset to store the values received from each
24986 // processor
24990 }
24991
24992 // Create at least one entry so we don't get a seg fault below
24994 {
24996 }
24997
24998 // Vector where to receive the data sent from each processor
25001 if (my_rank != root_processor)
25002 {
25003 // Create at least one entry so we don't get a seg fault below
25005 {
25007 }
25008 } // if (my_rank!=root_processor)
25009
25010 // Gather the info. from all processors
25012 // to send
25013 // info. from
25014 // each
25015 // processor
25016 n_unsigned_data_send_to_root, // Total number of data to
25017 // send from each
25018 // processor
25020 &package_unsigned_data_received_root[0], // Container
25021 // where to
25022 // receive the
25023 // info. from
25024 // all the
25025 // processors
25026 &n_unsigned_data_received_in_root[0], // Number of data
25027 // to receive from
25028 // each processor
25029 &root_unsigned_offsets_receive[0], // The offset to
25030 // store the
25031 // info. from each
25032 // processor
25034 root_processor, // The processor that receives all the
25035 // info.
25036 comm_pt->mpi_comm());
25037
25038 // Store the info. to be sent by root to other processors
25040 // Total data sent to each processor from root
25042
25043 // The root processor now has all the info. regarding the shared
25044 // nodes and the adjacency matrix of each pair of processors
25045 if (my_rank == root_processor)
25046 {
25047 // Decode the info. received from all processors
25048 // Counter to decode the info.
25049 unsigned decode_counter = 0;
25050
25051 // Store the local alias of the nodes in each processor
25052 // [x][][][][] iproc
25053 // [][x][][][] jproc
25054 // [][][x][][] inode
25055 // [][][][x][] ialias
25056 // [][][][][x] alias_data
25058 // Store the local adjacency matrix of each processor
25060
25061 // Loop over all the processors
25062 for (unsigned iproc = 0; iproc < nproc; iproc++)
25063 {
25064 local_node_alias[iproc].resize(nproc);
25065
25066 // Resize the local adjacency matrix to store the info. sent
25067 // from all processors
25069
25071 {
25072 // Loop over all the processors to decode the info. received
25073 // from each one
25074 for (unsigned jproc = 0; jproc < nproc; jproc++)
25075 {
25076 // Read the processor number to which the info. correspond
25077 const unsigned read_jproc =
25079
25080 // The read processor must be the same as the jproc, if that
25081 // is not the case then there is a synchronisation issue
25082 if (read_jproc != jproc)
25083 {
25084 std::ostringstream error_stream;
25086 << "The read processor is different from the jproc, this is\n"
25087 << "a synchronisation issue. The data are not read in the\n"
25088 << "sameorder as the were packaged\n"
25089 << "Read processor: (" << read_jproc << ")\n"
25090 << "Current jproc: (" << jproc << ")\n\n";
25091 throw OomphLibError(
25092 error_stream.str(),
25093 "RefineableTriangleMesh::compute_shared_node_degree_helper()",
25095 }
25096
25097 // Read the number of nodes in the shared boundaries between
25098 // iproc and jproc
25099 const unsigned read_n_shd_nodes_iproc_jproc =
25101
25102 // Resize the container
25104
25105 // Loop over the number of nodes shared between iproc and
25106 // jproc
25107 for (unsigned ishd = 0; ishd < read_n_shd_nodes_iproc_jproc; ishd++)
25108 {
25109 // Read the number of alias of the current ishd node
25110 const unsigned read_n_alias_node_iproc_jproc =
25112
25113 // Resize the container
25114 local_node_alias[iproc][jproc][ishd].resize(
25116
25117 for (unsigned ialias = 0; ialias < read_n_alias_node_iproc_jproc;
25118 ialias++)
25119 {
25120 // Resize the container, we know there are three data to
25121 // define the alias of a node
25122 local_node_alias[iproc][jproc][ishd][ialias].resize(3);
25123
25124 // The 1st processor with which is shared
25127
25128 // The 2nd processor with which is shared
25131
25132 // The index of the node in the interaction iproc-jproc
25135
25136 } // for (ialias < read_n_alias_node_iproc_jproc)
25137
25138 } // for (ishd < read_n_shd_nodes_iproc_jproc)
25139
25140 // Resize the local adjacency matrix
25143 // Read the adjacency matrix sent to root processor
25144 for (unsigned i = 0; i < read_n_shd_nodes_iproc_jproc; i++)
25145 {
25146 // Resize the local adjacency matrix
25149 for (unsigned j = 0; j < read_n_shd_nodes_iproc_jproc; j++)
25150 {
25151 // Read the adjacency matrix entry
25154 } // for (j < read_n_shd_nodes_iproc_jproc)
25155
25156 } // for (i < read_n_shd_nodes_iproc_jproc)
25157
25158 } // for (jproc < nproc)
25159
25160 } // for (iproc < nproc)
25161
25162 } // for (iproc < nproc)
25163
25164#ifdef PARANOID
25166 {
25167 std::ostringstream error_stream;
25169 << "The number of data decoded in root received from others\n"
25170 << "processors is different from the total number of data received\n"
25171 << "Data decoded: (" << decode_counter << ")\n"
25172 << "Data received: (" << n_unsigned_total_data_receive_in_root
25173 << ")\n\n"
25174 << "This is a synchronisation issue so you are probably sending\n"
25175 << "more or less info. than the one that is being decoded\n\n";
25176 throw OomphLibError(
25177 error_stream.str(),
25178 "RefineableTriangleMesh::compute_shared_node_degree_helper()",
25180 }
25181#endif
25182
25183 // Assign a unique id to the nodes (uses the alias information to
25184 // identify the repetition of a node in other processors). The
25185 // global node id is given by the position (index) in the global
25186 // node alias
25187
25188 // Keep track of those alias already assigned a unique id
25189 std::map<Vector<unsigned>, bool> alias_done;
25190
25191 // Store all the alias associated to each node
25193
25194 // Loop over all the processors
25195 for (unsigned iproc = 0; iproc < nproc; iproc++)
25196 {
25197 for (unsigned jproc = iproc + 1; jproc < nproc; jproc++)
25198 {
25199 // Read the number of nodes shared between the processors
25200 const unsigned n_shd_nodes_iproc_jproc =
25202#ifdef PARANOID
25203 // Read the number of nodes shared in the other direction
25204 const unsigned n_shd_nodes_jproc_iproc =
25206
25208 {
25209 std::ostringstream error_stream;
25211 << "The number of nodes shared between iproc and jproc is\n"
25212 << "different from the number of nodes shared between jproc\n"
25213 << "and iproc\n"
25214 << "Nodes shared between processor (" << iproc << ") and "
25215 << "processor (" << jproc << "): (" << n_shd_nodes_iproc_jproc
25216 << ")\n"
25217 << "Nodes shared between processor (" << jproc << ") and "
25218 << "processor (" << iproc << "): (" << n_shd_nodes_jproc_iproc
25219 << ")\n\n";
25220 throw OomphLibError(
25221 error_stream.str(),
25222 "RefineableTriangleMesh::compute_shared_node_degree_helper()",
25224 } // if (n_shd_nodes_iproc_jproc != n_shd_nodes_jproc_iproc)
25225#endif
25226
25227 // Loop over the nodes shared between the processors
25228 for (unsigned ishd = 0; ishd < n_shd_nodes_iproc_jproc; ishd++)
25229 {
25230 // Get the number of alias associated to the node on each
25231 // processor
25232 const unsigned n_alias_iproc_jproc =
25234 const unsigned n_alias_jproc_iproc =
25236
25237 // Store all the found alias to the node
25239
25240 // Flag to indicate if a new alias has been added
25241 bool new_alias_added = false;
25242
25243 // Start by adding the "direct" alias of the node
25244 for (unsigned ialias = 0; ialias < n_alias_iproc_jproc; ialias++)
25245 {
25246 // Get the alias of the node
25249 // Check if already done
25251 {
25252 // Add the alias of the node
25253 node_alias.push_back(current_alias);
25254 // Set the flag to indicate a new alias has been added
25255 new_alias_added = true;
25256 // Mark the alias as done
25257 alias_done[current_alias] = true;
25258 } // if (!alias_done[i_alias])
25259
25260 } // for (ialias < n_alias_iproc_jproc)
25261
25262 // Start by adding the "direct" alias of the node
25263 for (unsigned ialias = 0; ialias < n_alias_jproc_iproc; ialias++)
25264 {
25265 // Get the alias of the node
25268
25269 // Check if already done
25271 {
25272 // Add the alias of the node
25273 node_alias.push_back(current_alias);
25274 // Set the flag to indicate a new alias has been added
25275 new_alias_added = true;
25276 // Mark the alias as done
25277 alias_done[current_alias] = true;
25278 } // if (!alias_done[i_alias])
25279
25280 } // for (ialias < n_alias_jproc_iproc)
25281
25282 unsigned counter_alias = 0;
25283 // Visit the alias of the node and add any new found
25284 // alias, end until all its alias have been included
25285
25286 unsigned n_current_alias = node_alias.size();
25288 // while(new_alias_added) // we need to check all the alias,
25289 // including those added during the process
25290 {
25291 new_alias_added = false;
25292 // Store the current visited alias
25294
25295 // Get the alias associated with the current alias
25298 [current_alias[2]];
25299
25300 // Get all the alias associated with the alias of the
25301 // current alias
25302 const unsigned n_alias = alias_of_current_alias.size();
25303
25304 // Loop over the new alias and check if require to add
25305 // them
25306 for (unsigned k = 0; k < n_alias; k++)
25307 {
25308 // Get the alias of the node
25310
25311 // Check if already done
25312 if (!alias_done[add_alias])
25313 {
25314 // Add the alias of the node
25315 node_alias.push_back(add_alias);
25316 // Set the flag to indicate a new alias has been
25317 // added
25318 new_alias_added = true;
25319 // Mark the alias ad done
25320 alias_done[add_alias] = true;
25321 } // if (!alias_done[i_alias])
25322
25323 } // for (k < n_alias)
25324
25325 // Get the alias associated with the current alias (in the
25326 // other direction)
25329 [current_alias[2]];
25330
25331 // Get all the alias associated with the current alias
25332 // (in the other direction)
25333 const unsigned n_alias2 = alias_of_current_alias2.size();
25334
25335 // Loop over the new alias and check if require to add
25336 // them
25337 for (unsigned k = 0; k < n_alias2; k++)
25338 {
25339 // Get the alias of the node
25341
25342 // Check if already done
25343 if (!alias_done[add_alias])
25344 {
25345 // Add the alias of the node
25346 node_alias.push_back(add_alias);
25347 // Set the flag to indicate a new alias has been
25348 // added
25349 new_alias_added = true;
25350 // Mark the alias ad done
25351 alias_done[add_alias] = true;
25352 } // if (!alias_done[i_alias])
25353
25354 } // for (k < n_alias)
25355
25356 // Go for the next alias
25357 counter_alias++;
25358
25359 // Update the number of alias so that the while stops when
25360 // all the alias have been visited and no new alias was
25361 // added
25363
25364 } // while(new_alias_added || counter_alias < n_current_alias)
25365
25366 // If the node has not been previously added, then include
25367 // all its alias
25368 if (node_alias.size() > 0)
25369 {
25370 // Add all the found alias of the node to the global alias
25371 // storage
25372 global_node_alias.push_back(node_alias);
25373 }
25374
25375 } // for (ishd < n_shd_nodes_iproc_jproc)
25376
25377 } // for (jproc < nproc)
25378
25379 } // for (iproc < nproc)
25380
25381 // We now have the global number of nodes, each with its own id
25382 // (the index in the global_node_alias vector)
25383
25384 // Get the number of global shared nodes
25385 const unsigned n_global_shared_nodes = global_node_alias.size();
25386
25387 // Create matrix from local to global shared node id
25389
25390 // Loop over all the processors to resize
25391 for (unsigned iproc = 0; iproc < nproc; iproc++)
25392 {
25393 // Resize the map matrix
25395 } // for (iproc < nproc)
25396
25397 // Loop over all the processors to resize (the third direction,
25398 // required if we want to loop over the half of the matrix only)
25399 for (unsigned iproc = 0; iproc < nproc; iproc++)
25400 {
25401 // Loop over the half of the matrix to resize
25402 for (unsigned jproc = iproc + 1; jproc < nproc; jproc++)
25403 {
25404 // Read the number of nodes shared between the processors
25405 const unsigned n_shd_nodes = local_node_alias[iproc][jproc].size();
25406
25407 // Resize the map matrix
25409
25410 // ... and resize the other half map matrix
25412
25413 } // for (jproc < nproc)
25414
25415 } // for (iproc < nproc)
25416
25417 // Fill the matrix for mapping from local to global node id
25418
25419 // Loop over the global nodes, and for each alias assign the
25420 // corresponding global node id
25421 for (unsigned k = 0; k < n_global_shared_nodes; k++)
25422 {
25423 // Get the number of alias associated to the current global node
25424 const unsigned n_alias_global_node = global_node_alias[k].size();
25425 // Loop over the alias and assign the global node id
25426 for (unsigned l = 0; l < n_alias_global_node; l++)
25427 {
25428 // Get the 1st processor
25429 const unsigned iproc = global_node_alias[k][l][0];
25430 // Get the 2nd processor
25431 const unsigned jproc = global_node_alias[k][l][1];
25432 // Get the node number
25433 const unsigned ishd = global_node_alias[k][l][2];
25434 // Assign the global node id
25436
25437 } // for (l < n_alias_global_node)
25438
25439 } // for (k < n_global_shared_nodes)
25440
25441 // Create the global adjacency matrix
25443 // Resize the global adjacency matrix
25444 for (unsigned k = 0; k < n_global_shared_nodes; k++)
25445 {
25446 // Resize
25448 } // for (k < n_global_shared_nodes)
25449
25450 // Add the entries to the global adjacency matrix and compute the
25451 // degree of each node
25452
25453 // Store the degree of the global nodes
25455
25456 // Loop over the processors
25457 for (unsigned iproc = 0; iproc < nproc; iproc++)
25458 {
25459 // Loop over the half of the matrix to resize
25460 for (unsigned jproc = iproc + 1; jproc < nproc; jproc++)
25461 {
25462 // Get the number of nodes shared between the processors
25463 const unsigned n_shd_nodes = local_node_alias[iproc][jproc].size();
25464
25465 // Search for entries in the local adjacency matrix that set a
25466 // connection among the nodes
25467
25468 // Loop over the shared nodes in the current pair of
25469 // processors
25470 for (unsigned ishd = 0; ishd < n_shd_nodes; ishd++)
25471 {
25472 for (unsigned jshd = ishd + 1; jshd < n_shd_nodes; jshd++)
25473 {
25474 // Are the nodes associated
25476 {
25477 // Get the global nodes id
25478
25479 // Get the "left-node" global id
25480 const int global_shd_node_left =
25482
25483 // Get the "right-node" global id
25484 const int global_shd_node_right =
25486
25487#ifdef PARANOID
25488 // Check if the local nodes have a global node
25489 // associated
25490 if (global_shd_node_left == -1)
25491 {
25492 std::ostringstream error_stream;
25494 << "The local node in processors iproc and jproc has no\n"
25495 << "global node assigned\n"
25496 << "iproc processor: (" << iproc << ")\n"
25497 << "jproc processor: (" << jproc << ")\n"
25498 << "Local node: (" << ishd << ")\n\n";
25499 throw OomphLibError(error_stream.str(),
25500 "RefineableTriangleMesh::compute_shared_"
25501 "node_degree_helper()",
25503 }
25504
25505 // Check if the local nodes have a global node
25506 // associated
25507 if (global_shd_node_right == -1)
25508 {
25509 std::ostringstream error_stream;
25511 << "The local node in processors iproc and jproc has no\n"
25512 << "global node assigned\n"
25513 << "iproc processor: (" << iproc << ")\n"
25514 << "jproc processor: (" << jproc << ")\n"
25515 << "Local node: (" << jshd << ")\n\n";
25516 throw OomphLibError(error_stream.str(),
25517 "RefineableTriangleMesh::compute_shared_"
25518 "node_degree_helper()",
25520 }
25521#endif
25522 // Get the unsigned version of the indexes
25523 const unsigned uleft =
25524 static_cast<unsigned>(global_shd_node_left);
25525 const unsigned uright =
25526 static_cast<unsigned>(global_shd_node_right);
25527
25528 // Add the entry in the global adjacency matrix
25530
25531 // ... and in the other direction too
25533
25534 // Add on to the degree of the left node
25536
25537 // Add on to the degree of the right node
25539
25540 } // if (local_adjacency_matrix[iproc][jproc][ishd][jshd] > 0)
25541
25542 } // // for (jshd < n_shd_nodes)
25543
25544 } // for (ishd < n_shd_nodes)
25545
25546 } // for (jproc < nproc)
25547
25548 } // for (iproc < nproc)
25549
25550 // Assign the global degree to the shared nodes between each pair
25551 // of processors
25553 // Resize the container
25554 for (unsigned iproc = 0; iproc < nproc; iproc++)
25555 {
25557 }
25558
25559 // Loop over the processors and visited their shared nodes
25560 for (unsigned iproc = 0; iproc < nproc; iproc++)
25561 {
25562 // Only visit the half of the data
25563 for (unsigned jproc = iproc + 1; jproc < nproc; jproc++)
25564 {
25565 // Get the number of shared nodes between this pair of
25566 // processors (iproc, jproc)
25567 const unsigned n_shd_nodes = local_node_alias[iproc][jproc].size();
25568
25569 // Resize the container to store the local degree of the nodes
25571 // ... and in the other way too
25573
25574 // Loop over the number of nodes shared between the pair of
25575 // processors
25576 for (unsigned ishd = 0; ishd < n_shd_nodes; ishd++)
25577 {
25578 // Get the global node id for the current shared node
25579 const int global_shd_node_id =
25581
25582#ifdef PARANOID
25583 // Check if the local nodes have a global node associated
25584 if (global_shd_node_id == -1)
25585 {
25586 std::ostringstream error_stream;
25588 << "The local node in processors iproc and jproc has no\n"
25589 << "global node assigned\n"
25590 << "iproc processor: (" << iproc << ")\n"
25591 << "jproc processor: (" << jproc << ")\n"
25592 << "Local node: (" << ishd << ")\n\n";
25593 throw OomphLibError(
25594 error_stream.str(),
25595 "RefineableTriangleMesh::compute_shared_node_degree_helper()",
25597 }
25598#endif
25599
25600 // Get the unsigned version of the global index
25601 const unsigned uglobal_shd_node_id =
25602 static_cast<unsigned>(global_shd_node_id);
25603
25604 // Get the degree of the node
25605 const unsigned node_degree =
25607
25608 // Set the degree in the container for the degree of the
25609 // nodes in the local interaction between processors
25611 // ... and in the other way too
25613
25614 } // for (ishd < n_shd_nodes)
25615
25616 } // for (jproc < nproc)
25617
25618 } // for (iproc < nproc)
25619
25620 // Clear the container where the info. will be sent back to each
25621 // processor
25623
25624 // Prepare the data to sent it back to each processor (encode the
25625 // info. to sent to all processors)
25626 for (unsigned iproc = 0; iproc < nproc; iproc++)
25627 {
25628 // Count the number of data sent to iproc processor
25629 unsigned count_n_data_sent_to_iproc = 0;
25630 for (unsigned jproc = 0; jproc < nproc; jproc++)
25631 {
25632 // No shared nodes between the same processor
25633 if (iproc != jproc)
25634 {
25635 // Get the number of nodes shared between the processors
25636 const unsigned n_shd_nodes =
25638
25639 // Add the number of data sent to iproc processor
25641
25642 // Loop over the nodes shared between the pair of processors
25643 for (unsigned ishd = 0; ishd < n_shd_nodes; ishd++)
25644 {
25647 } // for (ishd < n_shd_nodes)
25648
25649 } // if (iproc != jproc)
25650
25651 } // for (jproc < nproc)
25652
25653 // Set the number of data sent to iproc processor
25655
25656 } // for (iproc < nproc)
25657
25658 } // if (my_rank == root_processor)
25659
25660 // Total data received from root to this processor
25662
25663 // Get the number of data that each processor receives from root
25664 MPI_Scatter(&n_unsigned_data_sent_from_root[0], // Info. sent from
25665 // root to each
25666 // processor
25667 1, // The number of data sent from root to each
25668 // processor
25671 // info. received
25672 // from root
25673 1, // The number of data received from root
25675 root_processor, // The processor that sends the
25676 // info.
25677 comm_pt->mpi_comm());
25678
25679 // Receive the info. sent by root
25682
25683 // Compute the offsets to each processor
25686 for (unsigned iproc = 1; iproc < nproc; iproc++)
25687 {
25688 // Compute the offset to send the values to each processor
25692 }
25693
25694 if (my_rank != root_processor)
25695 {
25696 // Create at least one entry so we don't get a seg fault below
25698 {
25700 }
25701 } // if (my_rank!=root_processor)
25702
25703 // Create at least one entry so we don't get a seg fault below
25705 {
25707 }
25708
25709 // Get the data from root
25711 // info. sent
25712 // from root
25713 // to others
25714 // processors
25715 &n_unsigned_data_sent_from_root[0], // The number of
25716 // data sent from
25717 // root to others
25718 // processors
25719 &root_unsigned_offsets_sent[0], // The offsets to each
25720 // processors
25723 // storage
25724 // in the
25725 // processor
25726 // that
25727 // receives
25728 // the
25729 // info.
25730 n_unsigned_data_received_from_root, // The number of
25731 // data that the
25732 // current
25733 // processor
25734 // receives from
25735 // root
25737 root_processor, // The root processors
25738 comm_pt->mpi_comm());
25739
25740 // Decode the info.
25741
25742 // Keep track of the already nodes done
25743 std::map<Node*, bool> node_done;
25744
25745 // Read the global degree assigned to the shared nodes between the
25746 // current processors and the other processors
25747 int decode_counter = 0;
25748 // Store the global degree of the local nodes
25750 // Loop over the processors
25751 for (unsigned iproc = 0; iproc < nproc; iproc++)
25752 {
25753 // There are no shared nodes with the current processor itself
25754 if (iproc != my_rank)
25755 {
25756 // Get the number of nodes shared with the iproc processor
25757 const unsigned n_nodes = tmp_sorted_shared_node_pt[iproc].size();
25758
25759 // Read the global degree of the node
25761
25762 // Loop over the nodes
25763 for (unsigned ishd = 0; ishd < n_nodes; ishd++)
25764 {
25765 // Get the node degree assigned to the ishd node in between
25766 // the interaction of the iproc and the current processor
25767 const unsigned node_degree =
25769
25770 // Get the node
25772
25773 // Has the node been assigned a global degree
25774 if (!node_done[shd_node_pt])
25775 {
25776 // Assign the global degree to the node
25778 // Mark the node as done
25779 node_done[shd_node_pt] = true;
25780 }
25781#ifdef PARANOID
25782 else
25783 {
25784 // The node has been already done, check that the node
25785 // degree is the same as the already assigned
25787 {
25788 std::ostringstream error_stream;
25790 << "The local node has already assigned a global degree,\n"
25791 << "however, a different degree for the same node has been\n"
25792 << "read from the data sent from root processor\n"
25793 << "iproc processor: (" << iproc << ")\n"
25794 << "Local node: (" << ishd << ")\n"
25795 << "---------------------------------------------------------\n"
25796 << "Already assigned degree: ("
25797 << global_node_degree[shd_node_pt] << ")\n"
25798 << "New found degree: (" << node_degree << ")\n"
25799 << "---------------------------------------------------------\n"
25800 << "Node coordinates: (" << shd_node_pt->x(0) << ", "
25801 << shd_node_pt->x(1) << ")\n\n";
25802 throw OomphLibError(
25803 error_stream.str(),
25804 "RefineableTriangleMesh::compute_shared_node_degree_helper()",
25806 }
25807
25808 } // else if (!node_done[shd_node_pt])
25809#endif // #ifdef PARANOID
25810
25811 } // for (ishd < n_nodes)
25812
25813 } // if (iproc != my_rank)
25814
25815 } // for (iproc < nproc)
25816
25817#ifdef PARANOID
25818 // Ensure that all the info. sent from root processor has been read
25820 {
25821 std::ostringstream error_stream;
25823 << "The number of data decoded received from root processor is\n"
25824 << "different from the total number of data received from the root\n"
25825 << "processor\n"
25826 << "Data decoded: (" << decode_counter << ")\n"
25827 << "Data received: (" << n_unsigned_data_received_from_root << ")\n\n"
25828 << "This is a synchronisation issue so you are probably sending\n"
25829 << "more or less info. than the one that is being decoded\n\n";
25830 throw OomphLibError(
25831 error_stream.str(),
25832 "RefineableTriangleMesh::compute_shared_node_degree_helper()",
25834 }
25835#endif
25836 }
25837
25838 //======================================================================
25839 // Sort the nodes on the new shared boundaries (after load balancing),
25840 // computes the alias of the nodes and creates the adjacency matrix
25841 // that represent the graph created by the shared edges between each
25842 // pair of processors
25843 // ======================================================================
25844 template<class ELEMENT>
25849 std::map<Node*, Vector<Vector<unsigned>>>& node_alias,
25851 {
25852 // Get the number of processors and the rank
25853 const unsigned nproc = this->communicator_pt()->nproc();
25854 const unsigned my_rank = this->communicator_pt()->my_rank();
25855
25856 // Assign a unique id to each node shared between each pair of
25857 // processors, in this case the current processor and the iproc
25858
25859 // ... also compute the alias of each node (processor and index of
25860 // the node in all processors where it appears)
25861
25862 // Clear the alias info
25863 node_alias.clear();
25864
25865 // Temporary storage for the index of the nodes
25867
25868 // Loop over the processors
25869 for (unsigned iproc = 0; iproc < nproc; iproc++)
25870 {
25871 // There is no shared elements between the same processor
25872 if (iproc != my_rank)
25873 {
25874 // Map to mark those nodes already visited
25875 std::map<Node*, bool> done_node;
25876
25877 // A map is used to sort the nodes using their coordinates as
25878 // the key of the map
25879 // std::map<std::pair<double, double>, Node*> sorted_nodes_pt;
25880 std::map<std::pair<double, double>, Node*, classcomp> sorted_nodes_pt;
25881
25882 // Get the number of unsorted face elements
25884
25885 // Loop over the unsorted elements
25886 for (unsigned e = 0; e < n_unsorted_face_ele; e++)
25887 {
25888 // Get a root element
25890 // Get the left node of the face element
25892
25893 // Check if the node has been already sorted in the
25894 // interaction between the current processor and iproc
25895 // processor
25896 if (!done_node[left_node_pt])
25897 {
25898 std::pair<double, double> vertex =
25899 std::make_pair(left_node_pt->x(0), left_node_pt->x(1));
25901 // Mark the node as done
25902 done_node[left_node_pt] = true;
25903 }
25904
25905 // Get the number of nodes of the face element
25906 const unsigned n_nodes = face_ele_pt->nnode();
25907 // Get the right node of the face element
25909
25910 // Check if the node has been already sorted in the
25911 // interaction between the current processor and iproc
25912 // processor
25914 {
25915 std::pair<double, double> vertex =
25916 std::make_pair(right_node_pt->x(0), right_node_pt->x(1));
25918 // Mark the node as done
25919 done_node[right_node_pt] = true;
25920 }
25921
25922 } // for (e < nunsorted_face_ele)
25923
25924 // The nodes are already sorted, we need to return them in the
25925 // proper container
25926
25927 // The counter to enumerate the nodes
25928 unsigned counter = 0;
25929
25930 // Go through the map container which already have the nodes
25931 // sorted they have the same sorting on all processors
25932 for (std::map<std::pair<double, double>, Node*>::iterator it =
25933 sorted_nodes_pt.begin();
25934 it != sorted_nodes_pt.end();
25935 it++)
25936 {
25937 // Get the node
25938 Node* node_pt = (*it).second;
25939 // Store the node at the corresponding index
25941
25942 // Create the temporary access to the node index
25944
25945 // Fill the info. for the node alias
25947 // The current processor
25948 alias[0] = my_rank;
25949 // The processor with which is shared
25950 alias[1] = iproc;
25951 // The index with that processor
25952 alias[2] = counter++;
25953
25954 // Store the info. of the alias
25955 node_alias[node_pt].push_back(alias);
25956
25957 } // Loop map
25958
25959 } // if (iproc != my_rank)
25960
25961 } // for (iproc < nproc)
25962
25963 // Loop over the processors to resize and initialize the adjacency
25964 // matrix
25965 for (unsigned iproc = 0; iproc < nproc; iproc++)
25966 {
25967 // Get the number of nodes shared with iproc
25968 const unsigned n_shd_nodes = tmp_sorted_shared_node_pt[iproc].size();
25969 // Resize the adjacency matrix
25971 for (unsigned i = 0; i < n_shd_nodes; i++)
25972 {
25973 // Resize the adjacency matrix
25975
25976 // Initialize the
25977 for (unsigned j = 0; j < n_shd_nodes; j++)
25978 {
25979 adjacency_matrix[iproc][i][j] = 0;
25980 } // for (j < n_shd_nodes)
25981
25982 } // for (i < n_shd_nodes)
25983
25984 } // for (iproc < nproc)
25985
25986 // Loop over the processors to fill the adjacency matrix
25987 for (unsigned iproc = 0; iproc < nproc; iproc++)
25988 {
25989 // There is no shared elements between the same processor
25990 if (iproc != my_rank)
25991 {
25992 // Get the number of unsorted face elements
25994
25995 // Loop over the unsorted elements
25996 for (unsigned e = 0; e < n_unsorted_face_ele; e++)
25997 {
25998 // Get a root element
26000 // Get the left node of the face element
26002
26003 // Get the number of nodes of the face element
26004 const unsigned n_nodes = face_ele_pt->nnode();
26005 // Get the right node of the face element
26007
26008 // Get the index of each of the nodes
26010 const unsigned right_node_index =
26012
26013 // Add an entry to the adjacency matrix to indicate the
26014 // association of left and right node
26016 // ... both directions
26018
26019 } // for (e < n_unsorted_face_ele)
26020
26021 } // if (iproc != my_rank)
26022
26023 } // for (iproc < nproc)
26024 }
26025
26026 //======================================================================
26027 /// Get the nodes on the shared boundary (b), these are stored
26028 /// in the segment they belong
26029 //======================================================================
26030 template<class ELEMENT>
26034 {
26035 // Clear the data structure were to return the nodes
26036 tmp_segment_nodes.clear();
26037
26038 // Get the face elements that created the shared boundary from the
26039 // bulk shared boundary elements
26040
26041#ifdef PARANOID
26042 // The temporary storage for the halo face elements
26044#endif
26045 // The temporary storage for the nonhalo face elements
26047
26048 // Get the number of shared boundary elements associated with the
26049 // current shared boundary
26050 const unsigned nshared_bound_ele =
26051 this->nshared_boundary_element(shd_bnd_id);
26052
26053 // Loop over the elements in the shared boundary to create the face
26054 // elements
26055 for (unsigned e = 0; e < nshared_bound_ele; e++)
26056 {
26057 // Get the shared boundary element
26059 this->shared_boundary_element_pt(shd_bnd_id, e);
26060
26061 // Get the face index
26062 int face_index = this->face_index_at_shared_boundary(shd_bnd_id, e);
26063
26064 // Before adding the new element we need to ensure that the edge
26065 // that this element represents has not been already added
26067 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
26068
26069 // Nonhalo element
26070 if (!bulk_ele_pt->is_halo())
26071 {
26072 // Add nonhalo shared face element to the container
26074 }
26075#ifdef PARANOID
26076 else // halo element
26077 {
26078 // Add halo shared face element to the container
26080 }
26081#endif
26082
26083 } // for (e < nshared_bound_ele)
26084
26085 // Mark the face elements already used
26086 std::map<FiniteElement*, bool> shared_face_done;
26087
26088 // Get the number of nonhalo face elements
26090
26091 // If we are in PARANOID mode check that there is one halo element
26092 // for each nonhalo element
26093#ifdef PARANOID
26094 // Get the number of halo face elements
26096
26097 // The number of nonhalo shared face boundary elements must be the
26098 // half of the total number of shared boundary elements
26100 {
26101 std::ostringstream error_message;
26102 error_message
26103 << "The number of shared boundary elements (" << nshared_bound_ele
26104 << ") is not the double\nof the number of unsorted nonhalo shared "
26105 << "face boundary elements (" << nnonhalo_face_shared_ele
26106 << ")\n for the current boundary (" << shd_bnd_id << ")\n\n";
26107 throw OomphLibError(
26108 error_message.str(),
26109 "RefineableTriangleMesh::get_shared_boundary_segment_nodes_helper()",
26111 }
26112
26113 // The number of halo shared face boundary elements must be the
26114 // half of the total number of shared boundary elements
26116 {
26117 std::ostringstream error_message;
26118 error_message
26119 << "The number of shared boundary elements (" << nshared_bound_ele
26120 << ") is not the double\nof the number of unsorted halo shared "
26121 << "face boundary elements (" << nhalo_face_shared_ele
26122 << ")\n for the current boundary (" << shd_bnd_id << ")\n\n";
26123 throw OomphLibError(
26124 error_message.str(),
26125 "RefineableTriangleMesh::get_shared_boundary_segment_nodes_helper()",
26127 }
26128
26129 // ------------------------------------------------------------------
26130 // Loop over the nonhalo face elements and look for the halo face
26131 // element at the other side of the shared boundary
26132 for (unsigned inh = 0; inh < nnonhalo_face_shared_ele; inh++)
26133 {
26134 // Get the inh-th face element
26136
26137 // Get the number of nodes on the face element
26138 const unsigned nnodes_nh = nonhalo_face_ele_pt->nnode();
26139 // Get the first and last node on the element
26142
26143 // Now find the (halo) face element at the other side of the
26144 // shared boundary
26145 for (unsigned ih = 0; ih < nhalo_face_shared_ele; ih++)
26146 {
26147 // Get the ih-th face element
26149
26150 // Check that the face element has not been done
26152 {
26153 // Get the number of nodes on the face element
26154 const unsigned nnodes_h = halo_face_ele_pt->nnode();
26155 // Get the first and last node on the element
26158
26159 // If the nodes are the same then we have found the (halo)
26160 // face element at the other side of the shared boundary
26163 {
26164 // Mark the face elements as done
26167
26168 // Break the loop for (ih < nhalo_face_shared_ele)
26169 break;
26170 } // if (nh_first_node_pt == h_first_node_pt &&
26171 // nh_last_node_pt == h_last_node_pt)
26172 else if (nh_first_node_pt == h_last_node_pt &&
26174 {
26175 // Mark the face elements as done
26178
26179 // Break the loop for (ih < nhalo_face_shared_ele)
26180 break;
26181 } // else if (nh_first_node_pt == h_last_node_pt &&
26182 // nh_last_node_pt == h_first_node_pt)
26183
26184 } // if (face_done[halo_face_ele_pt])
26185
26186 } // for (ih < nhalo_face_shared_ele)
26187
26188 } // for (inh < nnonhalo_face_shared_ele)
26189
26190 // The number of done shared face elements MUST be the same as the
26191 // sum of the nonhalo and halo shared boundary face elements
26194 {
26195 std::ostringstream error_message;
26196 error_message << "The number of DONE shared boundary face elements ("
26198 << ") is not the same\n as the sum of"
26199 << "the nonhalo face shared boundary elements ("
26201 << ")\nand the halo face shared "
26202 << "boundary elements (" << nhalo_face_shared_ele
26203 << ") for the\n/"
26204 << "current boundary (" << shd_bnd_id << ")\n\n";
26205 throw OomphLibError(
26206 error_message.str(),
26207 "RefineableTriangleMesh::get_shared_boundary_segment_nodes_helper()",
26209 }
26210#endif // #ifdef PARANOID
26211
26212 // -------------------------------------------------------------
26213 // Now sort the face elements
26214 // -------------------------------------------------------------
26215
26216 // We already have the shared face elements that make the shared
26217 // boundary now sort them to create a contiguous boundary
26218
26219 // Clear the already done face elements
26220 shared_face_done.clear();
26221
26222 unsigned nsorted_face_ele = 0;
26223
26224 // Storing for the sorting nodes extracted from the face elements
26225 std::list<Node*> sorted_nodes;
26226
26227 // Get the root face element
26230
26231 // Mark face as done
26233
26234 // The initial and final node on the list
26235 const unsigned nnodes_root = root_face_ele_pt->nnode();
26238
26239 // Push back on the list the new nodes
26240 sorted_nodes.push_back(first_node_pt);
26241 sorted_nodes.push_back(last_node_pt);
26242
26243 // Sort the face elements
26245 {
26246 // Flag to indicate when a node was added
26247 bool node_added = false;
26248
26249 // Start from the next edge since we have already added the
26250 // previous one as the initial face element
26251 for (unsigned iface = 1; iface < nnonhalo_face_shared_ele; iface++)
26252 {
26255
26256 // If face has not been sorted
26258 {
26259 // Get the number of nodes for the current face element
26260 const unsigned tmp_nnodes = tmp_shared_face_ele_pt->nnode();
26261
26262 // Get each individual node
26265
26267 {
26268 // Push front the new node
26269 sorted_nodes.push_front(right_node_pt);
26271 node_added = true;
26272 }
26273 else if (left_node_pt == last_node_pt)
26274 {
26275 // Push back the new node
26276 sorted_nodes.push_back(right_node_pt);
26278 node_added = true;
26279 }
26280 else if (right_node_pt == first_node_pt)
26281 {
26282 // Push front the new node
26283 sorted_nodes.push_front(left_node_pt);
26285 node_added = true;
26286 }
26287 else if (right_node_pt == last_node_pt)
26288 {
26289 // Push back the new node
26290 sorted_nodes.push_back(left_node_pt);
26292 node_added = true;
26293 }
26294
26295 if (node_added)
26296 {
26297 // Mark as done only if one of its nodes has been added to
26298 // the list
26301
26302 // Break the for
26303 break;
26304 }
26305
26306 } // if (!shared_face_done[tmp_shared_face_ele_pt])
26307
26308 } // for (iface < nnonhalo_face_shared_ele)
26309
26310 } // while (nsorted_face_ele < nnonhalo_face_shared_ele))
26311
26312 // Here we can safely delete the face elements, they are no longer
26313 // required
26314
26315 // First the nonhalo face elements
26316 for (unsigned inh = 0; inh < nnonhalo_face_shared_ele; inh++)
26317 {
26320 } // for (inh < nnonhalo_face_shared_ele)
26321
26322#ifdef PARANOID
26323 // ... then the halo face elements
26324 for (unsigned ih = 0; ih < nhalo_face_shared_ele; ih++)
26325 {
26328 } // for (inh < nhalo_face_shared_ele)
26329#endif
26330
26331 // ------------------------------------------------
26332 // Now copy the nodes to the output container
26333 // ------------------------------------------------
26334 // Get the number of nodes in the container
26335 const unsigned n_nodes = sorted_nodes.size();
26336
26337 // First resize the container
26338 tmp_segment_nodes.resize(1);
26339 tmp_segment_nodes[0].resize(n_nodes);
26340
26341 // Counter
26342 unsigned counter = 0;
26343
26344 // Loop over the list of nodes and copy them in the output container
26345 for (std::list<Node*>::iterator it = sorted_nodes.begin();
26346 it != sorted_nodes.end();
26347 it++)
26348 {
26349 tmp_segment_nodes[0][counter] = (*it);
26350 counter++;
26351 } // Loop over sorted nodes
26352 }
26353
26354 //=====start of get_required_elemental_information_load_balance_helper====
26355 /// Helper function to get the required elemental information from
26356 /// the element that will be sent to iproc processor.
26357 /// This info. involves the association of the element to a boundary or
26358 /// region.
26359 //========================================================================
26360 template<class ELEMENT>
26363 unsigned& iproc,
26366 {
26367 // Check if the element is associated with the original boundaries
26368 const unsigned nbound = this->initial_shared_boundary_id();
26369
26370 // Get the number of processors
26371 const unsigned nproc = this->communicator_pt()->nproc();
26372
26373 // ------------------------------------------------------------------
26374 // Stores the information regarding the boundaries associated to the
26375 // element (it that is the case)
26378
26379 unsigned counter_face_indexes = 0;
26380
26381 for (unsigned b = 0; b < nbound; b++)
26382 {
26383 // Get the number of elements associated to boundary i
26384 const unsigned nboundary_ele = nboundary_element(b);
26385 for (unsigned e = 0; e < nboundary_ele; e++)
26386 {
26387 if (ele_pt == this->boundary_element_pt(b, e))
26388 {
26389 // Keep track of the boundaries associated to the element
26390 associated_boundaries.push_back(b);
26391 // Get the face index
26392 face_index_on_boundary.push_back(face_index_at_boundary(b, e));
26394#ifdef PARANOID
26395 if (counter_face_indexes > 2)
26396 {
26397 std::stringstream error_message;
26398 error_message
26399 << "A triangular element can not have more than two of its faces "
26400 << "on a boundary!!!\n\n";
26401 throw OomphLibError(error_message.str(),
26402 "RefineableTriangleMesh::get_required_"
26403 "elemental_information_helper()",
26405 }
26406#else
26407 // Already found 2 face indexes on the same boundary?
26408 if (counter_face_indexes == 2)
26409 {
26410 break;
26411 }
26412#endif // #ifdef PARANOID
26413
26414 } // if (ele_pt == this->boundary_element_pt(b,e))
26415
26416 } // (e < nboundary_ele)
26417
26418 } // (b < nbound)
26419
26420 // If the element is associated to any boundary then package all the
26421 // relevant info
26423 if (nassociated_boundaries > 0)
26424 {
26425 Flat_packed_unsigneds.push_back(1);
26426#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26427 Flat_packed_unsigneds_string.push_back(
26428 "The element is a boundary element");
26429#endif
26430 Flat_packed_unsigneds.push_back(nassociated_boundaries);
26431#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26432 std::stringstream junk;
26433 junk << "The elements is associated to " << nassociated_boundaries
26434 << " boundaries";
26435 Flat_packed_unsigneds_string.push_back(junk.str());
26436#endif
26437
26438 // Package the ids of the associated boundaries and the
26439 // corresponding face index for each boundary (if the element is a
26440 // corner element, it will have two faces associated to the
26441 // boundary)
26442 for (unsigned i = 0; i < nassociated_boundaries; i++)
26443 {
26444 unsigned b = associated_boundaries[i];
26445 Flat_packed_unsigneds.push_back(b);
26446#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26447 std::stringstream junk;
26448 junk << "Element associated to boundary " << b << " of "
26449 << nassociated_boundaries << " total associated boundaries";
26450 Flat_packed_unsigneds_string.push_back(junk.str());
26451#endif
26452 unsigned f = face_index_on_boundary[i];
26453 Flat_packed_unsigneds.push_back(f);
26454#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26455 std::stringstream junk2;
26456 junk2 << "Face index " << f << " for associated boundary " << b;
26457 Flat_packed_unsigneds_string.push_back(junk2.str());
26458#endif
26459 }
26460
26461 // If the element is associated to any boundary then we should
26462 // check if the mesh has regions, if that is the case then we need
26463 // to check to which region the boundary element does belong
26464
26465 // If the mesh has regions we should look for the element
26466 // associated to a boundary and a specified region
26469
26470 // Now check for the case when we have regions in the mesh
26471 const unsigned n_regions = this->nregion();
26472 if (n_regions > 1)
26473 {
26474 // Used to count the number of faces associated with
26475 // boundary-regions
26477 // Loop over the boundaries
26478 for (unsigned b = 0; b < nbound; b++)
26479 {
26480 // Go through each region by getting the region id
26481 for (unsigned i_reg = 0; i_reg < n_regions; i_reg++)
26482 {
26483 // Get thre region id associated with the (i_reg)-th region
26484 const unsigned region_id =
26485 static_cast<unsigned>(this->Region_attribute[i_reg]);
26486
26487 // Loop over all elements associated with the current boundary
26488 // and the i_reg-th region and check if the element is part of
26489 // any region
26490 const unsigned nele_in_region =
26491 this->nboundary_element_in_region(b, region_id);
26492 for (unsigned ee = 0; ee < nele_in_region; ee++)
26493 {
26494 // Check if the boundary-region element is the same as the
26495 // element
26496 if (ele_pt ==
26497 this->boundary_element_in_region_pt(b, region_id, ee))
26498 {
26499 // Storage for the boundary and region associated to the
26500 // element
26502
26503 // Keep track of the boundaries associated to the element
26504 bound_and_region[0] = b;
26505 // Keep track of the regions associated to the element
26507 // Add the boundaries and regions in the storage to be
26508 // sent to other processors
26510 // Get the face index and keep track of it
26512 this->face_index_at_boundary_in_region(b, region_id, ee));
26513
26514 // Increase the number of faces of the element associated
26515 // to boundary-regions
26517
26518#ifdef PARANOID
26520 {
26521 std::stringstream error_message;
26522 error_message << "A triangular element can not have more "
26523 "than two of its\n"
26524 << "faces on a boundary!!!\n\n";
26525 throw OomphLibError(error_message.str(),
26526 "RefineableTriangleMesh::get_required_"
26527 "elemental_information_helper()",
26529 } // if (counter_face_indexes_in_regions > 2)
26530#endif
26531
26532 } // The element is a boundary-region element
26533
26534 } // for (ee < nele_in_region)
26535
26536 } // for (i_reg < n_regions)
26537
26538 } // for (b < nbound)
26539
26540 } // if (n_regions > 1)
26541
26542 // Now package the info. to be sent to other processors
26543 const unsigned nassociated_boundaries_and_regions =
26546 {
26547 Flat_packed_unsigneds.push_back(1);
26548#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26549 Flat_packed_unsigneds_string.push_back(
26550 "The element is associated to boundaries and regions");
26551#endif
26552
26553 Flat_packed_unsigneds.push_back(nassociated_boundaries_and_regions);
26554#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26555 std::stringstream junk;
26556 junk << "The element is associated to "
26557 << nassociated_boundaries_and_regions << " boundaries-regions";
26558 Flat_packed_unsigneds_string.push_back(junk.str());
26559#endif
26560
26561 // Package the ids of the associated boundaries, regions and the
26562 // corresponding face index for each boundary-region (if the
26563 // element is a corner element, it will have two faces
26564 // associated to the boundary-region)
26565 for (unsigned i = 0; i < nassociated_boundaries_and_regions; i++)
26566 {
26567 const unsigned b = associated_boundaries_and_regions[i][0];
26568 Flat_packed_unsigneds.push_back(b);
26569#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26570 std::stringstream junk;
26571 junk << "Element associated to boundary " << b << " of "
26573 << " total associated boundaries-regions";
26574 Flat_packed_unsigneds_string.push_back(junk.str());
26575#endif
26576
26577 const unsigned r = associated_boundaries_and_regions[i][1];
26578 Flat_packed_unsigneds.push_back(r);
26579#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26580 std::stringstream junk2;
26581 junk2 << "Element associated to region " << r << " of "
26583 << " total associated boundaries-regions";
26584 Flat_packed_unsigneds_string.push_back(junk2.str());
26585#endif
26586
26587 const unsigned f = face_index_on_boundary_and_region[i];
26588 Flat_packed_unsigneds.push_back(f);
26589#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26590 std::stringstream junk3;
26591 junk3 << "Face index " << f << " for associated boundary-region ("
26592 << b << "-" << r << ")";
26593 Flat_packed_unsigneds_string.push_back(junk3.str());
26594#endif
26595 } // for (i < nassociated_boundaries_and_regions)
26596 } // if (nassociated_boundaries_and_regions > 0)
26597 else
26598 {
26599 Flat_packed_unsigneds.push_back(0);
26600#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26601 Flat_packed_unsigneds_string.push_back(
26602 "The element is NOT associated to boundaries and regions");
26603#endif
26604 } // else if (nassociated_boundaries_and_regions > 0)
26605 }
26606 else
26607 {
26608 Flat_packed_unsigneds.push_back(0);
26609#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26610 Flat_packed_unsigneds_string.push_back(
26611 "The element is not associated to any original boundary");
26612#endif
26613 }
26614
26615 // ------------------------------------------------------------
26616 // Now review if the element is associated to a shared boundary
26617
26618 // Store the shared boundaries, and therefore the face indexes
26619 // associated to the element
26622
26623 // Get the shared boundaries in this processor
26625 this->shared_boundaries_in_this_processor(my_rank_shared_boundaries_ids);
26626
26627 // Get the number of shared boundaries
26629 // Loop over the shared boundaries
26630 for (unsigned i = 0; i < nmy_rank_shd_bnd; i++)
26631 {
26632 // Get the boundary id
26633 const unsigned sb = my_rank_shared_boundaries_ids[i];
26634
26635 // Get the number of elements associated to shared boundary sb
26636 const unsigned nboundary_ele = this->nshared_boundary_element(sb);
26637 for (unsigned e = 0; e < nboundary_ele; e++)
26638 {
26639 if (ele_pt == this->shared_boundary_element_pt(sb, e))
26640 {
26641 // Keep track of the boundaries associated to the element
26643 // Get the face index
26645 this->face_index_at_shared_boundary(sb, e));
26646 }
26647 } // (e < nboundary_ele)
26648 } // (i < nmy_rank_shd_bnd)
26649
26650 // If the element is associated to a shared boundary then package
26651 // all the relevant info
26652 const unsigned nassociated_shared_boundaries =
26655 {
26656 Flat_packed_unsigneds.push_back(3);
26657#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26658 Flat_packed_unsigneds_string.push_back(
26659 "The element is a shared boundary element");
26660#endif
26661 Flat_packed_unsigneds.push_back(nassociated_shared_boundaries);
26662#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26663 std::stringstream junk;
26664 junk << "The elements is associated to " << nassociated_shared_boundaries
26665 << "shared boundaries";
26666 Flat_packed_unsigneds_string.push_back(junk.str());
26667#endif
26668
26669 // Package the ids of the associated boundaries
26670 for (unsigned i = 0; i < nassociated_shared_boundaries; i++)
26671 {
26672 const unsigned b = associated_shared_boundaries[i];
26673 Flat_packed_unsigneds.push_back(b);
26674#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26675 std::stringstream junk;
26676 junk << "Element associated to shared boundary " << b << " of "
26677 << nassociated_shared_boundaries << " total associated boundaries";
26678 Flat_packed_unsigneds_string.push_back(junk.str());
26679#endif
26680
26681 const unsigned f = face_index_on_shared_boundary[i];
26682 Flat_packed_unsigneds.push_back(f);
26683#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26684 std::stringstream junk2;
26685 junk2 << "Face index " << f << " for associated shared boundary " << b;
26686 Flat_packed_unsigneds_string.push_back(junk2.str());
26687#endif
26688 }
26689 }
26690 else
26691 {
26692 Flat_packed_unsigneds.push_back(0);
26693#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26694 Flat_packed_unsigneds_string.push_back(
26695 "The element is not associated to any shared boundary");
26696#endif
26697 }
26698
26699 // Now check if the element is haloed with any processor
26700
26701 // Store the index of the haloed element with the jproc
26703
26704 // Loop over the processors
26705 for (unsigned jproc = 0; jproc < nproc; jproc++)
26706 {
26707 // Get the number of haloed elements with jproc
26708 const unsigned n_haloed_jproc = f_haloed_ele_pt[jproc].size();
26709 // Loop over the haloed elements with jproc
26710 for (unsigned ihd = 0; ihd < n_haloed_jproc; ihd++)
26711 {
26712 // Is a haloed element?
26714 {
26715 // Store the haloed index with the jproc processor
26716 index_haloed[jproc].push_back(ihd);
26717 // Break the searching with the jproc processor
26718 break;
26719 } // if (ele_pt == f_haloed_ele_pt[jproc][ihd])
26720
26721 } // for (ihd < n_haloed_jproc)
26722
26723 } // for (jproc < nproc)
26724
26725 // Send the haloed info.
26726 // Loop over the processors
26727 for (unsigned jproc = 0; jproc < nproc; jproc++)
26728 {
26729 // Is the element haloed with the jproc processor
26730 const unsigned n_index_haloed_jproc = index_haloed[jproc].size();
26731 Flat_packed_unsigneds.push_back(n_index_haloed_jproc);
26732#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26733 Flat_packed_unsigneds_string.push_back(
26734 "The number of haloed indexes the element is with processor jproc");
26735#endif
26736 for (unsigned ihd = 0; ihd < n_index_haloed_jproc; ihd++)
26737 {
26738 Flat_packed_unsigneds.push_back(index_haloed[jproc][ihd]);
26739#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26740 Flat_packed_unsigneds_string.push_back(
26741 "The haloed index of the element with jproc");
26742#endif
26743 } // for (ihd < n_index_haloed_jproc)
26744
26745 } // for (jproc < nproc)
26746 }
26747
26748 //======================================================================
26749 /// Helper function to add nodes on a new domain as a result of
26750 /// load balance
26751 //======================================================================
26752 template<class ELEMENT>
26754 unsigned& iproc,
26757 Node* nod_pt)
26758 {
26759 // Attempt to add this node to the new domain
26761 const unsigned new_added_node_index =
26762 this->try_to_add_node_pt_load_balance(new_nodes_on_domain, nod_pt);
26763
26764 // If it was added then the new index should match the size of the storage
26766 {
26767 Flat_packed_unsigneds.push_back(1);
26768
26769#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26770 std::stringstream junk;
26771 junk << "Node needs to be constructed [size="
26772 << Flat_packed_unsigneds.size() << "]; last entry: "
26773 << Flat_packed_unsigneds[Flat_packed_unsigneds.size() - 1];
26774 Flat_packed_unsigneds_string.push_back(junk.str());
26775#endif
26776
26777 // This helper function gets all the required information for the
26778 // specified node and stores it into MPI-sendable information
26779 // so that a new copy can be made on the receiving process
26780 get_required_nodal_information_load_balance_helper(
26782 }
26783 else // It was already added
26784 {
26785 Flat_packed_unsigneds.push_back(0);
26786#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26787 std::stringstream junk;
26788 junk << "Node was already added [size=" << Flat_packed_unsigneds.size()
26789 << "]; last entry: "
26790 << Flat_packed_unsigneds[Flat_packed_unsigneds.size() - 1];
26791
26792 Flat_packed_unsigneds_string.push_back(junk.str());
26793#endif
26794
26795 // This node has been already added, so tell the other process
26796 // its index in the equivalent storage
26797 Flat_packed_unsigneds.push_back(new_added_node_index);
26798#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26799 Flat_packed_unsigneds_string.push_back("new added node index");
26800#endif
26801 }
26802 }
26803
26804 //======start of get_required_nodal_information_load_balance_helper=======
26805 /// Helper function to get the required nodal information from an
26806 /// haloed node so that a fully-functional halo node (and therefore element)
26807 /// can be created on the receiving process
26808 //========================================================================
26809 template<class ELEMENT>
26813 unsigned& iproc,
26814 Node* nod_pt)
26815 {
26816 unsigned my_rank = this->communicator_pt()->my_rank();
26817 const unsigned nproc = this->communicator_pt()->nproc();
26818
26819 // Tell the halo copy of this node how many values there are
26820 // [NB this may be different for nodes within the same element, e.g.
26821 // when using Lagrange multipliers]
26822 unsigned n_val = nod_pt->nvalue();
26823 Flat_packed_unsigneds.push_back(n_val);
26824#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26825 Flat_packed_unsigneds_string.push_back("Number of values");
26826#endif
26827
26828 unsigned n_dim = nod_pt->ndim();
26829
26830 // Default number of previous values to 1
26831 unsigned n_prev = 1;
26832 if (this->Time_stepper_pt != 0)
26833 {
26834 // Add number of history values to n_prev
26835 n_prev = this->Time_stepper_pt->ntstorage();
26836 }
26837
26838 // -----------------------------------------------------
26839 // Is the node on an original boundary?
26840 // Store the original boundaries where the node may be
26842 // Loop over the original boundaries of the mesh and check if live
26843 // on one of them
26844 const unsigned n_bnd = this->initial_shared_boundary_id();
26845 for (unsigned bb = 0; bb < n_bnd; bb++)
26846 {
26847 // Which boundaries (could be more than one) is it on?
26848 if (nod_pt->is_on_boundary(bb))
26849 {
26850 original_boundaries.push_back(bb);
26851 }
26852 }
26853
26855 // Is the node on any original boundary?
26856 if (n_original_boundaries > 0)
26857 {
26858 // Indicate that the node is on an original boundary
26859 Flat_packed_unsigneds.push_back(2);
26860#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26861 Flat_packed_unsigneds_string.push_back(
26862 "Node is on the original boundaries");
26863#endif
26864
26865 Flat_packed_unsigneds.push_back(n_original_boundaries);
26866#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26867 std::stringstream junk;
26868 junk << "Node is on " << n_original_boundaries << " original boundaries";
26869 Flat_packed_unsigneds_string.push_back(junk.str());
26870#endif
26871
26872 // Loop over the original boundaries the node is on
26873 for (unsigned i = 0; i < n_original_boundaries; i++)
26874 {
26875 Flat_packed_unsigneds.push_back(original_boundaries[i]);
26876#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26877 std::stringstream junk;
26878 junk << "Node is on boundary " << original_boundaries[i] << " of "
26879 << nb;
26880 Flat_packed_unsigneds_string.push_back(junk.str());
26881#endif
26882 // Get the boundary coordinate of the node
26884 nod_pt->get_coordinates_on_boundary(original_boundaries[i], zeta);
26885 Flat_packed_doubles.push_back(zeta[0]);
26886 }
26887 }
26888 else
26889 {
26890 // Indicate that the node is NOT on an original boundary
26891 Flat_packed_unsigneds.push_back(0);
26892#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26893 Flat_packed_unsigneds_string.push_back(
26894 "Node is on any original boundary");
26895#endif
26896 }
26897
26898 // -------------------------------------------------------
26899 // Is the node on shared boundaries?
26900 bool node_on_shared_boundary = false;
26901 // Loop over the shared boundaries with the iproc processors and
26902 // check if live on one of them
26903 const unsigned n_shd_bnd = this->nshared_boundaries(my_rank, iproc);
26904 for (unsigned bb = 0; bb < n_shd_bnd; bb++)
26905 {
26906 // Get the boundary id
26907 unsigned i_bnd = this->shared_boundaries_ids(my_rank, iproc, bb);
26908 // Which boundaries (could be more than one) is it on?
26909 if (this->is_node_on_shared_boundary(i_bnd, nod_pt))
26910 {
26912 break;
26913 }
26914 }
26915
26916 // If the node live on any of the shared boundaries with the iproc
26917 // processor then just get the node number according to the
26918 // sorted_shared_boundary_node_pt() scheme and send it accross
26920 {
26921 Flat_packed_unsigneds.push_back(1);
26922#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26923 Flat_packed_unsigneds_string.push_back("Node is on shared boundary");
26924#endif
26925
26926 // Store the shared boundaries where the node is on
26928 // Loop over the shared boundaries with the iproc processor
26929 for (unsigned bb = 0; bb < n_shd_bnd; bb++)
26930 {
26931 // Get the boundary id
26932 const unsigned i_bnd = this->shared_boundaries_ids(my_rank, iproc, bb);
26933 // Which boundaries (could be more than one) is it on?
26934 if (this->is_node_on_shared_boundary(i_bnd, nod_pt))
26935 {
26936 shd_boundaries.push_back(i_bnd);
26937 }
26938 }
26939
26940 // Get the number of shared boundaries the node is on
26941 const unsigned n_shd_bnd_is_on = shd_boundaries.size();
26942 // Send the number of shared boundaries the node is on
26943 Flat_packed_unsigneds.push_back(n_shd_bnd_is_on);
26944#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26945 std::stringstream junk;
26946 junk << "Node is on " << n_shd_bnd_is_on << " shared boundaries";
26947 Flat_packed_unsigneds_string.push_back(junk.str());
26948#endif
26949
26950 // Loop over the shared boundaries to send their ids
26951 for (unsigned i = 0; i < n_shd_bnd_is_on; i++)
26952 {
26953 Flat_packed_unsigneds.push_back(shd_boundaries[i]);
26954#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26955 std::stringstream junk;
26956 junk << "Node is on boundary " << shd_boundaries[i] << " of " << nb;
26957 Flat_packed_unsigneds_string.push_back(junk.str());
26958#endif
26959 }
26960
26961 // Given that the node is on at least one boundary get the index
26962 // of the node in one of the boundaries and send this index
26963 unsigned shared_boundary_id = shd_boundaries[0];
26964 // Get the number of nodes on the given shared boundary
26965 const unsigned n_nodes_on_shared_boundary =
26966 nsorted_shared_boundary_node(shared_boundary_id);
26967 // Store the index of the node on the shared boundary
26969#ifdef PARANOID
26970 // Flag to know if the node has been found
26972#endif
26973 // Loop over the nodes on the shared boundary to find the node
26974 for (unsigned i = 0; i < n_nodes_on_shared_boundary; i++)
26975 {
26976 // Get the i-th node on the shared boundary
26977 Node* shared_node_pt =
26978 sorted_shared_boundary_node_pt(shared_boundary_id, i);
26979 // Is the node we are looking for
26980 if (shared_node_pt == nod_pt)
26981 {
26982 // Store the index
26984#ifdef PARANOID
26985 // Mark as found
26987#endif
26988 break; // break
26989 }
26990
26991 } // for (i < nnodes_on_shared_boundary)
26992
26993#ifdef PARANOID
26995 {
26996 std::ostringstream error_message;
26997 error_message << "The index of the node on boundary ("
26998 << shared_boundary_id << ") was not found.\n"
26999 << "The node coordinates are (" << nod_pt->x(0) << ","
27000 << nod_pt->x(1) << ").\n";
27001 throw OomphLibError(
27002 error_message.str(),
27003 "RefineableTriangleMesh::get_required_nodal_information_helper()",
27005 }
27006#endif
27007 // Send the index of the node on the shared boundary
27008 Flat_packed_unsigneds.push_back(index_node_on_shared_boundary);
27009#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27010 std::stringstream junk2;
27011 junk2 << "Node index on boundary " << boundaries[0] << " is "
27013 Flat_packed_unsigneds_string.push_back(junk2.str());
27014#endif
27015
27016 } // if (node_on_shared_boundary)
27017 else
27018 {
27019 // The node is not on a shared boundary
27020 Flat_packed_unsigneds.push_back(0);
27021#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27022 Flat_packed_unsigneds_string.push_back(
27023 "Node is not on a shared boundary");
27024#endif
27025 }
27026
27027 // ----------------------------------------------------------------
27028 // Is the node on any shared boundary where the receiver processor
27029 // is not involved?
27030
27031 // Now check if the node is on a shared boundary created by the
27032 // current processor (my_rank) and other processor different that
27033 // the iproc processor. This info. will help to complete the sending
27034 // of halo(ed) information between processors
27035
27036 // Flag to know if the node is on a shared boundary with other
27037 // processor
27039 // Count the number of other shared boundaries it could be on
27041
27042 // Loop over the shared boundaries of the sent processor (my_rank)
27043 // and other processors (jproc)
27044 for (unsigned jproc = 0; jproc < nproc; jproc++)
27045 {
27046 // Do not search with the iproc processor, that was done before
27047 // above
27048 if (jproc != iproc)
27049 {
27050 // Get the number of shared boundaries with the jproc processor
27051 const unsigned n_jshd_bnd = this->nshared_boundaries(my_rank, jproc);
27052 // Loop over the shared boundaries
27053 for (unsigned bb = 0; bb < n_jshd_bnd; bb++)
27054 {
27055 // Get the boundary id
27056 const unsigned j_shd_bnd =
27057 this->shared_boundaries_ids(my_rank, jproc, bb);
27058 // Is the node part of this boundary?
27059 if (this->is_node_on_shared_boundary(j_shd_bnd, nod_pt))
27060 {
27061 // DEBP("Sending to");
27062 // DEBP(iproc);
27063 // DEBP("Pair of procs where other shared");
27064 // DEBP(my_rank);
27065 // DEBP(jproc);
27066 // DEBP(i_bnd);
27068 // Increase the counter for the number of shared boundaries
27069 // with other processors the node is on
27071 } // if (this->is_node_on_shared_boundary(j_shd_bnd, nod_pt)
27072
27073 } // for (bb<n_jshd_bnd)
27074
27075 } // if (jproc != iproc)
27076
27077 } // for (jproc < nproc)
27078
27079 // If the node is on a shared boundary with another processor
27080 // (my_rank, jproc), then send the flag and look for the info.
27082 {
27083 Flat_packed_unsigneds.push_back(4);
27084#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27085 Flat_packed_unsigneds_string.push_back(
27086 "Node is on shared boundary no related with the received processor: 4");
27087#endif
27088
27089 // The number of packages of information that will be sent to the
27090 // "iproc" processor. This helps to know how many packages of data
27091 // read from the received processor
27092 Flat_packed_unsigneds.push_back(
27094#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27095 std::stringstream junk;
27096 junk << "Number of other shared boundaries that the node is on: "
27098 Flat_packed_unsigneds_string.push_back(junk.str());
27099#endif
27100
27101 // Counter to ensure that the correct number of data has been sent
27103 // Loop over the shared boundaries with other processors and get:
27104 // 1) The processors defining the shared boundary
27105 // 2) The shared boundary id
27106 // 3) The index of the node on the shared boundary
27111 // Loop over the processors again
27112 for (unsigned jproc = 0; jproc < nproc; jproc++)
27113 {
27114 // Do not search with the iproc processor, that was done before
27115 // above
27116 if (jproc != iproc)
27117 {
27118 // Get the number of shared boundaries with the jproc
27119 // processor
27120 const unsigned n_jshd_bnd = this->nshared_boundaries(my_rank, jproc);
27121 for (unsigned bb = 0; bb < n_jshd_bnd; bb++)
27122 {
27123 // Get the boundary id
27124 const unsigned j_shd_bnd =
27125 this->shared_boundaries_ids(my_rank, jproc, bb);
27126 // Is the node part of this boundary?
27127 if (this->is_node_on_shared_boundary(j_shd_bnd, nod_pt))
27128 {
27129 // Include the first processor
27130 other_processor_1.push_back(my_rank);
27131 // Include the second processor
27132 other_processor_2.push_back(jproc);
27133 // Include the shared boundary id
27134 shd_bnd_ids.push_back(j_shd_bnd);
27135 // Increase the counter for found shared boundaries with
27136 // other processors
27138 }
27139
27140 } // for (bb < nshared_bnd)
27141
27142 } // if (jproc != iproc)
27143
27144 } // for (jproc < nproc)
27145
27146 // Get the indexes of the node on all the shared boundaries where
27147 // it was found
27148 const unsigned n_other_processors = other_processor_1.size();
27149 // Loop over the processors where the node was found
27150 for (unsigned i = 0; i < n_other_processors; i++)
27151 {
27152 // Get the shared boundary id
27153 unsigned shd_bnd_id = shd_bnd_ids[i];
27154 // Get the number of nodes on that shared boundary
27155 const unsigned n_nodes_on_shd_bnd =
27156 nsorted_shared_boundary_node(shd_bnd_id);
27157
27158#ifdef PARANOID
27160#endif
27161 for (unsigned i = 0; i < n_nodes_on_shd_bnd; i++)
27162 {
27163 // Get the i-th shared boundary node
27164 Node* shared_node_pt = sorted_shared_boundary_node_pt(shd_bnd_id, i);
27165 // Is the same node?
27166 if (shared_node_pt == nod_pt)
27167 {
27168 // DEBP(i_node);
27169 // DEBP(nod_pt->x(0));
27170 // DEBP(nod_pt->x(1));
27171 // Include the index of the node
27172 indexes.push_back(i);
27173#ifdef PARANOID
27174 // Mark as found the node
27176#endif
27177 break;
27178 } // if (shared_node_pt == nod_pt)
27179
27180 } // for (i < n_nodes_on_shd_bnd)
27181
27182#ifdef PARANOID
27184 {
27185 std::ostringstream error_message;
27186 error_message << "The index of the node on boundary (" << shd_bnd_id
27187 << "), shared by other processors\nwas not found.\n"
27188 << "The node coordinates are (" << nod_pt->x(0) << ","
27189 << nod_pt->x(1) << ").\n";
27190 throw OomphLibError(
27191 error_message.str(),
27192 "RefineableTriangleMesh::get_required_nodal_information_helper()",
27194 }
27195#endif
27196 } // for (i < n_other_processors)
27197
27198 // Now send the info. but first check that the number of found
27199 // nodes be the same that the previously found shared boundaries
27200 // with the node
27201#ifdef PARANOID
27204 {
27205 std::ostringstream error_message;
27206 error_message << "The number of shared boundaries where the node is on "
27207 << "is different:\n"
27208 << "nshared_boundaries_with_other_processors_have_node: ("
27210 << ")\n"
27211 << "counter_shd_bnd_with_other_procs_have_node: ("
27213 throw OomphLibError(
27214 error_message.str(),
27215 "RefineableTriangleMesh::get_required_nodal_information_helper()",
27217 } // if (counter_shd_bnd_with_other_procs_have_node !=
27218 // nshared_boundaries_with_other_processors_have_node)
27219#endif
27220
27221 // Loop over the info. to send it
27222 for (unsigned i = 0; i < n_other_processors; i++)
27223 {
27224 Flat_packed_unsigneds.push_back(other_processor_1[i]);
27225#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27226 std::stringstream junk1;
27227 junk1 << "Processor where the other shared boundary "
27228 << "has the node: " << other_processor_1[i];
27229 Flat_packed_unsigneds_string.push_back(junk1.str());
27230#endif
27231
27232 Flat_packed_unsigneds.push_back(other_processor_2[i]);
27233#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27234 std::stringstream junk2;
27235 junk2 << "Processor where the other shared boundary "
27236 << "has the node: " << other_processor_2[i];
27237 Flat_packed_unsigneds_string.push_back(junk2.str());
27238#endif
27239
27240 Flat_packed_unsigneds.push_back(shd_bnd_ids[i]);
27241#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27242 std::stringstream junk3;
27243 junk3 << "Other shared boundary id where the node is on"
27244 << boundaries[i];
27245 Flat_packed_unsigneds_string.push_back(junk3.str());
27246#endif
27247
27248 Flat_packed_unsigneds.push_back(indexes[i]);
27249#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27250 std::stringstream junk4;
27251 junk4 << "Node index on other shared boundary " << boundaries[i]
27252 << " is " << indexes[i];
27253 Flat_packed_unsigneds_string.push_back(junk4.str());
27254#endif
27255
27256 } // for (i < n_other_processors)
27257
27258 } // if (node_on_shared_boundary_with_other_processors)
27259 else
27260 {
27261 Flat_packed_unsigneds.push_back(0);
27262#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27263 Flat_packed_unsigneds_string.push_back(
27264 "Node is on any shared boundary with other processors");
27265#endif
27266 } // else if (node_on_shared_boundary_with_other_processors)
27267
27268 // It may still be possible that the node be shared with the
27269 // processor that receives the info. but it is neither on shared
27270 // boundary with the receiver processor nor on a shared boundary
27271 // with others processors. Think in the next case:
27272
27273 // |-----|-----| - The elements in processor 3 need to be sent to
27274 // | 4 | 3 | processor 1, and that is all
27275 // |-----*-----| - When processor 1 receives the data from node (*)
27276 // | 1 | 2 | it just RE-CREATES it becasuse it is does not know
27277 // |-----|-----| that the node is also on the shared boundary that
27278 // processor 1 and 2 or processor 1 and 4 share.
27279
27280 // This problem become even worse if there would be more processors
27281 // between processor 3 and 2, or/and processor 3 and 4. Think in
27282 // triangles sharing the node (*)
27283
27284 // To solve this check if the node that we are trying to send is
27285 // part of the halo elements of the curreent processor (my_rank)
27286 // with any other processor (we need to check with all the
27287 // processors and not just with the processors to which we will send
27288 // to cover more cases)
27289
27290 // Store the halo element number with jproc where the node was found
27292 // Store the node number on the halo element where the node was found
27294
27295 // Loop over the processor
27296 for (unsigned jproc = 0; jproc < nproc; jproc++)
27297 {
27298 // Get the number of halo elements with the jproc processor
27299 const unsigned n_halo_jproc = f_halo_ele_pt[jproc].size();
27300 // Loop over the halo elements
27301 for (unsigned jh = 0; jh < n_halo_jproc; jh++)
27302 {
27304 // Get the number of nodes of the halo element
27305 const unsigned n_node = halo_ele_pt->nnode();
27306 // Loop over the nodes
27307 for (unsigned n = 0; n < n_node; n++)
27308 {
27309 // Is the node part of the ih-th halo element with jproc
27310 if (nod_pt == halo_ele_pt->node_pt(n))
27311 {
27312 halo_element_number[jproc].push_back(jh);
27314 // break with the nodes, no need to look for more nodes in
27315 // the element
27316 break;
27317 } // if (nod_pt == halo_ele_pt->node_pt(n))
27318
27319 } // for (n < n_node)
27320
27321 } // for (jh < n_halo_jproc)
27322
27323 } // for (jproc < nproc)
27324
27325 // Send the info. related with if the node is on halo elements with
27326 // any processor
27327
27328 // Loop over the processors
27329 for (unsigned jproc = 0; jproc < nproc; jproc++)
27330 {
27331 // Get the number of halo elements with jproc processor where the
27332 // node is
27333 const unsigned n_jproc_halo_ele_node_is_on =
27335 // Send the number of halo elements with jproc where the node is
27336 Flat_packed_unsigneds.push_back(n_jproc_halo_ele_node_is_on);
27337#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27338 std::stringstream junk5;
27339 junk5 << "Node is on " << n_jproc_halo_ele_node_is_on << " halo "
27340 << "elements with " << jproc << "-th processor";
27341 Flat_packed_unsigneds_string.push_back(junk5.str());
27342#endif
27343 // Send the halo elements indexes (which will be haloed elements
27344 // indexes in the receiver processor), and the indexes of the
27345 // nodes in each halo element
27346 for (unsigned i = 0; i < n_jproc_halo_ele_node_is_on; i++)
27347 {
27348 // The halo element index
27349 const unsigned halo_element_index = halo_element_number[jproc][i];
27350 Flat_packed_unsigneds.push_back(halo_element_index);
27351#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27352 std::stringstream junk6;
27353 junk6 << "Halo element index is (" << halo_element_index
27354 << ") with processor (" << jproc << ")";
27355 Flat_packed_unsigneds_string.push_back(junk6.str());
27356#endif
27357 // The node index on the halo element
27359 Flat_packed_unsigneds.push_back(node_index);
27360#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27361 std::stringstream junk7;
27362 junk7 << "The node index on the halo element index is (" << node_index;
27363 Flat_packed_unsigneds_string.push_back(junk7.str());
27364#endif
27365
27366 } // for (i < n_jproc_halo_ele_node_is_on)
27367
27368 } // for (jproc < nproc)
27369
27370 // Now check if it is required to send the info. of the node. If the
27371 // node is not on a shared boundary with the iproc processor then we
27372 // need to send the info.
27373
27374 // Flag to indicate if it is on a halo element with the iproc
27375 // processor. If this flag is true then there is no need to send the
27376 // info. to create the node, in the receiver processor the info is
27377 // copied from the indicated haloed element-node
27379 if (halo_element_number[iproc].size() > 0)
27380 {
27382 } // if (halo_element_number[iproc].size() > 0)
27383
27384 // if (!node_on_shared_boundary)
27386 {
27387 // Send all the info. to create it
27388
27389 // Is the Node algebraic? If so, send its ref values and
27390 // an indication of its geometric objects if they are stored
27391 // in the algebraic mesh
27392 AlgebraicNode* alg_nod_pt = dynamic_cast<AlgebraicNode*>(nod_pt);
27393 if (alg_nod_pt != 0)
27394 {
27395 // The external mesh should be algebraic
27396 AlgebraicMesh* alg_mesh_pt = dynamic_cast<AlgebraicMesh*>(this);
27397
27398 // Get default node update function ID
27399 unsigned update_id = alg_nod_pt->node_update_fct_id();
27400 Flat_packed_unsigneds.push_back(update_id);
27401#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27402 Flat_packed_unsigneds_string.push_back("Alg Node update id");
27403#endif
27404
27405 // Get reference values at default...
27406 unsigned n_ref_val = alg_nod_pt->nref_value();
27407 Flat_packed_unsigneds.push_back(n_ref_val);
27408#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27409 Flat_packed_unsigneds_string.push_back("Alg Node n ref values");
27410#endif
27411 for (unsigned i_ref_val = 0; i_ref_val < n_ref_val; i_ref_val++)
27412 {
27413 Flat_packed_doubles.push_back(alg_nod_pt->ref_value(i_ref_val));
27414 }
27415
27416 // Access geometric objects at default...
27417 unsigned n_geom_obj = alg_nod_pt->ngeom_object();
27418 Flat_packed_unsigneds.push_back(n_geom_obj);
27419#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27420 Flat_packed_unsigneds_string.push_back("Alg Node n geom objects");
27421#endif
27422 for (unsigned i_geom = 0; i_geom < n_geom_obj; i_geom++)
27423 {
27424 GeomObject* geom_obj_pt = alg_nod_pt->geom_object_pt(i_geom);
27425
27426 // Check this against the stored geometric objects in mesh
27427 unsigned n_geom_list = alg_mesh_pt->ngeom_object_list_pt();
27428
27429 // Default found index to zero
27430 unsigned found_geom_object = 0;
27431 for (unsigned i_list = 0; i_list < n_geom_list; i_list++)
27432 {
27433 if (geom_obj_pt == alg_mesh_pt->geom_object_list_pt(i_list))
27434 {
27436 }
27437 }
27438 Flat_packed_unsigneds.push_back(found_geom_object);
27439#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27440 Flat_packed_unsigneds_string.push_back("Found geom object");
27441#endif
27442 }
27443 } // (if alg_nod_pt!=0)
27444
27445 // Is it a SolidNode?
27446 SolidNode* solid_nod_pt = dynamic_cast<SolidNode*>(nod_pt);
27447 if (solid_nod_pt != 0)
27448 {
27449 unsigned n_solid_val = solid_nod_pt->variable_position_pt()->nvalue();
27450 for (unsigned i_val = 0; i_val < n_solid_val; i_val++)
27451 {
27452 for (unsigned t = 0; t < n_prev; t++)
27453 {
27454 Flat_packed_doubles.push_back(
27455 solid_nod_pt->variable_position_pt()->value(t, i_val));
27456 }
27457 }
27458
27460 solid_nod_pt->add_values_to_vector(values_solid_node);
27461 const unsigned nvalues_solid_node = values_solid_node.size();
27462 Flat_packed_unsigneds.push_back(nvalues_solid_node);
27463#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27464 std::stringstream junk;
27465 junk << "Number of values solid node: " << nvalues_solid_node;
27466 Flat_packed_unsigneds_string.push_back(junk.str());
27467#endif
27468 for (unsigned i = 0; i < nvalues_solid_node; i++)
27469 {
27470 Flat_packed_doubles.push_back(values_solid_node[i]);
27471 }
27472 }
27473
27474 // Finally copy info required for all node types
27475 for (unsigned i_val = 0; i_val < n_val; i_val++)
27476 {
27477 for (unsigned t = 0; t < n_prev; t++)
27478 {
27479 Flat_packed_doubles.push_back(nod_pt->value(t, i_val));
27480 }
27481 }
27482
27483 // Now do positions
27484 for (unsigned idim = 0; idim < n_dim; idim++)
27485 {
27486 for (unsigned t = 0; t < n_prev; t++)
27487 {
27488 Flat_packed_doubles.push_back(nod_pt->x(t, idim));
27489 // DEBP(nod_pt->x(t,idim));
27490 }
27491 }
27492
27493 } // if (!node_on_shared_boundary && !on_halo_element_with_iproc_processor)
27494 }
27495
27496 //======================================================================
27497 /// Helper function to create elements on the loop
27498 /// process based on the info received in
27499 /// send_and_received_elements_nodes_info
27500 //======================================================================
27501 template<class ELEMENT>
27503 unsigned& iproc,
27505 Vector<Vector<std::map<unsigned, FiniteElement*>>>&
27509 Vector<Vector<Vector<std::map<unsigned, Node*>>>>&
27512 std::map<Vector<unsigned>, unsigned>& node_name_to_global_index,
27514 {
27515#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27516 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
27517 << " Bool: New element needs to be constructed "
27518 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
27519 << std::endl;
27520#endif
27521
27522 if (Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++] == 1)
27523 {
27524 // Create a new element from the communicated values
27525 // and coords from the process that located zeta
27526 GeneralisedElement* new_el_pt = new ELEMENT;
27527
27528 // Add the new element to the mesh - Do not add the element yet
27529 // since no retained elements still need to be deleted
27530 // this->add_element_pt(new_el_pt);
27531
27532 // Cast to the FE pointer
27533 FiniteElement* f_el_pt = dynamic_cast<FiniteElement*>(new_el_pt);
27534
27535 // Add the element to the new elements in the domain container
27537
27538 // Set any additional information for the element
27539 this->add_element_load_balance_helper(
27541
27542 // Add nodes to the new element
27543 unsigned n_node = f_el_pt->nnode();
27544 for (unsigned j = 0; j < n_node; j++)
27545 {
27546 Node* new_nod_pt = 0;
27547
27548 // Call the add halo node helper function
27549 add_received_node_load_balance_helper(new_nod_pt,
27554 iproc,
27555 j,
27556 f_el_pt,
27560 }
27561 }
27562 else // the element already exists
27563 {
27564#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27565 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
27566 << " Index of existing element "
27567 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
27568 << std::endl;
27569#endif
27570
27571 // Incrase the index, we do anything else with the element
27572 Counter_for_flat_packed_unsigneds++;
27573
27574 } // else the element already exists
27575 }
27576
27577 //========start of add_element_load_balance_helper=====================
27578 /// Helper function to create elements on the loop
27579 /// process based on the info received in
27580 /// send_and_received_elements_nodes_info
27581 /// This function is in charge of verify if the element is associated
27582 /// to a boundary and associate to it if that is the case
27583 //======================================================================
27584 template<class ELEMENT>
27586 const unsigned& iproc,
27587 Vector<Vector<std::map<unsigned, FiniteElement*>>>&
27590 {
27591 // Get the number of processors
27592 const unsigned nproc = this->communicator_pt()->nproc();
27593
27594#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27595 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
27596 << " Bool: Element is associated to a boundary "
27597 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
27598 << std::endl;
27599#endif
27600
27601 // Is on an original boundary?
27602 const unsigned is_on_original_boundary =
27603 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
27604 if (is_on_original_boundary == 1)
27605 {
27606#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27607 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
27608 << " How many boundaries are associated with the element "
27609 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
27610 << std::endl;
27611#endif
27612 // Number of boundaries the element is associated with
27613 const unsigned nassociated_boundaries =
27614 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
27615
27616 // Loop over the associated boundaries
27617 for (unsigned b = 0; b < nassociated_boundaries; b++)
27618 {
27619#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27620 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
27621 << " Boundary associated to the element "
27622 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
27623 << std::endl;
27624#endif
27625
27626 // The boundary id
27627 const unsigned bnd =
27628 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
27629
27630#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27631 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
27632 << " Face index of the element "
27633 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
27634 << std::endl;
27635#endif
27636
27637 // The face index
27638 const unsigned face_index =
27639 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
27640
27641 // Associate the element with the boundary and establish as many
27642 // face indexes it has
27643 this->Boundary_element_pt[bnd].push_back(ele_pt);
27644 this->Face_index_at_boundary[bnd].push_back(face_index);
27645
27646 } // (b < nassociated_boundaries)
27647
27648 // Here read the info. regarding the boundary-region of the element
27649#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27650 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
27651 << " Bool: Element is associated to a boundary-region "
27652 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
27653 << std::endl;
27654#endif
27655
27656 // Is the element associated to a boundary-region?
27657 if (Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++] == 1)
27658 {
27659#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27661 << "Rec:" << Counter_for_flat_packed_unsigneds
27662 << " How many boundaries-regions are associated with the element "
27663 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
27664 << std::endl;
27665#endif
27666 // Number of boundary-regions the element is associated
27667 const unsigned nassociated_boundaries_and_regions =
27668 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
27669
27670 for (unsigned br = 0; br < nassociated_boundaries_and_regions; br++)
27671 {
27672#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27673 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
27674 << " Boundary associated to the element "
27675 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
27676 << std::endl;
27677#endif
27678 // The boundary id
27679 const unsigned bnd =
27680 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
27681
27682#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27683 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
27684 << " Region associated to the element "
27685 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
27686 << std::endl;
27687#endif
27688 // The region id
27689 const unsigned region =
27690 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
27691
27692#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27693 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
27694 << " Face index of the element in boundary-region "
27695 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
27696 << std::endl;
27697#endif
27698 const unsigned face_index =
27699 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
27700
27701 // Associate the element with the boundary-regions and establish
27702 // as many face indexes it has
27703 this->Boundary_region_element_pt[bnd][region].push_back(ele_pt);
27704 this->Face_index_region_at_boundary[bnd][region].push_back(
27705 face_index);
27706
27707 } // for (br < nassociated_boundaries_and_regions)
27708
27709 } // Is the element associated with a boundary-region?
27710
27711 } // The element is associated with an original boundary
27712#ifdef PARANOID
27713 else
27714 {
27715 if (is_on_original_boundary != 0)
27716 {
27717 std::ostringstream error_message;
27718 error_message
27719 << "The current element is not on an original boundary, this should\n"
27720 << "be indicated by a zero flag. However, the read value for\n"
27721 << "that flag is (" << is_on_original_boundary << ").\n\n";
27722 throw OomphLibError(
27723 error_message.str(),
27724 "RefineableTriangleMesh::add_element_load_balance_helper()",
27726 } // if (is_on_shared_boundary != 0)
27727 }
27728#endif
27729
27730#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27731 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
27732 << " Bool: Element is associated to a shared boundary "
27733 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
27734 << std::endl;
27735#endif
27736
27737 // Is the element a shared boundary element?
27738 const unsigned is_on_shared_boundary =
27739 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
27740 if (is_on_shared_boundary == 3)
27741 {
27742#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27744 << "Rec:" << Counter_for_flat_packed_unsigneds
27745 << " How many shared boundaries are associated with the element "
27746 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
27747 << std::endl;
27748#endif
27749
27750 // The number of shared boundaries the element is associated
27751 const unsigned nassociated_shared_boundaries =
27752 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
27753
27754 // Loop over the associated shared boundaries
27755 for (unsigned b = 0; b < nassociated_shared_boundaries; b++)
27756 {
27757#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27758 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
27759 << " Shared boundary associated to the element "
27760 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
27761 << std::endl;
27762#endif
27763 const unsigned bnd =
27764 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
27765
27766#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27768 << "Rec:" << Counter_for_flat_packed_unsigneds
27769 << " Face index of the element associated to the shared boundary "
27770 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
27771 << std::endl;
27772#endif
27773
27774 const unsigned face_index =
27775 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
27776
27777 this->add_shared_boundary_element(bnd, ele_pt);
27778 this->add_face_index_at_shared_boundary(bnd, face_index);
27779
27780 } // (b < nassociated_shared_boundaries)
27781
27782 } // The element is associted with a shared boundary
27783#ifdef PARANOID
27784 else
27785 {
27786 if (is_on_shared_boundary != 0)
27787 {
27788 std::ostringstream error_message;
27789 error_message
27790 << "The current element is not on a shared boundary, this should\n"
27791 << "be indicated by a zero flag. However, the read value for\n"
27792 << "that flag is (" << is_on_shared_boundary << ").\n\n";
27793 throw OomphLibError(
27794 error_message.str(),
27795 "RefineableTriangleMesh::add_element_load_balance_helper()",
27797 } // if (is_on_shared_boundary != 0)
27798 }
27799#endif
27800
27801 // Now check if the element is a haloed element in the sender
27802 // processor with any other processor
27803
27804 // Loop over the processors
27805 for (unsigned jproc = 0; jproc < nproc; jproc++)
27806 {
27807#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27808 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
27809 << " Bool: Number of haloed indexes of the element with the "
27810 << jproc << " processor: "
27811 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
27812 << std::endl;
27813#endif
27814 // Is the element haloed with the jproc processor
27815 const unsigned n_index_haloed_jproc =
27816 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
27817 // Loop over the number of haloed indexes
27818 for (unsigned ihd = 0; ihd < n_index_haloed_jproc; ihd++)
27819 {
27820#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27821 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
27822 << " Bool: The haloed element index with the " << jproc
27823 << " processor: "
27824 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
27825 << std::endl;
27826#endif
27827 const unsigned haloed_index =
27828 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
27829
27830 // Set the halod element in the proper storage
27832
27833 } // for (ihd < n_index_haloed_jproc)
27834
27835 } // for (jproc < nproc)
27836 }
27837
27838 //======================================================================
27839 /// Helper function to add a new node from load balance
27840 //======================================================================
27841 template<class ELEMENT>
27843 Node*& new_nod_pt,
27845 Vector<Vector<std::map<unsigned, FiniteElement*>>>&
27848 Vector<Vector<Vector<std::map<unsigned, Node*>>>>&
27850 unsigned& iproc,
27851 unsigned& node_index,
27852 FiniteElement* const& new_el_pt,
27854 std::map<Vector<unsigned>, unsigned>& node_name_to_global_index,
27856 {
27857 // Given the node, received information about it from processor
27858 // iproc, construct it on the current process
27859#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27860 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
27861 << " Bool: New node needs to be constructed "
27862 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
27863 << std::endl;
27864#endif
27865 if (Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++] == 1)
27866 {
27867 // Construct a new node based upon sent information, or copy a node
27868 // from one of the shared boundaries
27869 construct_new_node_load_balance_helper(new_nod_pt,
27874 iproc,
27875 node_index,
27876 new_el_pt,
27880 }
27881 else
27882 {
27883#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27884 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
27885 << " Index of existing halo node "
27886 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
27887 << std::endl;
27888#endif
27889 // The node already exist, copy it from the indicated position
27890
27891 // Get the node's index, and copy it
27893 [Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++]];
27894
27895 // Set the node in the current element
27897 }
27898 }
27899
27900 //============start_of_construct_new_node_load_balance_helper()=========
27901 /// Helper function which constructs a new node (on an
27902 /// element) with the information sent from the load balance
27903 /// process
27904 //======================================================================
27905 template<class ELEMENT>
27907 Node*& new_nod_pt,
27909 Vector<Vector<std::map<unsigned, FiniteElement*>>>&
27912 Vector<Vector<Vector<std::map<unsigned, Node*>>>>&
27914 unsigned& iproc,
27915 unsigned& node_index,
27916 FiniteElement* const& new_el_pt,
27918 std::map<Vector<unsigned>, unsigned>& node_name_to_global_index,
27920 {
27921 // Get the number of processors
27922 const unsigned nproc = this->communicator_pt()->nproc();
27923 // Get the rank of the current processor
27924 const unsigned my_rank = this->communicator_pt()->my_rank();
27925
27926 // The first entry indicates the number of values at this new Node
27927 //(which may be different across the same element e.g. Lagrange
27928 // multipliers)
27929#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27930 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
27931 << " Number of values of external halo node "
27932 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
27933 << std::endl;
27934#endif
27935 unsigned n_val = Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
27936
27937 // Null TimeStepper for now
27938 TimeStepper* time_stepper_pt = this->Time_stepper_pt;
27939 // Default number of previous values to 1
27940 unsigned n_prev = time_stepper_pt->ntstorage();
27941
27942 // ------------------------------------------------------
27943 // Check if the node is on an original boundary
27944#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27945 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
27946 << " Is the node on an original boundary "
27947 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
27948 << std::endl;
27949#endif
27950
27951 // Flag to indicate if the node is on original boundaries
27952 const unsigned node_on_original_boundaries =
27953 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
27954
27955 // Store the original boundaries where the node is on
27957 // Store the zeta coordinates of the node on the original boundaries
27959 // Store the number of original boundaries the node is on
27961
27963 {
27964 // How many original boundaries does the node live on?
27965#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27966 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
27967 << " Number of boundaries the node is on: "
27968 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
27969 << std::endl;
27970#endif
27972 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
27973
27974 // Resize the containers
27977
27978 for (unsigned i = 0; i < n_original_boundaries_node_is_on; i++)
27979 {
27980 // Boundary number
27981#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27982 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
27983 << " Node is on boundary "
27984 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
27985 << std::endl;
27986#endif
27988 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
27990 Flat_packed_doubles[Counter_for_flat_packed_doubles++];
27991 }
27992
27993 } // if (node_on_original_boundaries==2)
27994#ifdef PARANOID
27995 else
27996 {
27998 {
27999 std::ostringstream error_message;
28000 error_message
28001 << "The current node is not on an original boundary, this should\n"
28002 << "be indicated by a zero flag. However, the read value for\n"
28003 << "that flag is (" << node_on_original_boundaries << ").\n\n";
28004 throw OomphLibError(
28005 error_message.str(),
28006 "RefineableTriangleMesh::construct_new_halo_node_helper()",
28008 } // if (node_on_original_boundaries != 0)
28009 }
28010#endif
28011
28012 // --------------------------------------------------------------
28013 // Check if the node was on a shared boundary with the iproc
28014 // processor
28015#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28016 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
28017 << " Is node on shared boundary? "
28018 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
28019 << std::endl;
28020#endif
28021 const unsigned is_node_on_shared_boundary =
28022 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
28023 if (is_node_on_shared_boundary == 1)
28024 {
28025 // How many shared boundaries does the node live on?
28026#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28027 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
28028 << " Number of boundaries the node is on: "
28029 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
28030 << std::endl;
28031#endif
28032 const unsigned n_shd_bnd_node_is_on =
28033 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
28035 for (unsigned i = 0; i < n_shd_bnd_node_is_on; i++)
28036 {
28037 // Shared boundary number
28038#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28039 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
28040 << " Node is on boundary "
28041 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
28042 << std::endl;
28043#endif
28045 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
28046 }
28047
28048 // Get the index of the node on the shared boundary
28049#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28050 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
28051 << " Index of node on boundary "
28052 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
28053 << std::endl;
28054#endif
28055 // Get the node index of the node on the shared boundary
28057 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
28058
28059 // Get the pointer to the node with the received info.
28060 new_nod_pt = this->sorted_shared_boundary_node_pt(
28062
28063 } // if (is_node_on_shared_boundary == 1)
28064#ifdef PARANOID
28065 else
28066 {
28067 if (is_node_on_shared_boundary != 0)
28068 {
28069 std::ostringstream error_message;
28070 error_message
28071 << "The current node is not on a shared boundary, this should\n"
28072 << "be indicated by a zero flag. However, the read value for\n"
28073 << "that flag is (" << is_node_on_shared_boundary << ").\n\n";
28074 throw OomphLibError(
28075 error_message.str(),
28076 "RefineableTriangleMesh::construct_new_halo_node_helper()",
28078 } // if (node_on_shared_boundary != 0)
28079 }
28080#endif
28081
28082 // ------------------------------------------------------------
28083 // Is the node on a shared boundary with other processor?
28084#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28085 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
28086 << " Is the node on shared boundaries with other processors "
28087 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
28088 << std::endl;
28089#endif
28090
28091 // Is the node in shared boundaries no associated with the
28092 // receiver processor
28094 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
28095
28096 // The containers where to store the info.
28101
28102 // How many shared bounaries with other processors the node lives on
28104
28105 // Is the node on shared boundaries with other processors
28107 {
28108#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28109 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
28110 << " In how many shared boundaries with other "
28111 << "processors is the node "
28112 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
28113 << std::endl;
28114#endif
28115
28116 // How many nodes on other shared boundaries were found
28118 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
28119
28120 // Resize the containers
28125
28126 for (unsigned i = 0; i < n_shd_bnd_with_other_procs_have_node; i++)
28127 {
28128#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28129 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
28130 << " Processor where the other shared boundary"
28131 << "has the node"
28132 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
28133 << std::endl;
28134#endif
28135 // Read the other processor 1
28137 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
28138
28139#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28140 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
28141 << " Processor where the other shared boundary"
28142 << "has the node"
28143 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
28144 << std::endl;
28145#endif
28146 // Read the other processor 2
28148 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
28149
28150#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28151 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
28152 << " Other shared boundary id where the node is on: "
28153 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
28154 << std::endl;
28155#endif
28156
28157 // Read the other shared boundary id
28159 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
28160
28161#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28162 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
28163 << " Node index on the other shared boundary "
28164 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
28165 << std::endl;
28166#endif
28167
28168 // Read the node index on the other shared boundary
28169 other_indexes[i] =
28170 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
28171
28172 } // for (i < n_shd_bnd_with_other_procs_have_node)
28173
28174 } // if (is_the_node_in_shared_boundaries_with_other_processors == 4)
28175#ifdef PARANOID
28176 else
28177 {
28179 {
28180 std::ostringstream error_message;
28181 error_message
28182 << "The current node is not on a shared boundary with\n"
28183 << "other processors, this should be indicated by a zero flag.\n"
28184 << "However, the read value for that flag is ("
28186 throw OomphLibError(
28187 error_message.str(),
28188 "RefineableTriangleMesh::construct_new_node_load_balance_helper()",
28190 }
28191 }
28192#endif
28193
28194 // ------------------------------------------------------------
28195 // Receive the info. to check if the node is on a haloed element
28196 // with any processor
28197
28198 // Store the halo element number with jproc where the node was found
28200 // Store the node number on the halo element where the node was found
28202
28203 // Loop over the processors
28204 for (unsigned jproc = 0; jproc < nproc; jproc++)
28205 {
28206#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28207 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
28208 << " The node is on "
28209 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
28210 << " halo elements with " << jproc << " processor"
28211 << std::endl;
28212#endif
28213 // Get the number of halo elements with jproc processor where the
28214 // node was found
28215 const unsigned n_jproc_halo_ele_node_is_on =
28216 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
28217
28218 // Resize the containers
28222
28223 // Read halo elements indexes (which are indexes of the halo
28224 // elements of the sender processor (iproc) with other processors
28225 // (included my_rank)
28226 for (unsigned i = 0; i < n_jproc_halo_ele_node_is_on; i++)
28227 {
28228 // Get the halo element index in the jproc processor
28229#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28230 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
28231 << " The halo element index where the node is on "
28232 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
28233 << std::endl;
28234#endif
28235 // Get the node index on the halo element
28236 const unsigned halo_ele_index =
28237 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
28238#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28239 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
28240 << " The node index on the halo element where the node "
28241 << "is on "
28242 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
28243 << std::endl;
28244#endif
28245 const unsigned node_index_on_halo_ele =
28246 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
28247
28248 // Store the halo element number
28250 // Store the index of on the haloed element
28252
28253 } // for (i < n_jproc_halo_ele_node_is_on)
28254
28255 } // for (jproc < nproc)
28256
28257 // Store the node pointers obtained from the indicated halo elements
28258 // (use a set to check for the case when the node pointer is
28259 // different)
28260 std::set<Node*> set_haloed_node_pt;
28261
28262 // Store the node pointer obtained from the haloed elements
28263 Node* haloed_node_pt = 0;
28264
28265 // Flag to indicate if it is on a haloed element of the current
28266 // processor with the iproc processor. If this flag is true then
28267 // there is no need to read the info. to create the node, only copy
28268 // the node from the indicated haloed element-node
28270 if (halo_element_number[my_rank].size() > 0)
28271 {
28272 // The node is part of the haloed element in the current processor
28273 // (my_rank) with the receiver processor
28275
28276 // Get the number of haloed elements in the current processor
28278 // Loop over the different haloed indexes, and get the nodes
28279 // instances from all the indicated haloed elements (all of them
28280 // should be the same)
28281 for (unsigned i = 0; i < n_haloed_indexes; i++)
28282 {
28283 // Get the haloed element numbers where the node is on
28284 const unsigned haloed_index = halo_element_number[my_rank][i];
28285 // Get the node index on the haloed element
28286 const unsigned haloed_node_index =
28288
28289 // Get the haloed element (with iproc)
28291 // Get the node on the indicated node number
28294
28295 // Set the pointer for the obtained haloed node
28296 haloed_node_pt = tmp_haloed_node_pt;
28297
28298 // Add the node to the set of node pointers
28300
28301#ifdef PARANOID
28302 if (set_haloed_node_pt.size() > 1)
28303 {
28304 std::ostringstream error_message;
28305 error_message
28306 << "When adding the " << haloed_node_index << " node of the "
28307 << haloed_index << "-th haloed element\n"
28308 << "in the currrent processor with the " << iproc << " processor"
28309 << "it was found that\nthe node pointer is different from the other"
28310 << "instances of the node.\nIt means we have a repeated node."
28311 << "This are the node coordinates of the previous node instances\n"
28312 << "The last entry is for the just added node with a different "
28313 "node\n"
28314 << "pointer\n";
28315 for (std::set<Node*>::iterator it = set_haloed_node_pt.begin();
28316 it != set_haloed_node_pt.end();
28317 it++)
28318 {
28319 error_message << "Node: (" << (*it)->x(0) << ", " << (*it)->x(1)
28320 << ")\n";
28321 }
28322 error_message << "\n";
28323 throw OomphLibError(
28324 error_message.str(),
28325 "RefineableTriangleMesh::construct_new_node_load_balance_helper()",
28327 }
28328#endif
28329
28330 } // for (i < n_haloed_indexes)
28331
28332 } // if (halo_element_number[iproc].size() > 0)
28333
28334 // Flag to indicate if the node has been found on a haloed element
28335 // of other processor with the iproc processor
28337 // Loop over the processors (only until the iproc since no info. of
28338 // higher processors has been received)
28339 for (unsigned jproc = 0; jproc < iproc; jproc++)
28340 {
28341 // Is the node on a halo element with the jproc processor
28342 if (halo_element_number[jproc].size() > 0)
28343 {
28344 // Get the number of halo elements with the jproc processor
28345 const unsigned n_halo_indexes = halo_element_number[jproc].size();
28346 // Loop over the different halo indexes, and get the nodes
28347 // instances from all the indicated halo elements (all of them
28348 // should be the same)
28349 for (unsigned i = 0; i < n_halo_indexes; i++)
28350 {
28351 // Get the haloed element numbers where the node is on
28352 const unsigned haloed_index = halo_element_number[jproc][i];
28353 // Get the node index on the haloed element
28354 const unsigned haloed_node_index =
28356
28357 // Have we received the indicated element? (Get the haloed
28358 // element on jproc with the iproc processor)
28359 std::map<unsigned, FiniteElement*>::iterator it_map =
28361 // Have we received the indicated element?
28363 {
28364 // Set the flag of found element in other processors haloed
28365 // element, in this case in haloed elements of processor
28366 // jproc wiht iproc processor
28368
28369 // Get the element
28370 FiniteElement* tmp_haloed_ele_pt = (*it_map).second;
28371 // Get the node on the indicated node number
28374
28375 // Set the pointer for the obtained haloed node
28376 haloed_node_pt = tmp_haloed_node_pt;
28377
28378 // Add the node to the set of node pointers
28380
28381#ifdef PARANOID
28382 if (set_haloed_node_pt.size() > 1)
28383 {
28384 std::ostringstream error_message;
28385 error_message
28386 << "When adding the " << haloed_node_index << " node of the "
28387 << haloed_index << "-th haloed element "
28388 << "of the " << jproc << " processor\nwith the " << iproc
28389 << " processor, it was found that\n"
28390 << "the node pointer is different from the other\n"
28391 << "instances of the node.\nThis means we have a repeated "
28392 "node.\n"
28393 << "These are the node coordinates of the previous node "
28394 << "instances\n"
28395 << "The last entry is for the just added node with a "
28396 "different\n"
28397 << "node pointer\n";
28398 for (std::set<Node*>::iterator it = set_haloed_node_pt.begin();
28399 it != set_haloed_node_pt.end();
28400 it++)
28401 {
28402 error_message << "Node: (" << (*it)->x(0) << ", " << (*it)->x(1)
28403 << ")\n";
28404 }
28405 error_message << "\n";
28406 throw OomphLibError(error_message.str(),
28407 "RefineableTriangleMesh::construct_new_node_"
28408 "load_balance_helper()",
28410 }
28411#endif
28412
28413 } // if (it_map != received_old_haloed_element_pt[jproc][iproc].end())
28414 // Have we received the element?
28415
28416 } // for (i < n_haloed_indexes)
28417
28418 } // if (halo_element_number[iproc].size() > 0)
28419
28420 } // for (jproc < nproc)
28421
28422 // If the node was found in the haloed elements of the current
28423 // processor with the iproc processor, or in the haloed elements of
28424 // any other processor with the iproc processor then copy the node
28425 // pointer (no problem if we overwrite the node info. it should be
28426 // the same node pointer)
28429 {
28430 // Set the node pointer
28431 new_nod_pt = haloed_node_pt;
28432 }
28433
28434 // Now we have all the info. to decide if the node should be created
28435 // or not
28436
28437 // First check if the node is a shared boundary node, or if it has
28438 // been found on haloed elements
28439 if (is_node_on_shared_boundary == 1 ||
28441 {
28442 // We already have the node, we do not need to create it
28443
28444 // Only check if we need to add boundary info. to the node
28446 {
28447 // The node is a boundary node, add the boundary info. before
28448 // adding it to the domain
28449
28450 // Associate the node to the given boundaries
28451 for (unsigned i = 0; i < n_original_boundaries_node_is_on; i++)
28452 {
28453 add_boundary_node(original_boundaries_node_is_on[i], new_nod_pt);
28454 // Establish the boundary coordinates for the node
28456 zeta[0] = zeta_coordinates[i];
28457 new_nod_pt->set_coordinates_on_boundary(
28459 }
28460
28461 } // if (node_on_original_boundaries==2)
28462
28463 // Add the node to the domain
28465
28466 // Add the node to the element
28468
28469 } // if (is_node_on_shared_boundary == 1)
28470
28471 // Now check if the node is on a shared boundary with another
28472 // processor, if that is the case try to find the node that may have
28473 // been already sent by the other processors
28474
28475 // This flags indicates if the node was found, and then decide if it
28476 // is required to create the node
28478 // Flag to indicate whether the node should be created as a boundary
28479 // node or not. If the node lies on a shared boundary with other
28480 // processor the we create it as a boundary node. The processor from
28481 // which we are receiving info. (iproc) may not know that the node
28482 // lies on an original boundary. If the node lies on an original
28483 // boundary then its info. will be sent by another processor, then
28484 // we can set its boundary info. since the node was constructed as a
28485 // boundary node
28486 bool build_node_as_boundary_node = false;
28487
28489 {
28490 // Build the node as a boundary node
28492
28493 // Try to get the node pointer in case that the node has been
28494 // already sent by the other processors
28495
28496 // Get the number of initial shared boundaries to correct the
28497 // index of the shared boundary
28498 const unsigned initial_shd_bnd_id = this->initial_shared_boundary_id();
28499
28500 // Add the found nodes in the container, should be the same but
28501 // better check
28503
28504 // Now try to find the node in any of the other shared boundaries
28505 for (unsigned i = 0; i < n_shd_bnd_with_other_procs_have_node; i++)
28506 {
28507 unsigned oproc1 = other_processor_1[i];
28508 unsigned oproc2 = other_processor_2[i];
28509
28510 // Check that we always check with the lower processors number
28511 // first
28512 if (oproc1 > oproc2)
28513 {
28514 oproc2 = oproc1;
28516 } // if (oproc1 > oproc2)
28517
28518 // Re-compute the shared boundary id between the other
28519 // processors
28520 const unsigned shd_bnd_id =
28522 // Read the index
28523 const unsigned index = other_indexes[i];
28524
28525 // Check if there are nodes received from the other processor
28526 // and with the given shared boundary
28527 const unsigned n_nodes_on_other_processor =
28529
28531 {
28532 // Check if we can find the index of the node in that other
28533 // processor and shared boundary id
28534 std::map<unsigned, Node*>::iterator it =
28536
28537 // If the index exist then get the node pointer
28538 if (it !=
28540 {
28541 // Mark the node as found
28543 // Get the node pointer
28544 Node* tmp_node_pt =
28546 found_node_pt.push_back(tmp_node_pt);
28547 } // if (it!=
28548 // other_proc_shd_bnd_node_pt[oproc1][oproc2][shd_bnd_id].end())
28549
28550 } // if (n_nodes_on_other_processor > 0)
28551
28552 } // for (i < n_shd_bnd_with_other_procs_have_node)
28553
28554 // If the node was found, then all their instances should be the
28555 // same but better check
28557 {
28558#ifdef PARANOID
28559 const unsigned ntimes_node_found = found_node_pt.size();
28560 for (unsigned j = 1; j < ntimes_node_found; j++)
28561 {
28562 if (found_node_pt[j - 1] != found_node_pt[j])
28563 {
28564 std::ostringstream error_message;
28565 error_message
28566 << "The instances of the node that was found to be on a\n"
28567 << "shared boundary with other processors are not the same,\n"
28568 << "the coordinates for the nodes are these:\n"
28569 << "(" << found_node_pt[j - 1]->x(0) << ", "
28570 << found_node_pt[j - 1]->x(1) << ")\n"
28571 << "(" << found_node_pt[j]->x(0) << ", " << found_node_pt[j]->x(1)
28572 << ")\n"
28573 << "Not be surprised if they are the same since the node is\n"
28574 << "repeated!!!\n";
28575 throw OomphLibError(
28576 error_message.str(),
28577 "RefineableTriangleMesh::construct_new_halo_node_helper()",
28579
28580 } // if (found_node_pt[j-1] != found_node_pt[j])
28581
28582 } // for (j < ntimes_node_found)
28583#endif
28584
28585 // Check if the node is a shared boundary node from the current
28586 // processor and the iproc processor, if that is the case, and
28587 // the node is also on a shared boundary with other processor,
28588 // then the pointer should be the same!!!
28589 if (is_node_on_shared_boundary == 1)
28590 {
28591 // The pointer to the node is already assigned, it was
28592 // assigned when thenode was found to be on a shared boundary
28593 // with the iproc processor
28594 if (found_node_pt[0] != new_nod_pt)
28595 {
28596 std::ostringstream error_message;
28597 error_message
28598 << "The pointer of the node that was found to be on a\n"
28599 << "shared boundary with other processor(s) and the pointer\n"
28600 << "of the node on shared boundary with the receiver\n"
28601 << "processor (iproc) are not the same. This means we have a\n"
28602 << "repeated node)\n"
28603 << "The coordinates for the nodes are:\n"
28604 << "(" << found_node_pt[0]->x(0) << ", " << found_node_pt[0]->x(1)
28605 << ")\n"
28606 << "(" << new_nod_pt->x(0) << ", " << new_nod_pt->x(1) << ")\n"
28607 << "Not to be surprised if they are the same since the node is\n"
28608 << "repeated!!!\n";
28609 throw OomphLibError(
28610 error_message.str(),
28611 "RefineableTriangleMesh::construct_new_halo_node_helper()",
28613 } // if (found_node_pt[0] != new_nod_pt)
28614
28615 } // if (is_node_on_shared_boundary == 1)
28616 else
28617 {
28618 // Take the first instance of the node in case that it was
28619 // found and is not on a shared boundary with the iproc
28620 // processor
28622 }
28623
28624 } // if (found_node_in_other_shared_boundaries)
28625
28626 } // if (is_the_node_in_shared_boundaries_with_other_processors == 4)
28627
28628 // -----------------------------------------------------------------
28629 // Create the node or read the received info if the node is not on a
28630 // shared boundary with the iproc processor and if the node is not
28631 // part of the haloed elements with the iproc processor in the
28632 // current processors
28633 if (is_node_on_shared_boundary != 1 &&
28635 {
28636 // If the node is on a shared boundary with other processor we
28637 // need to read all the info. since the processor that sent the
28638 // info. did not know that the node is part of another shared
28639 // boundary
28640
28641 // If the node is not a shared boundary (with any processor), or
28642 // if this is the first time that the info. of the node is
28643 // received from any of the processors with which is has a shared
28644 // boundary, then we create the node
28645
28646 // Is the node a boundary node or should it be build as a boundary
28647 // node because it is on a shared boundary with other processors
28649 {
28650 // Check if necessary to create the node, or if it has been
28651 // already found in shared boundaries with other processors or
28652 // in the haloed elements with of other processors with the
28653 // iproc processor
28656 {
28657 // Construct a boundary node
28658 if (time_stepper_pt != 0)
28659 {
28660 new_nod_pt =
28662 }
28663 else
28664 {
28666 }
28667
28668 } // if (!found_node_in_other_shared_boundaries ||
28669 // !found_on_haloed_element_with_other_processor)
28670 else
28671 {
28672 // If the node was found then assign the node to the element
28674 } // else if (!found_node_in_other_shared_boundaries ||
28675 // !found_on_haloed_element_with_other_processor)
28676
28677 // Associate the node to the given boundaries
28678 for (unsigned i = 0; i < n_original_boundaries_node_is_on; i++)
28679 {
28680 add_boundary_node(original_boundaries_node_is_on[i], new_nod_pt);
28681 // Establish the boundary coordinates for the node
28683 zeta[0] = zeta_coordinates[i];
28684 new_nod_pt->set_coordinates_on_boundary(
28686 }
28687
28688 } // if (node is on an original boundary)
28689 else
28690 {
28691 // Check if necessary to create the node, or if it has been
28692 // already found in shared boundaries with other processors or
28693 // in the haloed elements with of other processors with the
28694 // iproc processor
28697 {
28698 // Construct an ordinary (non-boundary) node
28699 if (time_stepper_pt != 0)
28700 {
28702 }
28703 else
28704 {
28706 }
28707 } // if (!found_node_in_other_shared_boundaries ||
28708 // !found_on_haloed_element_with_other_processor)
28709 else
28710 {
28711 // If the node was found then assign the node to the element
28713 } // else // if (!found_node_in_other_shared_boundaries ||
28714 // !found_on_haloed_element_with_other_processor)
28715
28716 } // else (the node is not a boundary node)
28717
28718 // ... and gather all its information
28719
28720 // If the node was found or not in other shared boundaries, this
28721 // is the first time the node is received from this processor
28722 // (iproc), therefore it is added to the vector of nodes received
28723 // from this processor (iproc)
28725
28726 // Check if necessary to state all the info. to the node if it has
28727 // been already found in shared boundaries with other processors
28728 // or in the haloed elements with of other processors with the
28729 // iproc processor
28732 {
28733 // Add the node to the general node storage
28734 this->add_node_pt(new_nod_pt);
28735 } // if (!found_node_in_other_shared_boundaries ||
28736 // !found_on_haloed_element_with_other_processor)
28737
28738 // Is the new constructed node Algebraic?
28740
28741 // If it is algebraic, its node update functions will
28742 // not yet have been set up properly
28743 if (new_alg_nod_pt != 0)
28744 {
28745 // The AlgebraicMesh is the external mesh
28746 AlgebraicMesh* alg_mesh_pt = dynamic_cast<AlgebraicMesh*>(this);
28747
28748 /// The first entry of All_alg_nodal_info contains
28749 /// the default node update id
28750 /// e.g. for the quarter circle there are
28751 /// "Upper_left_box", "Lower right box" etc...
28752#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28753 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
28754 << " Alg node update id "
28755 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
28756 << std::endl;
28757#endif
28758
28759 unsigned update_id =
28760 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
28761
28762 Vector<double> ref_value;
28763
28764 // The size of this vector is in the next entry
28765 // of All_alg_nodal_info
28766#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28767 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
28768 << " Alg node # of ref values "
28769 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
28770 << std::endl;
28771#endif
28772 unsigned n_ref_val =
28773 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
28774
28775 // The reference values themselves are in
28776 // All_alg_ref_value
28777 ref_value.resize(n_ref_val);
28778 for (unsigned i_ref = 0; i_ref < n_ref_val; i_ref++)
28779 {
28780 ref_value[i_ref] =
28781 Flat_packed_doubles[Counter_for_flat_packed_doubles++];
28782 }
28783
28784 Vector<GeomObject*> geom_object_pt;
28785 /// again we need the size of this vector as it varies
28786 /// between meshes; we also need some indication
28787 /// as to which geometric object should be used...
28788
28789 // The size of this vector is in the next entry
28790 // of All_alg_nodal_info
28791#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28792 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
28793 << " Alg node # of geom objects "
28794 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
28795 << std::endl;
28796#endif
28797 unsigned n_geom_obj =
28798 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
28799
28800 // The remaining indices are in the rest of
28801 // All_alg_nodal_info
28802 geom_object_pt.resize(n_geom_obj);
28803 for (unsigned i_geom = 0; i_geom < n_geom_obj; i_geom++)
28804 {
28805#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28806 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
28807 << " Alg node: geom object index "
28808 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
28809 << std::endl;
28810#endif
28811 unsigned geom_index =
28812 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
28813 // This index indicates which of the AlgebraicMesh's
28814 // stored geometric objects should be used
28815 // (0 is a null pointer; everything else should have
28816 // been filled in by the specific Mesh). If it
28817 // hasn't been filled in then the update_node_update
28818 // call should fix it
28819 geom_object_pt[i_geom] = alg_mesh_pt->geom_object_list_pt(geom_index);
28820 }
28821
28822 // Check if necessary to state all the info. to the node if it
28823 // has been already found in shared boundaries with other
28824 // processors or in the haloed elements with of other processors
28825 // with the iproc processor
28828 {
28829 /// For the received update_id, ref_value, geom_object
28830 /// call add_node_update_info
28831 new_alg_nod_pt->add_node_update_info(
28832 update_id, alg_mesh_pt, geom_object_pt, ref_value);
28833
28834 /// Now call update_node_update
28835 alg_mesh_pt->update_node_update(new_alg_nod_pt);
28836
28837 } // if (!found_node_in_other_shared_boundaries ||
28838 // !found_on_haloed_element_with_other_processor)
28839
28840 } // if (new_alg_nod_pt!=0)
28841
28842 // Check if necessary to state all the info. to the node if it has
28843 // been already found in shared boundaries with other processors
28844 // or in the haloed elements with of other processors with the
28845 // iproc processor
28848 {
28849 // Is the node a MacroElementNodeUpdateNode?
28851 dynamic_cast<MacroElementNodeUpdateNode*>(new_nod_pt);
28852
28853 if (macro_nod_pt != 0)
28854 {
28855 // Need to call set_node_update_info; this requires
28856 // a Vector<GeomObject*> (taken from the mesh)
28857 Vector<GeomObject*> geom_object_vector_pt;
28858
28859 // Access the required geom objects from the
28860 // MacroElementNodeUpdateMesh
28862 dynamic_cast<MacroElementNodeUpdateMesh*>(this);
28863 geom_object_vector_pt = macro_mesh_pt->geom_object_vector_pt();
28864
28865 // Get local coordinate of node in new element
28869
28870 // Set node update info for this node
28871 macro_nod_pt->set_node_update_info(
28872 new_el_pt, s_in_macro_node_update_element, geom_object_vector_pt);
28873 }
28874
28875 } // if (!found_node_in_other_shared_boundaries ||
28876 // !found_on_haloed_element_with_other_processor)
28877
28878 // If there are additional values, resize the node
28879 unsigned n_new_val = new_nod_pt->nvalue();
28880
28881 // Check if necessary to state all the info. to the node if it has
28882 // been already found in shared boundaries with other processors
28883 // or in the haloed elements with of other processors with the
28884 // iproc processor
28887 {
28888 if (n_val > n_new_val)
28889 {
28890 // If it has been necessary to resize then it may be becuse
28891 // the node is on a FSI boundary, if that is the case we need
28892 // to set a map for these external values
28893
28894 // Cast to a boundary node
28896 dynamic_cast<BoundaryNodeBase*>(new_nod_pt);
28897
28898 // Create storage, if it doesn't already exist, for the map
28899 // that will contain the position of the first entry of
28900 // this face element's additional values,
28901 if (bnod_pt->index_of_first_value_assigned_by_face_element_pt() == 0)
28902 {
28903 bnod_pt->index_of_first_value_assigned_by_face_element_pt() =
28904 new std::map<unsigned, unsigned>;
28905 }
28906
28907 // Get pointer to the map
28908 std::map<unsigned, unsigned>* map_pt =
28909 bnod_pt->index_of_first_value_assigned_by_face_element_pt();
28910
28911 // The id of the face to which this node belong in the bulk
28912 // element
28913 const unsigned id_face = 0;
28914 // We only resize the node values Vector if we haven't done it yet
28915 std::map<unsigned, unsigned>::const_iterator p =
28916 map_pt->find(id_face);
28917
28918 // If this node hasn't been resized for current id
28919 if (p == map_pt->end())
28920 {
28921 // assign the face element id and the position of the
28922 // first entry to the boundary node
28923 (*map_pt)[id_face] = n_new_val;
28924
28925 // resize the node vector of values
28926 new_nod_pt->resize(n_val);
28927 }
28928
28929 } // if (n_val>n_new_val)
28930
28931 } // if (!found_node_in_other_shared_boundaries ||
28932 // !found_on_haloed_element_with_other_processor)
28933
28934 // Is the new node a SolidNode?
28935 SolidNode* solid_nod_pt = dynamic_cast<SolidNode*>(new_nod_pt);
28936 if (solid_nod_pt != 0)
28937 {
28938 unsigned n_solid_val = solid_nod_pt->variable_position_pt()->nvalue();
28939 for (unsigned i_val = 0; i_val < n_solid_val; i_val++)
28940 {
28941 for (unsigned t = 0; t < n_prev; t++)
28942 {
28943 double read_data =
28944 Flat_packed_doubles[Counter_for_flat_packed_doubles++];
28945
28946 // Check if necessary to state all the info. to the node if
28947 // it has been already found in shared boundaries with other
28948 // processors or in the haloed elements with of other
28949 // processors with the iproc processor
28952 {
28953 solid_nod_pt->variable_position_pt()->set_value(
28954 t, i_val, read_data);
28955 } // if (!found_node_in_other_shared_boundaries ||
28956 // !found_on_haloed_element_with_other_processor)
28957 }
28958 }
28959
28960#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28961 oomph_info << "Rec:" << Counter_for_flat_packed_unsigneds
28962 << " Number of values solid node: "
28963 << Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds]
28964 << std::endl;
28965#endif
28966 const unsigned nvalues_solid_node =
28967 Flat_packed_unsigneds[Counter_for_flat_packed_unsigneds++];
28969 for (unsigned i = 0; i < nvalues_solid_node; i++)
28970 {
28972 Flat_packed_doubles[Counter_for_flat_packed_doubles++];
28973 }
28974
28975 // Check if necessary to state all the info. to the node if it
28976 // has been already found in shared boundaries with other
28977 // processors or in the haloed elements with of other processors
28978 // with the iproc processor
28981 {
28982 unsigned index = 0;
28983 solid_nod_pt->read_values_from_vector(values_solid_node, index);
28984 } // if (!found_node_in_other_shared_boundaries ||
28985 // !found_on_haloed_element_with_other_processor)
28986 }
28987
28988 // Get copied history values
28989 // unsigned n_val=new_nod_pt->nvalue();
28990 for (unsigned i_val = 0; i_val < n_val; i_val++)
28991 {
28992 for (unsigned t = 0; t < n_prev; t++)
28993 {
28994 double read_data =
28995 Flat_packed_doubles[Counter_for_flat_packed_doubles++];
28996
28997 // Check if necessary to state all the info. to the node if it
28998 // has been already found in shared boundaries with other
28999 // processors or in the haloed elements with of other
29000 // processors with the iproc processor
29003 {
29004 new_nod_pt->set_value(t, i_val, read_data);
29005 } // if (!found_node_in_other_shared_boundaries ||
29006 // !found_on_haloed_element_with_other_processor)
29007 }
29008 }
29009
29010 // Get copied history values for positions
29011 unsigned n_dim = new_nod_pt->ndim();
29012 for (unsigned idim = 0; idim < n_dim; idim++)
29013 {
29014 for (unsigned t = 0; t < n_prev; t++)
29015 {
29016 double read_data =
29017 Flat_packed_doubles[Counter_for_flat_packed_doubles++];
29018
29019 // Check if necessary to state all the info. to the node if it
29020 // has been already found in shared boundaries with other
29021 // processors or in the haloed elements with of other
29022 // processors with the iproc processor
29025 {
29026 // Copy to coordinate
29027 new_nod_pt->x(t, idim) = read_data;
29028 // DEBP(new_nod_pt->x(t,idim));
29029 } // if (!found_node_in_other_shared_boundaries ||
29030 // !found_on_haloed_element_with_other_processor)
29031 }
29032 }
29033
29034 } // if (is_node_on_shared_boundary != 1)
29035
29036 // If the node was not found in other shared boundaries (possibly
29037 // because it is the first time the node has been sent) then copy
29038 // the node to the shared boundaries where it should be, use the
29039 // special container for this cases
29040 if (n_shd_bnd_with_other_procs_have_node > 0 && // The node is on
29041 // shared
29042 // boundaries with
29043 // other processors
29044 !found_node_in_other_shared_boundaries) // The node has not
29045 // been previously
29046 // set as with
29047 // shared with
29048 // other processors
29049 // (first time)
29050 {
29051 // Update the node pointer in all the references of the node
29052 this->update_other_proc_shd_bnd_node_helper(new_nod_pt,
29061
29062 } // if (!found_node_in_other_shared_boundaries)
29063 }
29064
29065#endif // #ifdef OOMPH_HAS_MPI
29066
29067 //======================================================================
29068 /// Get the nodes on the boundary (b), these are stored in the
29069 /// segment they belong (also used by the load balance method to
29070 /// re-set the number of segments per boundary after load balance has
29071 /// taken place)
29072 //======================================================================
29073 template<class ELEMENT>
29075 const unsigned& b, Vector<Vector<Node*>>& tmp_segment_nodes)
29076 {
29077 // Clear the data structure were to return the nodes
29078 tmp_segment_nodes.clear();
29079
29080 // Temporary storage for face elements
29082
29083 // Temporary storage for number of elements adjacent to the boundary
29084 unsigned nel = 0;
29085
29086 // Temporary storage for elements adjacent to the boundary that have
29087 // a common edge (related with internal boundaries)
29088 unsigned n_repeated_ele = 0;
29089
29090 // Get the number of regions
29091 const unsigned n_regions = this->nregion();
29092
29093 // Temporary storage for already visited pair of nodes (edges)
29095
29096 // Are there more than one region?
29097 if (n_regions > 1)
29098 {
29099 for (unsigned rr = 0; rr < n_regions; rr++)
29100 {
29101 const unsigned region_id =
29102 static_cast<unsigned>(this->Region_attribute[rr]);
29103
29104 // Loop over all elements on boundaries in region rr
29105 const unsigned nel_in_region =
29106 this->nboundary_element_in_region(b, region_id);
29107
29108 // Number of repeated element in region
29109 unsigned nel_repeated_in_region = 0;
29110
29111 // Only bother to do anything else, if there are elements
29112 // associated with the boundary and the current region
29113 if (nel_in_region > 0)
29114 {
29115 // Flag that activates when a repeated face element is found,
29116 // possibly because we are dealing with an internal boundary
29117 bool repeated = false;
29118
29119 // Loop over the bulk elements adjacent to boundary b
29120 for (unsigned e = 0; e < nel_in_region; e++)
29121 {
29122 // Get pointer to the bulk element that is adjacent to boundary b
29124 this->boundary_element_in_region_pt(b, region_id, e);
29125
29126#ifdef OOMPH_HAS_MPI
29127 // In a distributed mesh only work with nonhalo elements
29128 if (this->is_mesh_distributed() && bulk_elem_pt->is_halo())
29129 {
29130 // Increase the number of repeated elements
29132 // Go for the next element
29133 continue;
29134 }
29135#endif
29136
29137 // Find the index of the face of element e along boundary b
29138 int face_index =
29139 this->face_index_at_boundary_in_region(b, region_id, e);
29140
29141 // Before adding the new element we need to be sure that the
29142 // edge that this element represents has not been already
29143 // added
29145 new DummyFaceElement<ELEMENT>(bulk_elem_pt, face_index);
29146
29147 // Number of nodes in the face element
29148 const unsigned n_nodes = tmp_ele_pt->nnode();
29149
29150 std::pair<Node*, Node*> tmp_pair = std::make_pair(
29152
29153 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
29155
29156 // Search for repeated nodes
29157 unsigned n_done_nodes = done_nodes_pt.size();
29158 for (unsigned l = 0; l < n_done_nodes; l++)
29159 {
29160 if (tmp_pair == done_nodes_pt[l] ||
29162 {
29164 repeated = true;
29165 break;
29166 }
29167
29168 } // for (l < n_done_nodes)
29169
29170 // Create new face element?
29171 if (!repeated)
29172 {
29173 // Add the pair of nodes (edge) to the node dones
29174 done_nodes_pt.push_back(tmp_pair);
29175 // Add the face element to the storage
29176 face_el_pt.push_back(tmp_ele_pt);
29177 }
29178 else
29179 {
29180 // Clean up
29181 delete tmp_ele_pt;
29182 tmp_ele_pt = 0;
29183 }
29184
29185 // Re-start
29186 repeated = false;
29187
29188 } // for (e < nel_in_region)
29189
29190 // Add on the number of elements in the boundary with the
29191 // current region
29192 nel += nel_in_region;
29193
29194 // Add on the number of repeated elements
29196
29197 } // if (nel_in_region > 0)
29198
29199 } // for (rr < n_regions)
29200
29201 } // if (n_regions > 1)
29202 // Otherwise it's just the normal boundary functions
29203 else
29204 {
29205 // Assign the number of boundary elements
29206 nel = this->nboundary_element(b);
29207
29208 // Only bother to do anything else, if there are elements
29209 if (nel > 0)
29210 {
29211 // Flag that activates when a repeated face element is found,
29212 // possibly because we are dealing with an internal boundary
29213 bool repeated = false;
29214
29215 // Loop over the bulk elements adjacent to boundary b
29216 for (unsigned e = 0; e < nel; e++)
29217 {
29218 // Get pointer to the bulk element that is adjacent to boundary b
29219 FiniteElement* bulk_elem_pt = this->boundary_element_pt(b, e);
29220
29221#ifdef OOMPH_HAS_MPI
29222 // In a distributed mesh only work with nonhalo elements
29223 if (this->is_mesh_distributed() && bulk_elem_pt->is_halo())
29224 {
29225 // Increase the number of repeated elements
29227 // Go for the next element
29228 continue;
29229 }
29230#endif
29231
29232 // Find the index of the face of element e along boundary b
29233 int face_index = this->face_index_at_boundary(b, e);
29234
29235 // Before adding the new element we need to be sure that the
29236 // edge that this element represent has not been already added
29238 new DummyFaceElement<ELEMENT>(bulk_elem_pt, face_index);
29239
29240 // Number of nodes in the face element
29241 const unsigned n_nodes = tmp_ele_pt->nnode();
29242
29243 std::pair<Node*, Node*> tmp_pair = std::make_pair(
29245
29246 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
29248
29249 // Search for repeated nodes
29250 unsigned n_done_nodes = done_nodes_pt.size();
29251 for (unsigned l = 0; l < n_done_nodes; l++)
29252 {
29253 if (tmp_pair == done_nodes_pt[l] ||
29255 {
29257 repeated = true;
29258 break;
29259 }
29260
29261 } // for (l < n_done_nodes)
29262
29263 // Create new face element
29264 if (!repeated)
29265 {
29266 // Add the pair of nodes (edge) to the node dones
29267 done_nodes_pt.push_back(tmp_pair);
29268 // Add the face element to the storage
29269 face_el_pt.push_back(tmp_ele_pt);
29270 }
29271 else
29272 {
29273 // Free the repeated bulk element!!
29274 delete tmp_ele_pt;
29275 tmp_ele_pt = 0;
29276 }
29277
29278 // Re-start
29279 repeated = false;
29280
29281 } // for (e < nel)
29282
29283 } // if (nel > 0)
29284
29285 } // else if (n_regions > 1)
29286
29287 // Substract the repeated elements
29289
29290#ifdef PARANOID
29291 if (nel != face_el_pt.size())
29292 {
29293 std::ostringstream error_message;
29294 error_message
29295 << "The independet counting of face elements (" << nel << ") for "
29296 << "boundary (" << b << ") is different\n"
29297 << "from the real number of face elements in the container ("
29298 << face_el_pt.size() << ")\n";
29299 throw OomphLibError(
29300 error_message.str(),
29301 "RefineableTriangleMesh::get_boundary_segment_nodes_helper()",
29303 }
29304#endif
29305
29306 // Only bother to do anything else, if there are elements
29307 if (nel > 0)
29308 {
29309 // Assign the number of nonhalo face elements
29310 const unsigned nnon_halo_face_elements = nel;
29311
29312 // The vector of list to store the "segments" that compound the
29313 // boundary (segments may appear only in a distributed mesh)
29315
29316 // Number of already sorted face elements (only nonhalo face
29317 // elements for a distributed mesh)
29318 unsigned nsorted_face_elements = 0;
29319
29320 // Keep track of who's done (in a distributed mesh this apply to
29321 // nonhalo only)
29322 std::map<FiniteElement*, bool> done_ele;
29323
29324 // Keep track of which element is inverted (in distributed mesh
29325 // the elements may be inverted with respect to the segment they
29326 // belong)
29327 std::map<FiniteElement*, bool> is_inverted;
29328
29329 // Iterate until all possible segments have been created. In a non
29330 // distributed mesh there is only one segment which defines the
29331 // complete boundary
29333 {
29334 // The ordered list of face elements (in a distributed mesh a
29335 // collection of continuous face elements define a segment)
29336 std::list<FiniteElement*> sorted_el_pt;
29337
29338#ifdef PARANOID
29339 // Select an initial element for the segment
29340 bool found_initial_face_element = false;
29341#endif
29342
29344
29345 unsigned iface = 0;
29346#ifdef OOMPH_HAS_MPI
29347 if (this->is_mesh_distributed())
29348 {
29349 for (iface = 0; iface < nel; iface++)
29350 {
29352 // If not done then take it as initial face element
29353 if (!done_ele[ele_face_pt])
29354 {
29355#ifdef PARANOID
29356 // Set the flag to indicate the initial element was
29357 // found
29359#endif
29360 // Increase the number of sorted face elements
29362 // Set the index to the next face element
29363 iface++;
29364 // Add the face element in the container
29365 sorted_el_pt.push_back(ele_face_pt);
29366 // Mark as done
29367 done_ele[ele_face_pt] = true;
29368 break;
29369 } // if (!done_el[ele_face_pt])
29370 } // for (iface < nel)
29371 } // if (this->is_mesh_distributed())
29372 else
29373 {
29374#endif // #ifdef OOMPH_HAS_MPI
29375
29376 // When the mesh is not distributed just take the first
29377 // element and put it in the ordered list
29379#ifdef PARANOID
29380 // Set the flag to indicate the initial element was found
29382#endif
29383 // Increase the number of sorted face elements
29385 // Set the index to the next face element
29386 iface = 1;
29387 // Add the face element in the container
29388 sorted_el_pt.push_back(ele_face_pt);
29389 // Mark as done
29390 done_ele[ele_face_pt] = true;
29391#ifdef OOMPH_HAS_MPI
29392 } // else if (this->is_mesh_distributed())
29393#endif
29394
29395#ifdef PARANOID
29397 {
29398 std::ostringstream error_message;
29399 error_message << "Could not find an initial face element for the "
29400 "current segment\n";
29401 throw OomphLibError(
29402 error_message.str(),
29403 "RefineableTriangleMesh::get_boundary_segment_nodes_helper()",
29405 }
29406#endif
29407
29408 // Number of nodes in the face element
29409 const unsigned nnod = ele_face_pt->nnode();
29410
29411 // Left and rightmost nodes (the left and right nodes of the
29412 // current face element)
29415
29416 // Continue iterating if a new face element has been added to
29417 // the list
29418 bool face_element_added = false;
29419
29420 // While a new face element has been added to the set of sorted
29421 // face elements continue iterating
29422 do
29423 {
29424 // Start from the next face element since we have already
29425 // added the previous one as the initial face element (any
29426 // previous face element had to be added on previous
29427 // iterations)
29428 for (unsigned iiface = iface; iiface < nel; iiface++)
29429 {
29430 // Re-start flag
29431 face_element_added = false;
29432
29433 // Get the candidate element
29435
29436 // Check that the candidate element has not been done and is
29437 // not a halo element
29438 if (!done_ele[ele_face_pt])
29439 {
29440 // Get the left and right nodes of the current element
29443
29444 // New element fits at the left of segment and is not inverted
29446 {
29448 sorted_el_pt.push_front(ele_face_pt);
29449 is_inverted[ele_face_pt] = false;
29450 face_element_added = true;
29451 }
29452 // New element fits at the left of segment and is inverted
29453 else if (left_node_pt == local_left_node_pt)
29454 {
29456 sorted_el_pt.push_front(ele_face_pt);
29457 is_inverted[ele_face_pt] = true;
29458 face_element_added = true;
29459 }
29460 // New element fits on the right of segment and is not inverted
29462 {
29464 sorted_el_pt.push_back(ele_face_pt);
29465 is_inverted[ele_face_pt] = false;
29466 face_element_added = true;
29467 }
29468 // New element fits on the right of segment and is inverted
29470 {
29472 sorted_el_pt.push_back(ele_face_pt);
29473 is_inverted[ele_face_pt] = true;
29474 face_element_added = true;
29475 }
29476
29478 {
29479 // Mark the face element as done
29480 done_ele[ele_face_pt] = true;
29482 break;
29483 }
29484
29485 } // if (!done_el[ele_face_pt])
29486
29487 } // for (iiface<nnon_halo_face_element)
29488
29489 } while (face_element_added &&
29491
29492 // Store the created segment in the vector of segments
29494
29495 } // while(nsorted_face_elements < nnon_halo_face_elements);
29496
29497 // The number of boundary segments in this processor
29498 const unsigned nsegments = segment_sorted_ele_pt.size();
29499
29500#ifdef PARANOID
29501 if (nnon_halo_face_elements > 0 && nsegments == 0)
29502 {
29503 std::ostringstream error_message;
29504 error_message
29505 << "The number of segments is zero, but the number of nonhalo\n"
29506 << "elements is: (" << nnon_halo_face_elements << ")\n";
29507 throw OomphLibError(
29508 error_message.str(),
29509 "RefineableTriangleMesh::get_boundary_segment_nodes_helper()",
29511 } // if (nnon_halo_face_elements > 0 && nsegments == 0)
29512#endif
29513
29514 // Go through all the segments, visit each face element in order
29515 // and get the nodes based that represent the boundary segment
29516
29517 // Resize the container to store the nodes with the required
29518 // number of segments
29519 tmp_segment_nodes.resize(nsegments);
29520
29521 for (unsigned is = 0; is < nsegments; is++)
29522 {
29523#ifdef PARANOID
29524 if (segment_sorted_ele_pt[is].size() == 0)
29525 {
29526 std::ostringstream error_message;
29527 error_message << "The (" << is << ")-th segment has no elements\n";
29528 throw OomphLibError(
29529 error_message.str(),
29530 "RefineableTriangleMesh::get_boundary_segment_nodes_helper()",
29532 } // if (segment_sorted_ele_pt[is].size() == 0)
29533#endif
29534
29535 // Get access to the first element on the segment
29537
29538 // Number of nodes
29539 const unsigned nnod = first_ele_pt->nnode();
29540
29541 // Get the first node of the current segment
29544 {
29546 }
29547
29548 // Add the node to the corresponding segment
29550
29551 // Now loop over face elements in order to get the nodes
29552 for (std::list<FiniteElement*>::iterator it =
29554 it != segment_sorted_ele_pt[is].end();
29555 it++)
29556 {
29557 // Get element
29559
29560 // The last node pointer
29561 Node* last_node_pt = 0;
29562
29563 // Get the last node
29564 if (!is_inverted[ele_pt])
29565 {
29567 }
29568 else
29569 {
29571 }
29572
29573 // Add the node to the corresponding segment
29574 tmp_segment_nodes[is].push_back(last_node_pt);
29575
29576 } // iterator over the elements in the segment
29577
29578 } // for (is < nsegments)
29579
29580 } // for (if (nel > 0))
29581
29582 // Free memory allocation
29583 for (unsigned e = 0; e < nel; e++)
29584 {
29585 delete face_el_pt[e];
29586 face_el_pt[e] = 0;
29587 } // for (e < nel)
29588 }
29589
29590 //======================================================================
29591 /// Adapt problem based on specified elemental error estimates
29592 /// This function implement serial and parallel mesh adaptation, the
29593 /// sections for parallel mesh adaptation are clearly identified by
29594 /// checking whether the mesh is distributed or not
29595 //======================================================================
29596 template<class ELEMENT>
29598 {
29600
29601 // ==============================================================
29602 // BEGIN: Compute target areas
29603 // ==============================================================
29604
29605 // Get refinement targets
29607 double min_angle = compute_area_target(elem_error, target_area);
29608
29609 // Post-process to allow only quantised target areas
29610 // in an attempt to more closely mimick the structured
29611 // case and limit the diffusion of small elements.
29612 bool quantised_areas = true;
29613 if (quantised_areas)
29614 {
29615 unsigned n = target_area.size();
29616 double total_area = 0;
29617 // If the mesh is distributed then we need to get the contribution
29618 // of all processors to compute the total areas
29619 // ------------------------------------------
29620 // DISTRIBUTED MESH: BEGIN
29621 // ------------------------------------------
29622#ifdef OOMPH_HAS_MPI
29623 if (this->is_mesh_distributed())
29624 {
29625 // When working in parallel we get the total area from the sum
29626 // of the the sub-areas of all the meshes
29627 double sub_area = 0.0;
29628
29629 // Only add the area of nonhalo elements
29630 for (unsigned e = 0; e < n; e++)
29631 {
29632 // Get the pointer to the element
29633 FiniteElement* ele_pt = this->finite_element_pt(e);
29634 if (!ele_pt->is_halo())
29635 {
29636 sub_area += ele_pt->size();
29637 }
29638 } // for (e<n)
29639
29640 // Get the communicator of the mesh
29641 OomphCommunicator* comm_pt = this->communicator_pt();
29642
29643 // Get the total area
29645 &sub_area, &total_area, 1, MPI_DOUBLE, MPI_SUM, comm_pt->mpi_comm());
29646 }
29647 else
29648 {
29649 for (unsigned e = 0; e < n; e++)
29650 {
29651 total_area += this->finite_element_pt(e)->size();
29652 }
29653 }
29654 // ------------------------------------------
29655 // DISTRIBUTED MESH: END
29656 // ------------------------------------------
29657#else // #ifdef OOMPH_HAS_MPI
29658 for (unsigned e = 0; e < n; e++)
29659 {
29660 total_area += this->finite_element_pt(e)->size();
29661 }
29662#endif // #ifdef OOMPH_HAS_MPI
29663
29664 for (unsigned e = 0; e < n; e++)
29665 {
29666 unsigned level =
29667 unsigned(ceil(log(target_area[e] / total_area) / log(1.0 / 3.0))) - 1;
29668 double new_target_area = total_area * pow(1.0 / 3.0, int(level));
29670 }
29671 }
29672
29673 // std::ofstream tmp;
29674 // tmp.open((Global_string_for_annotation::
29675 // String[0]+"overall_target_areas"+
29676 // StringConversion::to_string(Global_unsigned::Number)+".dat").c_str());
29677
29678 // Get maximum target area
29679 unsigned n = target_area.size();
29680 double max_area = 0.0;
29681 double min_area = DBL_MAX;
29682 for (unsigned e = 0; e < n; e++)
29683 {
29686
29687 // tmp << (finite_element_pt(e)->node_pt(0)->x(0)+
29688 // finite_element_pt(e)->node_pt(1)->x(0)+
29689 // finite_element_pt(e)->node_pt(2)->x(0))/3.0 << " "
29690 // << (finite_element_pt(e)->node_pt(0)->x(1)+
29691 // finite_element_pt(e)->node_pt(1)->x(1)+
29692 // finite_element_pt(e)->node_pt(2)->x(1))/3.0 << " "
29693 // << target_area[e] << " "
29694 // << finite_element_pt(e)->size() << " "
29695 // << elem_error[e] << " " << std::endl;
29696 }
29697
29698 // tmp.close();
29699
29700 oomph_info << "Maximum target area: " << max_area << std::endl;
29701 oomph_info << "Minimum target area: " << min_area << std::endl;
29702 oomph_info << "Number of elements to be refined: " << this->Nrefined
29703 << std::endl;
29704 oomph_info << "Number of elements to be unrefined: " << this->Nunrefined
29705 << std::endl;
29706 oomph_info << "Min. angle: " << min_angle << std::endl;
29707
29709 this->max_and_min_element_size(orig_max_area, orig_min_area);
29710 oomph_info << "Max./min. element size in original mesh: " << orig_max_area
29711 << " " << orig_min_area << std::endl;
29712
29713 // ==============================================================
29714 // END: Compute target areas
29715 // ==============================================================
29716
29717 // Check if boundaries need to be updated (regardless of
29718 // requirements of bulk error estimator) but don't do anything!
29719 bool check_only = true;
29723
29724 // Get the number of outer boundaries and check if they require
29725 // update
29726 const unsigned nouter = this->Outer_boundary_pt.size();
29727
29728 if (this->is_automatic_creation_of_vertices_on_boundaries_allowed())
29729 {
29730 // loop over the outer boundaries
29731 for (unsigned i_outer = 0; i_outer < nouter; i_outer++)
29732 {
29733 outer_boundary_update_necessary = this->update_polygon_using_face_mesh(
29734 this->Outer_boundary_pt[i_outer], check_only);
29735 // Break the loop if at least one needs updating
29737 }
29738
29739 // Do not waste time if we already know that it is necessary an update
29740 // on the boundary representation
29742 {
29743 // Check if we need to generate a new 1D mesh representation of
29744 // the inner hole boundaries
29745 const unsigned nhole = this->Internal_polygon_pt.size();
29748 this->surface_remesh_for_inner_hole_boundaries(internal_point_coord,
29749 check_only);
29750
29751 // If there was not necessary a change even on the internal closed
29752 // curve then finally check for the open curves as well
29754 {
29755 const unsigned n_open_polyline = this->Internal_open_curve_pt.size();
29756 // loop over the open polylines
29757 for (unsigned i = 0; i < n_open_polyline; i++)
29758 {
29760 this->update_open_curve_using_face_mesh(
29761 this->Internal_open_curve_pt[i], check_only);
29762 // If at least one needs modification then break the for loop
29764 }
29765 }
29766 }
29767 }
29768
29769 // Flag to indicate whether we need to adapt or not (for parallel
29770 // mesh adaptation only)
29771 int adapt_all = 0;
29772 // ------------------------------------------
29773 // DISTRIBUTED MESH: BEGIN
29774 // ------------------------------------------
29775#ifdef OOMPH_HAS_MPI
29776 // When working in distributed meshes we need to ensure that all the
29777 // processors take part on the adaptation process. If at least one
29778 // of the processors requires adaptation then all processor take
29779 // part on the adaptation process.
29780 int adapt_this_processor = 0;
29781 if (this->is_mesh_distributed())
29782 {
29783 // Do this processor requires adaptation?
29784 if ((Nrefined > 0) || (Nunrefined > max_keep_unrefined()) ||
29785 (min_angle < min_permitted_angle()) ||
29789 {
29791 }
29792
29793 // Get the communicator of the mesh
29794 OomphCommunicator* comm_pt = this->communicator_pt();
29795
29796 // Verify if at least one processor needs mesh adaptation
29798 &adapt_all,
29799 1,
29800 MPI_INT,
29801 MPI_SUM,
29802 comm_pt->mpi_comm());
29803 }
29804#endif
29805 // ------------------------------------------
29806 // DISTRIBUTED MESH: END
29807 // ------------------------------------------
29808
29809 // Should we bother to adapt?
29810 if ((Nrefined > 0) || (Nunrefined > max_keep_unrefined()) ||
29811 (min_angle < min_permitted_angle()) ||
29815 {
29816 if (!((Nrefined > 0) || (Nunrefined > max_keep_unrefined())))
29817 {
29821 {
29823 << "Mesh regeneration triggered by inaccurate interface/surface\n"
29824 << "representation; setting Nrefined to number of elements.\n"
29825 << "outer_boundary_update_necessary : "
29827 << "inner_boundary_update_necessary : "
29829 << "inner_open_boundary_update_necessary: "
29831 Nrefined = nelement();
29832 }
29833 else
29834 {
29835 oomph_info << "Mesh regeneration triggered by min angle criterion;\n"
29836 << "setting Nrefined to number of elements.\n";
29837 Nrefined = nelement();
29838 }
29839 }
29840
29841 // ------------------------------------------
29842 // DISTRIBUTED MESH: BEGIN
29843 // ------------------------------------------
29844#ifdef OOMPH_HAS_MPI
29845 else if (this->is_mesh_distributed() && adapt_this_processor == 0 &&
29846 adapt_all > 0)
29847 {
29848 oomph_info << "Mesh regeneration triggered by (" << adapt_all
29849 << ") processor(s) "
29850 << "that require(s)\n adaptation\n";
29851 }
29852#endif
29853 // ------------------------------------------
29854 // DISTRIBUTED MESH: END
29855 // ------------------------------------------
29856
29857 // ==============================================================
29858 // BEGIN: Updating of boundaries representation (unrefinement and
29859 // refinement of polylines)
29860 // ==============================================================
29861
29862 // Add the initial and final vertices of the polylines that
29863 // present connections to a list of non-delete-able vertices. The
29864 // vertices where the connections are performed cannot be deleted
29865 add_vertices_for_non_deletion();
29866
29867 // ------------------------------------------
29868 // DISTRIBUTED MESH: BEGIN
29869 // ------------------------------------------
29870#ifdef OOMPH_HAS_MPI
29871 // Synchronise connections for shared boundaries among
29872 // processors. This is required since one of the processor may noy
29873 // know that some of its shared boundaries have connections, thus
29874 // the vertices receiving the connections cannot be deleted
29875 if (this->is_mesh_distributed())
29876 {
29877 synchronize_shared_boundary_connections();
29878 }
29879#endif // #ifdef OOMPH_HAS_MPI
29880 // ------------------------------------------
29881 // DISTRIBUTED MESH: END
29882 // ------------------------------------------
29883
29884 // Are we allowing automatic insertion of vertices on boundaries?
29885 // If YES then Triangle automatically insert points along
29886 // boundaries, if NOT, then points are inserted along the
29887 // boundaries based on the target areas of boundary elements. When
29888 // the mesh is distributed the automatic insertion of vertices by
29889 // Triangle along the boundaries is not allowed
29890 if (this->is_automatic_creation_of_vertices_on_boundaries_allowed())
29891 {
29892 // Generate a new 1D mesh representation of the inner hole boundaries
29893 unsigned nhole = this->Internal_polygon_pt.size();
29895 this->surface_remesh_for_inner_hole_boundaries(internal_point_coord);
29896
29897 // Update the representation of the outer boundary
29898 for (unsigned i_outer = 0; i_outer < nouter; i_outer++)
29899 {
29900 this->update_polygon_using_face_mesh(
29901 this->Outer_boundary_pt[i_outer]);
29902 }
29903
29904 // After updating outer and internal closed boundaries it is also
29905 // necessary to update internal boundaries.
29906 unsigned n_open_polyline = this->Internal_open_curve_pt.size();
29907 for (unsigned i = 0; i < n_open_polyline; i++)
29908 {
29909 this->update_open_curve_using_face_mesh(
29910 this->Internal_open_curve_pt[i]);
29911 }
29912 }
29913 else
29914 {
29915 // Update the representation of the internal boundaries using
29916 // the element's target area
29917
29918 // Get the number of interal polygons
29919 const unsigned ninternal = this->Internal_polygon_pt.size();
29920 for (unsigned i_internal = 0; i_internal < ninternal; i_internal++)
29921 {
29922 this->update_polygon_using_elements_area(
29923 this->Internal_polygon_pt[i_internal], target_area);
29924 }
29925
29926 // Update the representation of the outer boundaries using the
29927 // element's target area
29928 for (unsigned i_outer = 0; i_outer < nouter; i_outer++)
29929 {
29930 this->update_polygon_using_elements_area(
29931 this->Outer_boundary_pt[i_outer], target_area);
29932 }
29933
29934 // Update the representation of the internal open boundaries
29935 // using the element's target areas
29936 const unsigned n_open_polyline = this->Internal_open_curve_pt.size();
29937 for (unsigned i = 0; i < n_open_polyline; i++)
29938 {
29939 this->update_open_curve_using_elements_area(
29940 this->Internal_open_curve_pt[i], target_area);
29941 }
29942
29943 // ------------------------------------------
29944 // DISTRIBUTED MESH: BEGIN
29945 // ------------------------------------------
29946
29947 // When working with a distributed mesh we require to update the
29948 // boundary representation of the shared boundaries, this is
29949 // based on the target areas of the elements adjaced to the
29950 // shared boundaries
29951#ifdef OOMPH_HAS_MPI
29952 // Update shared boundaries if the mesh is distributed
29953 if (this->is_mesh_distributed())
29954 {
29955 // Get the rank of the current processor
29956 const unsigned my_rank = this->communicator_pt()->my_rank();
29957
29958 // Get the number of shared curves
29959 const unsigned n_curves = this->nshared_boundary_curves(my_rank);
29960 // Loop over the shared curves in the current processor
29961 for (unsigned nc = 0; nc < n_curves; nc++)
29962 {
29963 // Update the shared polyline
29964 this->update_shared_curve_using_elements_area(
29965 this->Shared_boundary_polyline_pt[my_rank][nc], // shared_curve,
29966 target_area);
29967 }
29968
29969 } // if (this->is_mesh_distributed())
29970#endif
29971
29972 // ------------------------------------------
29973 // DISTRIBUTED MESH: END
29974 // ------------------------------------------
29975
29976 } // else if
29977 // (this->is_automatic_creation_of_vertices_on_boundaries_allowed())
29978
29979 // ==============================================================
29980 // END: Updating of boundaries representation (unrefinement and
29981 // refinement of polylines)
29982 // ==============================================================
29983
29984 // ==============================================================
29985 // BEGIN: Reset boundary coordinates for boundaries with no
29986 // associated GeomObject
29987 // ==============================================================
29988
29989 // If there is not a geometric object associated with the boundary
29990 // then reset the boundary coordinates so that the lengths are
29991 // consistent in the new mesh and the old mesh.
29992 const unsigned n_boundary = this->nboundary();
29993
29996
29997 // ------------------------------------------
29998 // DISTRIBUTED MESH: BEGIN
29999 // ------------------------------------------
30000#ifdef OOMPH_HAS_MPI
30001 // Clear storage for assignment of initial zeta values for
30002 // boundaries
30003 if (this->is_mesh_distributed())
30004 {
30005 this->Assigned_segments_initial_zeta_values.clear();
30006 }
30007#endif // #ifdef OOMPH_HAS_MPI
30008 // ------------------------------------------
30009 // DISTRIBUTED MESH: END
30010 // ------------------------------------------
30011
30012 // Loop over the boundaries to assign boundary coordinates
30013 for (unsigned b = 0; b < n_boundary; ++b)
30014 {
30015 // ------------------------------------------
30016 // DISTRIBUTED MESH: BEGIN
30017 // ------------------------------------------
30018#ifdef OOMPH_HAS_MPI
30019 if (this->is_mesh_distributed())
30020 {
30021 // In a distributed mesh, the boundaries may have been split
30022 // across processors during the distribution process, thus we
30023 // need to compute the connectivity among the segments of the
30024 // boundary to correctly assign its boundary coordinates
30025 this->compute_boundary_segments_connectivity_and_initial_zeta_values(
30026 b);
30027 }
30028#endif
30029 // ------------------------------------------
30030 // DISTRIBUTED MESH: END
30031 // ------------------------------------------
30032
30033 // Does the boundary has an associated GeomObject
30034 if (this->boundary_geom_object_pt(b) == 0)
30035 {
30036 this->template setup_boundary_coordinates<ELEMENT>(b);
30037 }
30038
30039 // ------------------------------------------
30040 // DISTRIBUTED MESH: BEGIN
30041 // ------------------------------------------
30042#ifdef OOMPH_HAS_MPI
30043 if (this->is_mesh_distributed())
30044 {
30045 // Synchronise boundary coordinates for internal open curves,
30046 // also establish the boundary coordinates for the nodes on
30047 // the corners of elements not on the boundary
30048 this->synchronize_boundary_coordinates(b);
30049 }
30050#endif
30051 // ------------------------------------------
30052 // DISTRIBUTED MESH: END
30053 // ------------------------------------------
30054
30055 } // for (b<n_boundary)
30056
30059
30060 // ==============================================================
30061 // END: Reset boundary coordinates for boundaries with no
30062 // associated GeomObject
30063 // ==============================================================
30064
30065 // ------------------------------------------
30066 // DISTRIBUTED MESH: BEGIN
30067 // ------------------------------------------
30068#ifdef OOMPH_HAS_MPI
30069 // ==============================================================
30070 // BEGIN: Create the new representation of the domain by joining
30071 // the original boundaries and the shared boundaries.
30072 // ==============================================================
30073
30074 // Storage for the new temporary polygons "closed" by the shared
30075 // boundaries
30077
30078 // Storage for the new temporary open curves, could be the
30079 // original open curves or "chunks" of the original open curves
30080 // not overlapped by shared boundaries
30082
30083 if (this->is_mesh_distributed())
30084 {
30085 // Create the new polygons and open curves with help of the
30086 // original polylines and shared polylines
30087 this->create_distributed_domain_representation(tmp_outer_polygons_pt,
30089
30090 // Create the connections of the temporary domain representations
30091 this->create_temporary_boundary_connections(tmp_outer_polygons_pt,
30093 }
30094 // ==============================================================
30095 // END: Create the new representation of the domain by joining
30096 // the original boundaries and the shared boundaries.
30097 // ==============================================================
30098#endif
30099 // ------------------------------------------
30100 // DISTRIBUTED MESH: END
30101 // ------------------------------------------
30102
30103 // Re-establish polylines' connections. The boundary
30104 // representation has changed (new polylines), therefore we need
30105 // to update the connection information
30108 restore_boundary_connections(resume_initial_connection_polyline_pt,
30110
30111 // Update the region information by setting the coordinates from the
30112 // centroid of the first element in each region (which should allow
30113 // automatic updates when the regions deform)
30114 {
30115 unsigned n_region = this->nregion();
30116 if (n_region > 1)
30117 {
30118 for (std::map<unsigned, Vector<double>>::iterator it =
30119 this->Regions_coordinates.begin();
30120 it != this->Regions_coordinates.end();
30121 ++it)
30122 {
30123 // Storage for the approximate centroid
30124 Vector<double> centroid(2, 0.0);
30125
30126 // Get the region id
30127 unsigned region_id = it->first;
30128
30129 // Report information
30130 oomph_info << "Region " << region_id << ": " << it->second[0] << " "
30131 << it->second[1] << " ";
30132
30133 // Check that there is at least one element in the region
30134 unsigned n_region_element = this->nregion_element(region_id);
30135 if (n_region_element > 0)
30136 {
30137 // Cache pointer to the first element
30138 FiniteElement* const elem_pt =
30139 this->region_element_pt(region_id, 0);
30140
30141 // Loop over the corners of the triangle and average
30142 for (unsigned n = 0; n < 3; n++)
30143 {
30144 Node* const nod_pt = elem_pt->node_pt(n);
30145 for (unsigned i = 0; i < 2; i++)
30146 {
30147 centroid[i] += nod_pt->x(i);
30148 }
30149 }
30150 for (unsigned i = 0; i < 2; i++)
30151 {
30152 centroid[i] /= 3;
30153 }
30154 // Now we have the centroid set it
30155 it->second = centroid;
30156
30157 oomph_info << " , " << it->second[0] << " " << it->second[1]
30158 << std::endl;
30159 } // end of case when there is at least one element
30160
30161 } // loop over regions coordinates
30162
30163 } // if(n_region > 1)
30164
30165 } // Updating region info.
30166
30167 // ==============================================================
30168 // BEGIN: Create background mesh
30169 // ==============================================================
30170
30171 // Are we dealing with a solid mesh?
30172 SolidMesh* solid_mesh_pt = dynamic_cast<SolidMesh*>(this);
30173
30174 // Build temporary uniform background mesh
30175 //----------------------------------------
30176 // with area set by maximum required area
30177 //---------------------------------------
30179
30180 // The storage for the new temporary boundaries representation to
30181 // create the background mesh
30185
30186#ifdef OOMPH_HAS_MPI
30187 if (!this->is_mesh_distributed())
30188#endif
30189 {
30190 // Copy the outer boundaries
30191 closed_curve_pt.resize(nouter);
30192 for (unsigned i = 0; i < nouter; i++)
30193 {
30194 closed_curve_pt[i] = this->Outer_boundary_pt[i];
30195 }
30196
30197 // Copy the internal closed boundaries (may be holes)
30198 const unsigned n_holes = this->Internal_polygon_pt.size();
30199 hole_pt.resize(n_holes);
30200 for (unsigned i = 0; i < n_holes; i++)
30201 {
30202 hole_pt[i] = this->Internal_polygon_pt[i];
30203 }
30204
30205 // Copy the internal open curves
30206 const unsigned n_open_curves = this->Internal_open_curve_pt.size();
30208 for (unsigned i = 0; i < n_open_curves; i++)
30209 {
30210 open_curves_pt[i] = this->Internal_open_curve_pt[i];
30211 }
30212 }
30213 // ------------------------------------------
30214 // DISTRIBUTED MESH: BEGIN
30215 // ------------------------------------------
30216#ifdef OOMPH_HAS_MPI
30217 else
30218 {
30219 // Copy the new representation of the outer/internal closed
30220 // boundaries
30221 const unsigned n_tmp_outer = tmp_outer_polygons_pt.size();
30223 for (unsigned i = 0; i < n_tmp_outer; i++)
30224 {
30226 }
30227
30228 // Copy the new representation of the internal open curves
30229 const unsigned n_open_curves = tmp_open_curves_pt.size();
30231 for (unsigned i = 0; i < n_open_curves; i++)
30232 {
30234 }
30235 }
30236#endif
30237 // ------------------------------------------
30238 // DISTRIBUTED MESH: END
30239 // ------------------------------------------
30240
30241 // ----------------------------------------------------------------
30242 // Gather all the information and use the TriangleMeshParameters
30243 // object which help us on the manage of all TriangleMesh object's
30244 // information
30245
30246 // Create the TriangleMeshParameters objects with the outer boundary
30247 // as the only one parameter
30249
30250 // Pass information about the holes
30251 triangle_mesh_parameters.internal_closed_curve_pt() = hole_pt;
30252
30253 // Pass information about the internal open boundaries
30254 triangle_mesh_parameters.internal_open_curves_pt() = open_curves_pt;
30255
30256 // Set the element area
30257 triangle_mesh_parameters.element_area() = max_area;
30258
30259 // Pass information about the extra holes (not defined with closed
30260 // boundaries)
30261 triangle_mesh_parameters.extra_holes_coordinates() =
30262 this->Extra_holes_coordinates;
30263
30264 // Pass information about regions
30265 triangle_mesh_parameters.regions_coordinates() =
30266 this->Regions_coordinates;
30267
30268 // Pass information about the using of regions
30269 if (this->Use_attributes)
30270 {
30271 triangle_mesh_parameters.enable_use_attributes();
30272 }
30273
30274 // Pass information about allowing the creation of new points
30275 if (!this->is_automatic_creation_of_vertices_on_boundaries_allowed())
30276 {
30278 .disable_automatic_creation_of_vertices_on_boundaries();
30279 }
30280
30281 // When the mesh is distributed we need to create a distributed
30282 // background mesh
30283#ifdef OOMPH_HAS_MPI
30284 if (this->is_mesh_distributed())
30285 {
30286 // Mark the mesh to be created as distributed by passing a
30287 // pointer to the communicator
30288 triangle_mesh_parameters.set_communicator_pt(this->communicator_pt());
30289 }
30290#endif
30291
30292 // ----------------------------------------------------------
30293 // Build the background mesh using Triangle
30294 // ----------------------------------------------------------
30296
30297 if (solid_mesh_pt != 0)
30298 {
30300 triangle_mesh_parameters, this->Time_stepper_pt);
30301 }
30302 else
30303 {
30305 triangle_mesh_parameters, this->Time_stepper_pt);
30306 }
30307
30308 if (Print_timings_level_adaptation > 2)
30309 {
30310 oomph_info << "CPU for building background mesh: "
30312 << std::endl;
30313 }
30314
30315 // Pass the info. regarding the maximum and minimum element size
30316 // from the old mesh to the background mesh
30317 const double this_max_element_size = this->max_element_size();
30318 const double this_min_element_size = this->min_element_size();
30319 tmp_new_mesh_pt->max_element_size() = this_max_element_size;
30320 tmp_new_mesh_pt->min_element_size() = this_min_element_size;
30321
30322 // ... also copy the minimum permitted angle
30323 const double this_min_permitted_angle = this->min_permitted_angle();
30324 tmp_new_mesh_pt->min_permitted_angle() = this_min_permitted_angle;
30325
30326 // ------------------------------------------
30327 // DISTRIBUTED MESH: BEGIN
30328 // ------------------------------------------
30329#ifdef OOMPH_HAS_MPI
30330 // If the mesh is distributed we need to pass and set the
30331 // information of internal boundaries overlaped by shared
30332 // boundaries
30333 if (this->is_mesh_distributed())
30334 {
30335 // Check if necessary to fill boundary elements for those
30336 // internal boundaries that overlap shared boundaries
30337 if (this->nshared_boundary_overlaps_internal_boundary() > 0)
30338 {
30339 // Copy the data structures that indicates which shared
30340 // boundaries are part of an internal boundary
30341 tmp_new_mesh_pt->shared_boundary_overlaps_internal_boundary() =
30342 this->shared_boundary_overlaps_internal_boundary();
30343
30344 // Copy the data structure that indicates which are the shared
30345 // boundaries in each processor
30346 tmp_new_mesh_pt->shared_boundaries_ids() =
30347 this->shared_boundaries_ids();
30348
30349 // Fill the structures for the boundary elements and face indexes
30350 // of the boundary elements
30352 ->fill_boundary_elements_and_nodes_for_internal_boundaries();
30353
30354 } // if (this->nshared_boundary_overlaps_internal_boundary() > 0)
30355
30356 } // if (this->is_mesh_distributed())
30357#endif // #ifdef OOMPH_HAS_MPI
30358 // ------------------------------------------
30359 // DISTRIBUTED MESH: END
30360 // ------------------------------------------
30361
30362 // Snap to curvilinear boundaries (some code duplication as this
30363 // is repeated below but helper function would take so many
30364 // arguments that it's nearly as messy...
30365
30366 // Pass the boundary geometric objects to the new mesh
30367 tmp_new_mesh_pt->boundary_geom_object_pt() =
30368 this->boundary_geom_object_pt();
30369
30370 // Reset the boundary coordinates if there is
30371 // a geometric object associated with the boundary
30372 tmp_new_mesh_pt->boundary_coordinate_limits() =
30373 this->boundary_coordinate_limits();
30374
30377
30378 for (unsigned b = 0; b < n_boundary; b++)
30379 {
30380 // ------------------------------------------
30381 // DISTRIBUTED MESH: BEGIN
30382 // ------------------------------------------
30383#ifdef OOMPH_HAS_MPI
30384 if (this->is_mesh_distributed())
30385 {
30386 // Identify the segments of the new mesh with the ones of the
30387 // original mesh
30389 ->identify_boundary_segments_and_assign_initial_zeta_values(b,
30390 this);
30391 }
30392#endif
30393 // ------------------------------------------
30394 // DISTRIBUTED MESH: END
30395 // ------------------------------------------
30396
30397 // Setup boundary coordinates for boundaries with GeomObject
30398 // associated
30399 if (tmp_new_mesh_pt->boundary_geom_object_pt(b) != 0)
30400 {
30402 }
30403 }
30404
30407
30409 // Move the nodes on the new boundary onto the old curvilinear
30410 // boundary. If the boundary is straight this will do precisely
30411 // nothing but will be somewhat inefficient
30412 for (unsigned b = 0; b < n_boundary; b++)
30413 {
30414 this->snap_nodes_onto_boundary(tmp_new_mesh_pt, b);
30415 }
30416
30417 const double t_total_snap_nodes_bg_mesh =
30419
30420 if (Print_timings_level_adaptation > 2)
30421 {
30422 oomph_info << "CPU for snapping nodes onto boundaries "
30423 << "(background mesh): " << t_total_snap_nodes_bg_mesh
30424 << std::endl;
30425 }
30426
30427 // Update mesh further?
30428 if (Mesh_update_fct_pt != 0)
30429 {
30430 Mesh_update_fct_pt(tmp_new_mesh_pt);
30431 }
30432
30433 // If we have a continuation problem
30434 // any problem in which the timestepper is a "generalisedtimestepper",
30435 // which will have been set by the problem, then ensure
30436 // all data in the new mesh has the appropriate timestepper
30437 /*if(dynamic_cast<GeneralisedTimeStepper*>(this->Time_stepper_pt))
30438 {
30439 tmp_new_mesh_pt->set_nodal_and_elemental_time_stepper(
30440 this->Time_stepper_pt);
30441 tmp_new_mesh_pt->set_mesh_level_time_stepper(this->Time_stepper_pt);
30442 }*/
30443
30444 // tmp_new_mesh_pt->output("mesh_nodes_snapped_0.dat");
30445 // this->output("existing_mesh.dat");
30446
30447 // ==============================================================
30448 // END: Create background mesh
30449 // ==============================================================
30450
30451 // ==============================================================
30452 // BEGIN: Transferring of target areas and creation of new mesh
30453 // ==============================================================
30454
30455 // Get the TriangulateIO object associated with that mesh
30457 tmp_new_mesh_pt->triangulateio_representation();
30459
30460 // If the mesh is a solid mesh then do the mapping based on the
30461 // Eulerian coordinates
30462 bool use_eulerian_coords = false;
30463 if (solid_mesh_pt != 0)
30464 {
30465 use_eulerian_coords = true;
30466 }
30467
30468#ifdef OOMPH_HAS_CGAL
30469
30470 // Make cgal-based bin
30473 {
30474 cgal_params.enable_use_eulerian_coordinates_during_setup();
30475 }
30477
30478#else
30479
30480 // Make nonrefineable bin
30483 {
30484 params.enable_use_eulerian_coordinates_during_setup();
30485 }
30487 bin_dim[0] = Nbin_x_for_area_transfer;
30488 bin_dim[1] = Nbin_y_for_area_transfer;
30489 params.dimensions_of_bin_array() = bin_dim;
30491
30492#endif
30493
30494 // Set up a map from pointer to element to its number
30495 // in the mesh
30496 std::map<GeneralisedElement*, unsigned> element_number;
30497 unsigned nelem = this->nelement();
30498 for (unsigned e = 0; e < nelem; e++)
30499 {
30500 element_number[this->element_pt(e)] = e;
30501 }
30502
30503#ifndef OOMPH_HAS_CGAL
30504
30505 // Create a vector to store the min target area of each bin (at
30506 // this stage the number of bins should not be that large, so it
30507 // should be safe to build a vector for the total number of bins)
30509
30510 // Get pointer to sample point container
30512 dynamic_cast<NonRefineableBinArray*>(
30513 mesh_geom_obj_pt->sample_point_container_pt());
30514 if (bin_array_pt == 0)
30515 {
30516 throw OomphLibError(
30517 "Sample point container has to be NonRefineableBinArray",
30520 }
30521
30522 {
30523 unsigned n_bin = 0;
30524 unsigned max_n_entry = 0;
30525 unsigned min_n_entry = UINT_MAX;
30526 unsigned tot_n_entry = 0;
30527 unsigned n_empty = 0;
30528 bin_array_pt->get_fill_stats(
30530
30531 oomph_info << "Before bin diffusion:"
30532 << " nbin:(" << n_bin << ")"
30533 << " nempty:(" << n_empty << ")"
30534 << " min:(" << min_n_entry << ")"
30535 << " max:(" << max_n_entry << ")"
30536 << " average entries:("
30537 << double(tot_n_entry) / double(n_bin) << ")" << std::endl;
30538 }
30539
30540 // Fill bin by diffusion
30542 oomph_info << "Going into diffusion bit...\n";
30543 bin_array_pt->fill_bin_by_diffusion();
30544 oomph_info << "Back from diffusion bit...\n";
30545 oomph_info << "Time for bin diffusion: "
30546 << TimingHelpers::timer() - t0_bin_diff << std::endl;
30547
30548 // Do some stats
30549 {
30550 unsigned n_bin = 0;
30551 unsigned max_n_entry = 0;
30552 unsigned min_n_entry = UINT_MAX;
30553 unsigned tot_n_entry = 0;
30554 unsigned n_empty = 0;
30555 bin_array_pt->get_fill_stats(
30557
30558 oomph_info << "After bin diffusion:"
30559 << " nbin:(" << n_bin << ")"
30560 << " nempty:(" << n_empty << ")"
30561 << " min:(" << min_n_entry << ")"
30562 << " max:(" << max_n_entry << ")"
30563 << " average entries:("
30564 << double(tot_n_entry) / double(n_bin) << ")" << std::endl;
30565 }
30566
30567 // For each bin, compute the minimum of the target areas in the bin
30568
30569 // Timing for map
30570 double t_total_map = 0.0;
30571
30572 // Counter for map
30573 unsigned counter_map = 0;
30574
30575 // Get access to the bins (we need access to the content of the
30576 // bins to compute the minimum of the target areas of the elements
30577 // in each bin)
30578 const std::map<unsigned,
30580 bins_pt = bin_array_pt->get_all_bins_content();
30581
30582 // Get the number of bins
30583 const unsigned n_bin = bins_pt->size();
30584
30585 // Create a vector to store the min target area of each bin (at
30586 // this stage the number of bins should not be that large, so it
30587 // should be safe to build a vector for the total number of bins)
30588 bin_min_target_area.resize(n_bin);
30589 for (unsigned u = 0; u < n_bin; u++)
30590 {
30591 bin_min_target_area[u] = 0.0;
30592 }
30593 // loop over the bins, get their elements and compute the minimum
30594 // target area of all of them
30595 typedef std::map<
30596 unsigned,
30598 for (IT it = bins_pt->begin(); it != bins_pt->end(); it++)
30599 {
30600 // The bin number
30601 unsigned ib = (*it).first;
30602
30603 // Get the number of elements in the bin
30604 const unsigned n_ele_bin = (*it).second.size();
30605
30606 // loop over the elements in the bin
30607 for (unsigned ee = 0; ee < n_ele_bin; ee++)
30608 {
30609 // Get ee-th element (in currrent mesh) in ib-th bin
30610 GeneralisedElement* ele_pt = (*it).second[ee].first;
30611 double t_map = TimingHelpers::timer();
30612 const unsigned ele_number = element_number[ele_pt];
30614
30615 // Increase the number of calls to map
30616 counter_map++;
30617
30618 // Go for smallest target area of any element in this bin to
30619 // force "one level" of refinement (the one-level-ness is
30620 // enforced below by limiting the actual reduction in area
30621 if (bin_min_target_area[ib] != 0)
30622 {
30625 }
30626 else
30627 {
30629 }
30630
30631 } // for (ee<n_ele_bin)
30632
30633 } // for (it!=bins.end())
30634
30635 oomph_info << "CPU for map[counter=" << counter_map
30636 << "]: " << t_total_map << std::endl;
30637
30638 // Optional output for debugging (keep it around!)
30639 const bool output_bins = false;
30640 if (output_bins)
30641 {
30642 unsigned length = bin_min_target_area.size();
30643 for (unsigned u = 0; u < length; u++)
30644 {
30645 oomph_info << "Bin n" << u
30646 << ",target area: " << bin_min_target_area[u] << std::endl;
30647 }
30648 }
30649
30650#endif
30651
30652 // Now start iterating to refine mesh recursively
30653 //-----------------------------------------------
30654 bool done = false;
30655 unsigned iter = 0;
30656#ifdef OOMPH_HAS_MPI
30657 // The number of elements that require (un)refinement
30658 unsigned n_ele_need_refinement = 0;
30659#endif
30660
30661 // The timing for the third stage of segments connectivity
30663
30664 // The timing for the transfering target areas
30665 double t_total_transfer_target_areas = 0.0;
30666
30667 // The timing for the copying of target areas
30668 double t_total_limit_target_areas = 0.0;
30669
30670 // The timing to create the new mesh
30672
30673 // The timing for the snapping of the nodes on the new meshes
30674 double t_total_snap_nodes = 0.0;
30675
30676 // The timing to check whether other processors need to adapt
30677 double t_total_wait_other_processors = 0.0;
30678 double t_iter = TimingHelpers::timer();
30679 while (!done)
30680 {
30681 // Accept by default but overwrite if things go wrong below
30682 done = true;
30683
30686
30687 // Loop over elements in new (tmp) mesh and visit all
30688 // its integration points. Check where it's located in the bin
30689 // structure of the current mesh and pass the target area
30690 // to the new element
30691 nelem = tmp_new_mesh_pt->nelement();
30692
30693 // Store the target areas for elements in the temporary
30694 // TriangulateIO mesh
30696 for (unsigned e = 0; e < nelem; e++)
30697 { // start loop el
30698 ELEMENT* el_pt =
30699 dynamic_cast<ELEMENT*>(tmp_new_mesh_pt->element_pt(e));
30700 unsigned nint = el_pt->integral_pt()->nweight();
30701 for (unsigned ipt = 0; ipt < nint; ipt++)
30702 {
30703 // Get the coordinate of current point
30704 Vector<double> s(2);
30705 for (unsigned i = 0; i < 2; i++)
30706 {
30707 s[i] = el_pt->integral_pt()->knot(ipt, i);
30708 }
30709
30710 Vector<double> x(2);
30711 el_pt->interpolated_x(s, x);
30712
30713#if OOMPH_HAS_CGAL
30714
30715 // Try the five nearest sample points for Newton search
30716 // then just settle on the nearest one
30718 unsigned max_sample_points =
30719 Max_sample_points_for_limited_locate_zeta_during_target_area_transfer;
30720 dynamic_cast<CGALSamplePointContainer*>(
30721 mesh_geom_obj_pt->sample_point_container_pt())
30722 ->limited_locate_zeta(x, max_sample_points, geom_obj_pt, s);
30723#ifdef PARANOID
30724 if (geom_obj_pt == 0)
30725 {
30726 std::stringstream error_message;
30727 error_message << "Limited locate zeta failed for zeta = [ "
30728 << x[0] << " " << x[1] << " ]. Makes no sense!\n";
30729 throw OomphLibError(error_message.str(),
30732 }
30733 else
30734 {
30735#endif
30736 FiniteElement* fe_pt = dynamic_cast<FiniteElement*>(geom_obj_pt);
30737#ifdef PARANOID
30738 if (fe_pt == 0)
30739 {
30740 std::stringstream error_message;
30741 error_message << "Cast to FE for GeomObject returned by "
30742 "limited locate zeta failed for zeta = [ "
30743 << x[0] << " " << x[1] << " ]. Makes no sense!\n";
30744 throw OomphLibError(error_message.str(),
30747 }
30748 else
30749 {
30750#endif
30751 // What's the target area of the element that contains this
30752 // point
30754
30755 // Go for smallest target area over all integration
30756 // points in new element
30757 // to force "one level" of refinement (the one-level-ness
30758 // is enforced below by limiting the actual reduction in
30759 // area
30761 {
30764 }
30765 else
30766 {
30768 }
30769#ifdef PARANOID
30770 }
30771 }
30772#endif
30773
30774#else
30775
30776 // Find the bin that contains that point and its contents
30777 int bin_number = 0;
30778 bin_array_pt->get_bin(x, bin_number);
30779
30780 // Did we find it?
30781 if (bin_number < 0)
30782 {
30783 // Not even within bin boundaries... odd
30784 std::stringstream error_message;
30785 error_message << "Very odd -- we're looking for a point[ " << x[0]
30786 << " " << x[1] << " ] that's not even \n"
30787 << "located within the bin boundaries.\n";
30788 throw OomphLibError(error_message.str(),
30789 "RefineableTriangleMesh::adapt()",
30791 } // if (bin_number<0)
30792 else
30793 {
30794 // Go for smallest target area of any element in this bin
30795 // to force "one level" of refinement (the one-level-ness
30796 // is enforced below by limiting the actual reduction in
30797 // area
30799 {
30803 }
30804 else
30805 {
30808 }
30809 }
30810
30811#endif
30812
30813 } // for (ipt<nint)
30814
30815 } // for (e<nelem)
30816
30817 // do some output (keep it alive!)
30818 const bool output_target_areas = false;
30820 {
30822 for (unsigned u = 0; u < length; u++)
30823 {
30824 oomph_info << "Element" << u
30825 << ",target area: " << new_transferred_target_area[u]
30826 << std::endl;
30827 }
30828 }
30829 oomph_info << "Time for loop over integration points in new mesh: "
30830 << TimingHelpers::timer() - t0_loop_int_pts << std::endl;
30831
30832 // {
30833 // tmp.open((Global_string_for_annotation::
30834 // String[0]+"binned_target_areas"+
30835 // StringConversion::to_string(Global_unsigned::Number)+".dat").c_str());
30836
30837 // Vector<Vector<std::pair<FiniteElement*,Vector<double> > > >
30838 // bin_content=
30839 // mesh_geom_obj_pt->bin_content();
30840 // unsigned nbin=bin_content.size();
30841 // for (unsigned b=0;b<nbin;b++)
30842 // {
30843 // unsigned nentry=bin_content[b].size();
30844 // for (unsigned entry=0;entry<nentry;entry++)
30845 // {
30846 // FiniteElement* el_pt=bin_content[b][entry].first;
30847 // GeneralisedElement* gen_el_pt=bin_content[b][entry].first;
30848 // Vector<double> s=bin_content[b][entry].second;
30849 // Vector<double> x(2);
30850 // el_pt->interpolated_x(s,x);
30851 // unsigned e_current=element_number[gen_el_pt];
30852 // tmp << x[0] << " " << x[1] << " "
30853 // << target_area[e_current] << " "
30854 // << el_pt->size() << " "
30855 // << std::endl;
30856 // }
30857 // }
30858 // tmp.close();
30859 // }
30860
30863
30864 if (Print_timings_level_adaptation > 2)
30865 {
30866 // Get the number of elements in the old mesh (this)
30867 const unsigned n_element = this->nelement();
30868 // Get the number of elements in the background mesh
30869 const unsigned n_element_background = tmp_new_mesh_pt->nelement();
30870
30871 oomph_info << "CPU for transfer of target areas "
30872 << "[n_ele_old_mesh=" << n_element
30873 << ", n_ele_background_mesh=" << n_element_background
30874 << "] (iter " << iter
30875 << "): " << t_sub_total_transfer_target_areas << std::endl;
30876 }
30877
30878 // Add the timing for tranfer of target areas
30880
30881 // // Output mesh
30882 // tmp_new_mesh_pt->output(("intermediate_mesh"+
30883 // StringConversion::to_string(iter)+".dat").c_str());
30884
30885 // tmp.open((Global_string_for_annotation::
30886 // String[0]+"target_areas_intermediate_mesh_iter"+
30887 // StringConversion::to_string(iter)+"_"+
30888 // StringConversion::to_string(Global_unsigned::Number)+".dat").c_str());
30889
30891
30892 // Now copy into target area for temporary mesh but limit to
30893 // the equivalent of one sub-division per iteration
30894#ifdef OOMPH_HAS_MPI
30895 unsigned n_ele_need_refinement_iter = 0;
30896#endif
30897
30898 // Don't delete! Keep these around for debugging
30899 // ofstream tmp_mesh_file;
30900 // tmp_mesh_file.open("tmp_mesh_file.dat");
30901 // tmp_new_mesh_pt->output(tmp_mesh_file);
30902 // tmp_mesh_file.close();
30903 // ofstream target_areas_file;
30904 // target_areas_file.open("target_areas_file.dat");
30905
30906 const unsigned nel_new = tmp_new_mesh_pt->nelement();
30908 for (unsigned e = 0; e < nel_new; e++)
30909 {
30910 // The finite element
30911 FiniteElement* f_ele_pt = tmp_new_mesh_pt->finite_element_pt(e);
30912
30913 // Transferred target area
30914 const double new_area = new_transferred_target_area[e];
30915 if (new_area <= 0.0)
30916 {
30917 std::ostringstream error_stream;
30919 << "This shouldn't happen! Element whose centroid is at "
30920 << (f_ele_pt->node_pt(0)->x(0) + f_ele_pt->node_pt(1)->x(0) +
30921 f_ele_pt->node_pt(2)->x(0)) /
30922 3.0
30923 << " "
30924 << (f_ele_pt->node_pt(0)->x(1) + f_ele_pt->node_pt(1)->x(1) +
30925 f_ele_pt->node_pt(2)->x(1)) /
30926 3.0
30927 << " "
30928 << " has no target area assigned\n";
30929 throw OomphLibError(error_stream.str(),
30932 }
30933 else
30934 {
30935 // Limit target area to the equivalent of uniform refinement
30936 // during this stage of the iteration
30938 if (new_target_area[e] < f_ele_pt->size() / 3.0)
30939 {
30940 new_target_area[e] = f_ele_pt->size() / 3.0;
30941
30942 // We'll need to give it another go later
30943 done = false;
30944 }
30945
30946 // Don't delete! Keep around for debugging
30947 // target_areas_file
30948 // << (f_ele_pt->node_pt(0)->x(0)+
30949 // f_ele_pt->node_pt(1)->x(0)+
30950 // f_ele_pt->node_pt(2)->x(0))/3.0 << " "
30951 // << (f_ele_pt->node_pt(0)->x(1)+
30952 // f_ele_pt->node_pt(1)->x(1)+
30953 // f_ele_pt->node_pt(2)->x(1))/3.0 << " "
30954 // << new_area << " "
30955 // << new_target_area[e] << std::endl;
30956
30957#ifdef OOMPH_HAS_MPI
30958 // Keep track of the elements that require (un)refinement
30960#endif
30961
30962 } // else if (new_area <= 0.0)
30963
30964 } // for (e < nel_new)
30965
30966 // Don't delete! Keep around for debugging
30967 // target_areas_file.close();
30968
30969 const double t_sub_total_limit_target_areas =
30971
30972 // Add the timing for copying target areas
30974
30975 if (Print_timings_level_adaptation > 2)
30976 {
30977 // Get the number of elements in the old mesh (this)
30978 const unsigned n_element = this->nelement();
30979 // Get the number of elements in the background mesh
30980 const unsigned n_element_background = tmp_new_mesh_pt->nelement();
30981
30982 oomph_info << "CPU for limiting target areas "
30983 << "[n_ele_old_mesh=" << n_element
30984 << ", n_ele_background_mesh=" << n_element_background
30985 << "] (iter " << iter
30986 << "): " << t_sub_total_limit_target_areas << std::endl;
30987 }
30988
30989 if (done)
30990 {
30992 << "All area adjustments accommodated by max. permitted area"
30993 << " reduction \n";
30994 }
30995 else
30996 {
30997 oomph_info << "NOT all area adjustments accommodated by max. "
30998 << "permitted area reduction \n";
30999 }
31000
31001 // tmp.close();
31002 // pause("doced binned_target_areas.dat and intermediate mesh targets");
31003
31004 // Now create the new mesh from TriangulateIO structure
31005 //-----------------------------------------------------
31006 // associated with uniform background mesh and the
31007 //------------------------------------------------
31008 // associated target element sizes.
31009 //---------------------------------
31010
31012
31013 // Solid mesh?
31014 if (solid_mesh_pt != 0)
31015 {
31019 this->Time_stepper_pt,
31020 this->Use_attributes,
31021 this->Allow_automatic_creation_of_vertices_on_boundaries,
31022 this->communicator_pt());
31023 }
31024 // No solid mesh
31025 else
31026 {
31030 this->Time_stepper_pt,
31031 this->Use_attributes,
31032 this->Allow_automatic_creation_of_vertices_on_boundaries,
31033 this->communicator_pt());
31034 }
31035
31036 // Sub-total to create new adapted mesh
31039
31040 // Add the time to the total snap nodes time
31042
31043 if (Print_timings_level_adaptation > 2)
31044 {
31045 // Get the number of elements of the new adapted mesh
31046 const unsigned n_element_new_adapted_mesh = new_mesh_pt->nelement();
31047
31048 oomph_info << "CPU for creation of new adapted mesh "
31050 << "[nele=" << n_element_new_adapted_mesh << "] (iter "
31052 << std::endl;
31053 }
31054
31055#ifdef OOMPH_HAS_MPI
31056 // ------------------------------------------
31057 // DISTRIBUTED MESH: BEGIN
31058 // ------------------------------------------
31059
31060 // This section is only required if we are dealing with
31061 // distributed meshes, otherwise there are not shared boundaries
31062 // overlapping internal boundaries
31063
31064 // Check if necessary to fill boundary elements for those internal
31065 // boundaries that overlap shared boundaries
31066 if (this->nshared_boundary_overlaps_internal_boundary() > 0)
31067 {
31068 // Copy the data structures that indicate which shared
31069 // boundaries are part of an internal boundary
31070 new_mesh_pt->shared_boundary_overlaps_internal_boundary() =
31071 this->shared_boundary_overlaps_internal_boundary();
31072
31073 // Copy the data structure that indicates which are the shared
31074 // boundaries in each processor
31075 new_mesh_pt->shared_boundaries_ids() = this->shared_boundaries_ids();
31076
31077 // Fill the structures for the boundary elements and face indexes
31078 // of the boundary elements
31080 ->fill_boundary_elements_and_nodes_for_internal_boundaries();
31081 }
31082 // ------------------------------------------
31083 // DISTRIBUTED MESH: END
31084 // ------------------------------------------
31085#endif // #ifdef OOMPH_HAS_MPI
31086
31087 // Snap to curvilinear boundaries (some code duplication as this
31088 // is repeated below but helper function would take so many
31089 // arguments that it's nearly as messy...
31090
31091 // Pass the boundary geometric objects to the new mesh
31092 new_mesh_pt->boundary_geom_object_pt() =
31093 this->boundary_geom_object_pt();
31094
31095 // Reset the boundary coordinates if there is
31096 // a geometric object associated with the boundary
31097 new_mesh_pt->boundary_coordinate_limits() =
31098 this->boundary_coordinate_limits();
31099
31102
31103 for (unsigned b = 0; b < n_boundary; b++)
31104 {
31105 // ------------------------------------------
31106 // DISTRIBUTED MESH: BEGIN
31107 // ------------------------------------------
31108
31109 // Before setting up boundary coordinates for the new mesh we
31110 // require to identify the segments with the old mesh to
31111 // assign initial zeta values
31112#ifdef OOMPH_HAS_MPI
31113 if (this->is_mesh_distributed())
31114 {
31115 // Identify the segments of the new mesh with the ones of
31116 // the original mesh
31118 ->identify_boundary_segments_and_assign_initial_zeta_values(b,
31119 this);
31120 }
31121#endif
31122 // ------------------------------------------
31123 // DISTRIBUTED MESH: END
31124 // ------------------------------------------
31125
31126 // Setup boundary coordinates for boundaries with GeomObject
31127 // associated
31128 if (new_mesh_pt->boundary_geom_object_pt(b) != 0)
31129 {
31131 }
31132 }
31133
31136
31138 // Move the nodes on the new boundary onto the old curvilinear
31139 // boundary. If the boundary is straight this will do precisely
31140 // nothing but will be somewhat inefficient
31141 for (unsigned b = 0; b < n_boundary; b++)
31142 {
31143 this->snap_nodes_onto_boundary(new_mesh_pt, b);
31144 }
31145
31146 const double t_sub_total_snap_nodes_new_mesh =
31148
31149 // Add the time to the total snap nodes time
31151
31152 if (Print_timings_level_adaptation > 2)
31153 {
31154 oomph_info << "CPU for snapping nodes onto boundaries (new mesh) "
31155 << "(iter " << iter
31156 << "): " << t_sub_total_snap_nodes_new_mesh << std::endl;
31157 }
31158
31159 // Update mesh further?
31160 if (Mesh_update_fct_pt != 0)
31161 {
31162 Mesh_update_fct_pt(new_mesh_pt);
31163 }
31164
31165 // If we have a continuation problem
31166 // any problem in which the timestepper is a "generalisedtimestepper",
31167 // which will have been set by the problem, then ensure
31168 // all data in the new mesh has the appropriate timestepper
31169 if (dynamic_cast<GeneralisedTimeStepper*>(this->Time_stepper_pt))
31170 {
31171 new_mesh_pt->set_nodal_and_elemental_time_stepper(
31172 this->Time_stepper_pt, false);
31173 new_mesh_pt->set_mesh_level_time_stepper(this->Time_stepper_pt,
31174 false);
31175 }
31176
31177 // Not done: get ready for another iteration
31178 iter++;
31179 delete tmp_new_mesh_pt;
31180
31181#ifdef OOMPH_HAS_MPI
31182 // Check whether the number of elements that need (un)refinement
31183 // from the previous iteration is the same, if that is the case
31184 // then we mark this processor as done
31186 {
31187 done = true;
31188 }
31189 // Update the number of elements that require further
31190 // (un)refinement
31192#endif // #ifdef OOMPH_HAS_MPI
31193
31194 // ------------------------------------------
31195 // DISTRIBUTED MESH: BEGIN
31196 // ------------------------------------------
31197
31198 // We can only finish the iteration adaptation process if ALL
31199 // the involved processor are marked as done, otherwise, ALL
31200 // processor need to go for another iteration
31201#ifdef OOMPH_HAS_MPI
31202 if (this->is_mesh_distributed())
31203 {
31204 // Time to check whether other processors have finish to adapt
31206
31207 // In case that the mesh is distributed it is necessary to
31208 // verify that no processor requires further refinement. If at
31209 // least one processor needs more refinement then all
31210 // processors need to go for another iteration to participate
31211 // in the communications
31213
31214 // Is this processor done?
31215 if (done)
31216 {
31218 }
31220 // Get the communicator of the mesh
31221 OomphCommunicator* comm_pt = this->communicator_pt();
31222 // Communicate with all procesoors to check whether we need to
31223 // re-iterate
31226 1,
31228 MPI_SUM,
31229 comm_pt->mpi_comm());
31230 // Are all processors done?
31231 if (nproc_not_done > 0)
31232 {
31234 << "At least one processors requires further refinement. "
31235 << "Go for another iteration." << std::endl;
31236 done = false;
31237 }
31238
31239 // Total to check whether other processors have finish to
31240 // adapt
31243
31244 // Add to the total timings to check whether other processors
31245 // need to adapt
31247
31248 if (Print_timings_level_adaptation > 2)
31249 {
31250 oomph_info << "CPU for waiting other processors "
31251 << "(iter " << iter
31253 << std::endl;
31254 }
31255
31256 } // if (this->is_mesh_distributed())
31257#endif
31258 // ------------------------------------------
31259 // DISTRIBUTED MESH: END
31260 // ------------------------------------------
31261
31262 if (!done)
31263 {
31264 oomph_info << "Going for another iteration. Current iteration ("
31265 << iter << ")" << std::endl;
31266
31267 // Use the new mesh as the tmp mesh
31269 tmp_new_triangulateio = new_mesh_pt->triangulateio_representation();
31270 }
31271
31272 } // end of iteration (while (!done))
31273
31274 // Delete the temporary geometric object representation of the
31275 // current mesh
31276 delete mesh_geom_obj_pt;
31277
31278 oomph_info << "CPU for iterative generation of new mesh (TOTAL): "
31279 << TimingHelpers::timer() - t_iter << std::endl;
31280
31281 if (Print_timings_level_adaptation > 1)
31282 {
31283 oomph_info << "-- CPU for creating new adapted meshes (TOTAL): "
31284 << t_total_create_new_adapted_mesh << std::endl;
31285
31286 oomph_info << "-- CPU for limiting target areas (TOTAL): "
31287 << t_total_limit_target_areas << std::endl;
31288
31289 oomph_info << "-- CPU for transferring target areas (TOTAL): "
31290 << t_total_transfer_target_areas << std::endl;
31291
31292 oomph_info << "-- CPU for waiting other processors (TOTAL): "
31293 << t_total_wait_other_processors << std::endl;
31294 }
31295
31296 // ==============================================================
31297 // END: Transferring of target areas and creation of new mesh
31298 // ==============================================================
31299
31300 // ==============================================================
31301 // BEGIN: Project solution from the old to the new mesh
31302 // ==============================================================
31303
31304 // Check that the projection step is not disabled
31305 if (!Disable_projection)
31306 {
31307 // Take the time for the projection step
31309
31310 // Print info. for tranfering target areas
31311 if (Print_timings_projection)
31312 {
31313 // Switch timings and stats on
31317 }
31318
31319 double t_proj = TimingHelpers::timer();
31320 oomph_info << "About to begin projection.\n";
31321
31322 // Project current solution onto new mesh
31323 //---------------------------------------
31326
31327 // Projection requires to be enabled as distributed if working
31328 // with a distributed mesh
31329#ifdef OOMPH_HAS_MPI
31330 if (this->is_mesh_distributed())
31331 {
31332 // ------------------------------------------
31333 // DISTRIBUTED MESH: BEGIN
31334 // ------------------------------------------
31335
31336 // We need to back up the time stepper object since the
31337 // projection class creates a new one
31338 Time* backed_up_time_pt = this->Time_stepper_pt->time_pt();
31339
31340 // Set the projection problem as distributed
31341 project_problem_pt->enable_problem_distributed();
31342
31343 // Pass the time stepper to the projection problem (used when
31344 // setting multi_domain_interation)
31345 project_problem_pt->add_time_stepper_pt(this->Time_stepper_pt);
31346
31347 // Set the mesh used for the projection object
31348 project_problem_pt->mesh_pt() = new_mesh_pt;
31349 // project_problem_pt->disable_suppress_output_during_projection();
31350
31351 // Use iterative solver for projection? By default, an iterative
31352 // solver is used for the projection stage
31353 if (!this->use_iterative_solver_for_projection())
31354 {
31355 project_problem_pt->disable_use_iterative_solver_for_projection();
31356 }
31357
31358 // Do the projection
31359 project_problem_pt->project(this);
31360
31361 // Reset the time stepper object (only affects distributed meshes)
31362 this->Time_stepper_pt->time_pt() = backed_up_time_pt;
31363
31364 // ------------------------------------------
31365 // DISTRIBUTED MESH: END
31366 // ------------------------------------------
31367
31368 } // if (this->is_mesh_distributed())
31369 else
31370#endif // #ifdef OOMPH_HAS_MPI
31371 {
31372 // Set the mesh used for the projection object
31373 project_problem_pt->mesh_pt() = new_mesh_pt;
31374
31375 // project_problem_pt->disable_suppress_output_during_projection();
31376
31377 // Use iterative solver for projection? By default, an iterative
31378 // solver is used for the projection stage
31379 if (!this->use_iterative_solver_for_projection())
31380 {
31381 project_problem_pt->disable_use_iterative_solver_for_projection();
31382 }
31383
31384 // Do the projection
31385 project_problem_pt->project(this);
31386 }
31387
31388 // Reset printing info. for projection
31389 if (Print_timings_projection)
31390 {
31391 // Switch timings and stats off
31395 }
31396
31397 // Get the total time for projection
31398 const double tt_projection =
31400
31401 if (Print_timings_level_adaptation > 1)
31402 {
31403 // Get the number of elements in the old mesh (this)
31404 const unsigned n_element = this->nelement();
31405 // Get the number of elements in the new mesh
31406 const unsigned n_element_new = new_mesh_pt->nelement();
31407 oomph_info << "CPU for projection (in mesh adaptation) "
31408 << "[n_ele_old_mesh=" << n_element
31409 << ", n_ele_new_mesh=" << n_element_new
31410 << "]: " << tt_projection << std::endl;
31411
31412 // ------------------------------------------
31413 // DISTRIBUTED MESH: BEGIN
31414 // ------------------------------------------
31415#ifdef OOMPH_HAS_MPI
31416 if (this->is_mesh_distributed())
31417 {
31418 // The maximum number of elements in the mesh (over all
31419 // processors)
31421 unsigned n_max_element_new_global = 0;
31422 // Get the maximum number of elements over all processors
31425 1,
31427 MPI_MAX,
31428 0,
31429 this->communicator_pt()->mpi_comm());
31430
31431 // The time for projection for this processor
31433 double tt_global_min_projection = 0.0;
31434 double tt_global_max_projection = 0.0;
31435
31436 // Get the minimum and maximum time for projection
31439 1,
31440 MPI_DOUBLE,
31441 MPI_MIN,
31442 0,
31443 this->communicator_pt()->mpi_comm());
31446 1,
31447 MPI_DOUBLE,
31448 MPI_MAX,
31449 0,
31450 this->communicator_pt()->mpi_comm());
31451
31452 if (this->communicator_pt()->my_rank() == 0)
31453 {
31454 oomph_info << "CPU for projection global (MIN): "
31455 << tt_global_min_projection << std::endl;
31456 oomph_info << "CPU for projection global (MAX) "
31457 << "[n_max_ele_new_global=" << n_max_element_new_global
31458 << "]: " << tt_global_max_projection << std::endl;
31459
31460 std::cerr << "CPU for projection global (MIN): "
31461 << tt_global_min_projection << std::endl;
31462 std::cerr << "CPU for projection global (MAX): "
31463 << "[n_max_ele_new_global=" << n_max_element_new_global
31464 << "]: " << tt_global_max_projection << std::endl;
31465 }
31466 }
31467#endif // #ifdef OOMPH_HAS_MPI
31468 // ------------------------------------------
31469 // DISTRIBUTED MESH: END
31470 // ------------------------------------------
31471
31472 } // if (Print_timings_level_adaptation>1)
31473
31474 oomph_info << "CPU for projection of solution onto new mesh: "
31475 << TimingHelpers::timer() - t_proj << std::endl;
31476
31477 // Delete the projection problem
31478 delete project_problem_pt;
31479
31480 } // if (!Disable_projection)
31481 else
31482 {
31483 oomph_info << "Projection disabled! The new mesh will contain zeros"
31484 << std::endl;
31485 }
31486
31487 // ==============================================================
31488 // END: Project solution from the old to the new mesh
31489 // ==============================================================
31490
31491 double t_rest = TimingHelpers::timer();
31492
31493 // Flush the old mesh
31494 unsigned nnod = nnode();
31495 for (unsigned j = nnod; j > 0; j--)
31496 {
31497 delete Node_pt[j - 1];
31498 Node_pt[j - 1] = 0;
31499 }
31500 unsigned nel = nelement();
31501 for (unsigned e = nel; e > 0; e--)
31502 {
31503 delete Element_pt[e - 1];
31504 Element_pt[e - 1] = 0;
31505 }
31506
31507 // Now copy back to current mesh
31508 //------------------------------
31509 nnod = new_mesh_pt->nnode();
31511 nel = new_mesh_pt->nelement();
31512 Element_pt.resize(nel);
31513 for (unsigned j = 0; j < nnod; j++)
31514 {
31516 }
31517 for (unsigned e = 0; e < nel; e++)
31518 {
31519 Element_pt[e] = new_mesh_pt->element_pt(e);
31520 }
31521
31522 // Copy the boundary elements information from the new mesh to the
31523 // original mesh
31524 unsigned nbound = 0;
31525
31526#ifdef OOMPH_HAS_MPI
31527 // If working with a distributed mesh we need to change the number
31528 // of boundaries so that shared boundaries information is also
31529 // copied from the old to the new mesh
31530 if (this->is_mesh_distributed())
31531 {
31532 // The boundaries to be copied include those new ones in the new
31533 // mesh (shared boundaries). This info. is required to
31534 // re-establish the halo/haloed scheme
31535 nbound = new_mesh_pt->nboundary();
31536 // After halo and haloed scheme has been re-established the
31537 // number of boundaries is changed to the original number of
31538 // boundaries
31539 }
31540 else
31541#endif
31542 {
31543 // The original number of boundaries
31545 }
31546
31547 Boundary_element_pt.resize(nbound);
31548 Face_index_at_boundary.resize(nbound);
31549 Boundary_node_pt.resize(nbound);
31550 for (unsigned b = 0; b < nbound; b++)
31551 {
31552 unsigned nel = new_mesh_pt->nboundary_element(b);
31553 Boundary_element_pt[b].resize(nel);
31554 Face_index_at_boundary[b].resize(nel);
31555 for (unsigned e = 0; e < nel; e++)
31556 {
31557 Boundary_element_pt[b][e] = new_mesh_pt->boundary_element_pt(b, e);
31558 Face_index_at_boundary[b][e] =
31559 new_mesh_pt->face_index_at_boundary(b, e);
31560 }
31561 unsigned nnod = new_mesh_pt->nboundary_node(b);
31562 Boundary_node_pt[b].resize(nnod);
31563 for (unsigned j = 0; j < nnod; j++)
31564 {
31565 Boundary_node_pt[b][j] = new_mesh_pt->boundary_node_pt(b, j);
31566 }
31567 }
31568
31569 // Also copy over the new boundary and region information
31570 unsigned n_region = new_mesh_pt->nregion();
31571 // Only bother if we have regions
31572 if (n_region > 1)
31573 {
31574 // Deal with the region information first
31575 this->Region_attribute.resize(n_region);
31576 for (unsigned r = 0; r < n_region; r++)
31577 {
31578 this->Region_attribute[r] = new_mesh_pt->region_attribute(r);
31579 // Get the region id
31580 unsigned r_id = static_cast<unsigned>(this->Region_attribute[r]);
31581 // Find the number of elements in the region
31582 unsigned n_region_element = new_mesh_pt->nregion_element(r_id);
31583 this->Region_element_pt[r_id].resize(n_region_element);
31584 for (unsigned e = 0; e < n_region_element; e++)
31585 {
31586 this->Region_element_pt[r_id][e] =
31587 new_mesh_pt->region_element_pt(r_id, e);
31588 }
31589 }
31590
31591 // Now the boundary region information
31592 this->Boundary_region_element_pt.resize(nbound);
31593 this->Face_index_region_at_boundary.resize(nbound);
31594
31595 // Now loop over the boundaries
31596 for (unsigned b = 0; b < nbound; ++b)
31597 {
31598 for (unsigned rr = 0; rr < n_region; rr++)
31599 {
31600 // The region id
31601 unsigned r = static_cast<unsigned>(this->Region_attribute[rr]);
31602
31603 unsigned n_boundary_el_in_region =
31604 new_mesh_pt->nboundary_element_in_region(b, r);
31605
31607 {
31608 // Allocate storage in the map
31609 this->Boundary_region_element_pt[b][r].resize(
31611 this->Face_index_region_at_boundary[b][r].resize(
31613
31614 // Copy over the information
31615 for (unsigned e = 0; e < n_boundary_el_in_region; ++e)
31616 {
31617 this->Boundary_region_element_pt[b][r][e] =
31618 new_mesh_pt->boundary_element_in_region_pt(b, r, e);
31619 this->Face_index_region_at_boundary[b][r][e] =
31620 new_mesh_pt->face_index_at_boundary_in_region(b, r, e);
31621 }
31622 }
31623 }
31624 } // End of loop over boundaries
31625
31626 } // End of case when more than one region
31627
31628 // ------------------------------------------
31629 // DISTRIBUTED MESH: BEGIN
31630 // ------------------------------------------
31631
31632 // Re-generate halo(ed) information (only for distributed meshes)
31633#ifdef OOMPH_HAS_MPI
31634 if (this->is_mesh_distributed())
31635 {
31636 // Delete halo(ed) information in the original mesh, the new
31637 // halo(ed) information is generated usign the info. of the new
31638 // mesh
31639 if (this->is_mesh_distributed())
31640 {
31641 this->Halo_node_pt.clear();
31642 this->Root_halo_element_pt.clear();
31643
31644 this->Haloed_node_pt.clear();
31645 this->Root_haloed_element_pt.clear();
31646
31647 this->External_halo_node_pt.clear();
31648 this->External_halo_element_pt.clear();
31649
31650 this->External_haloed_node_pt.clear();
31651 this->External_haloed_element_pt.clear();
31652 }
31653
31654 // Re-establish the shared boundary elements and nodes scheme
31655 // before re-establish halo(ed) information
31656 this->reset_shared_boundary_elements_and_nodes();
31657
31658 // -------------------------------------------------------------
31659 // Remove shared boundary elements and nodes from original
31660 // boundary elements and boundary nodes containers. Shared
31661 // boundary elements and nodes are stored in a special
31662 // container.
31663
31664 // Get the shared boundaries in this processor with any other
31665 // processor
31667 this->shared_boundaries_in_this_processor(
31669
31670 // Get the number of shared boundaries
31672 // Loop over the shared boundaries marked as original boundaries
31673 // in tmp_new_mesh
31674 for (unsigned i = 0; i < nmy_rank_shd_bnd; i++)
31675 {
31676 // Get the boundary id
31677 const unsigned shd_bnd_id = my_rank_shared_boundaries_ids[i];
31678 // Flush any previous relation of shared boundary elements
31679 // marked as original boundary elements in tmp_new_mesh
31680 this->Boundary_element_pt[shd_bnd_id].clear();
31681
31682 // Get the number of nodes associated with the original
31683 // boundary in tmp_new_mesh that is a shared boundary
31684 const unsigned tmp_nnodes = this->nshared_boundary_node(shd_bnd_id);
31685 for (unsigned n = 0; n < tmp_nnodes; n++)
31686 {
31687 Node* tmp_node_pt = this->boundary_node_pt(shd_bnd_id, n);
31688 tmp_node_pt->remove_from_boundary(shd_bnd_id);
31689 } // for (n < nnodes)
31690
31691 } // for (shd_bnd_id < nmy_rank_shd_bnd)
31692
31693 // Re-set the number of boundaries to the original one
31694 this->set_nboundary(n_boundary);
31695
31696 // Sort the nodes on the boundaries so that they have the same
31697 // order on all the boundaries
31698 this->sort_nodes_on_shared_boundaries();
31699
31700 // Re-set the halo(ed) scheme
31701 this->reset_halo_haloed_scheme();
31702
31703 // Set the correct number of segments for the boundaries with
31704 // geom objects associated
31705 for (unsigned b = 0; b < n_boundary; b++)
31706 {
31707 if (this->boundary_geom_object_pt(b) != 0)
31708 {
31709 const unsigned nsegments = new_mesh_pt->nboundary_segment(b);
31710 this->set_nboundary_segment_node(b, nsegments);
31711 }
31712 }
31713
31714 // Resume the connections in boundaries were it was suspended
31715 resume_boundary_connections(resume_initial_connection_polyline_pt,
31717
31718 } // if (this->is_mesh_distributed())
31719
31720#endif // #ifdef OOMPH_HAS_MPI
31721
31722 // ------------------------------------------
31723 // DISTRIBUTED MESH: END
31724 // ------------------------------------------
31725
31726 // Snap the newly created nodes onto any geometric objects
31727 this->snap_nodes_onto_geometric_objects();
31728
31729 // Copy the IDs of the vertex nodes
31730 this->Oomph_vertex_nodes_id = new_mesh_pt->oomph_vertex_nodes_id();
31731
31732 // Copy TriangulateIO representation
31733 TriangleHelper::clear_triangulateio(this->Triangulateio);
31734 bool quiet = true;
31735 this->Triangulateio =
31737 new_mesh_pt->triangulateio_representation(), quiet);
31738
31739 // Flush the mesh
31740 new_mesh_pt->flush_element_and_node_storage();
31741
31742 // Delete the mesh
31743 delete new_mesh_pt;
31744
31745 // Resume of timings
31746 if (Print_timings_level_adaptation > 2)
31747 {
31748 // Report timings related with setting boundary coordinates of
31749 // nodes on segments
31750 oomph_info << "CPU for segments connectivity (first stage) [sec]: "
31752 oomph_info << "CPU for segments connectivity (second stage) [sec]: "
31754 oomph_info << "CPU for segments connectivity (third stage) [sec]: "
31756 }
31757
31758 if (Print_timings_level_adaptation > 1)
31759 {
31760 const double t_total_segments_connectivity =
31764
31765 oomph_info << "CPU for segments connectivity (TOTAL) [sec]: "
31766 << t_total_segments_connectivity << std::endl;
31767
31768 if (Print_timings_level_adaptation > 2)
31769 {
31770 // Report timings for snapping of nodes onto boundaries
31771 oomph_info << "CPU for snapping nodes onto boundaries "
31772 << "(new mesh): " << t_total_snap_nodes << std::endl;
31773 }
31774
31776 oomph_info << "CPU for snapping nodes onto boundaries (TOTAL): "
31777 << t_total_snap_nodes << std::endl;
31778 }
31779
31780 double max_area = 0.0;
31781 double min_area = 0.0;
31782
31783 this->max_and_min_element_size(max_area, min_area);
31784 oomph_info << "Max/min element size in adapted mesh: " << max_area << " "
31785 << min_area << std::endl;
31786
31787 oomph_info << "CPU time for final bits [sec]: "
31788 << TimingHelpers::timer() - t_rest << std::endl;
31789 }
31790 else
31791 {
31792 oomph_info << "Not enough benefit in adaptation.\n";
31793 Nrefined = 0;
31794 Nunrefined = 0;
31795 }
31796
31798 oomph_info << "CPU time for adaptation [sec]: " << CPU_for_adaptation
31799 << std::endl;
31800
31801 // ------------------------------------------
31802 // DISTRIBUTED MESH: BEGIN
31803 // ------------------------------------------
31804#ifdef OOMPH_HAS_MPI
31805 if (this->is_mesh_distributed())
31806 {
31807 // Get the communicator
31808 OomphCommunicator* comm_pt = this->communicator_pt();
31809 // Get the total number of processors to compute the average
31810 const unsigned n_proc = comm_pt->nproc();
31811 if (Print_timings_level_adaptation > 1 && n_proc > 1)
31812 {
31813 double global_min_CPU_for_adaptation = 0.0;
31814 double global_max_CPU_for_adaptation = 0.0;
31816
31817 // Get the maximum and minimum of the adaptation times
31820 1,
31821 MPI_DOUBLE,
31822 MPI_MIN,
31823 0,
31824 comm_pt->mpi_comm());
31827 1,
31828 MPI_DOUBLE,
31829 MPI_MAX,
31830 0,
31831 comm_pt->mpi_comm());
31834 1,
31835 MPI_DOUBLE,
31836 MPI_SUM,
31837 0,
31838 comm_pt->mpi_comm());
31839
31840 // Get the rank of the processor
31841 const unsigned my_rank = comm_pt->my_rank();
31842 if (my_rank == 0)
31843 {
31844 oomph_info << "CPU for adaptation (MIN): "
31845 << global_min_CPU_for_adaptation << std::endl;
31846 oomph_info << "CPU for adaptation (MAX): "
31847 << global_max_CPU_for_adaptation << std::endl;
31848 oomph_info << "CPU for adaptation (AVERAGE): "
31849 << global_average_CPU_for_adaptation / n_proc << std::endl;
31850 } // if (my_rank==0)
31851
31852 } // if (Print_timings_level_adaptation>1&&n_proc>1)
31853
31854 } // if (this->is_mesh_distributed())
31855
31856 // ------------------------------------------
31857 // DISTRIBUTED MESH: END
31858 // ------------------------------------------
31859
31860#endif // #ifdef OOMPH_HAS_MPI
31861 }
31862
31863 //=========================================================================
31864 /// Mark the vertices that are not allowed for deletion by
31865 /// the unrefienment/refinement polyline methods. In charge of
31866 /// filling the Boundary_connections_pt structure
31867 //=========================================================================
31868 template<class ELEMENT>
31870 {
31871 // Clear any previous information
31872 // Boundary_chunk_connections_pt.clear();
31873 Boundary_connections_pt.clear();
31874
31875 // Loop over the boundaries in the domain (outer, internal -- closed
31876 // and open ---, and shared) and get the boundaries ids with
31877 // connections (have or receive)
31878
31879 // Store the boundaries ids that have or receive connection
31880 std::set<unsigned> boundary_id_with_connections;
31881
31882 // ------------------------------------------------------------------
31883 // Outer boundaries
31884 // ------------------------------------------------------------------
31885
31886 // Get the number of outer boundaries (closed boundaries)
31887 const unsigned n_outer_boundaries = this->Outer_boundary_pt.size();
31888
31889 // Loop over the outer boundaries
31890 for (unsigned i = 0; i < n_outer_boundaries; i++)
31891 {
31892 // Get a temporary polygon representation
31893 TriangleMeshPolygon* tmp_polygon_pt = this->Outer_boundary_pt[i];
31894 // Get the number of polylines associated to the current outer
31895 // boundary
31896 const unsigned n_polyline = tmp_polygon_pt->npolyline();
31897 // Loop over the polylines
31898 for (unsigned p = 0; p < n_polyline; p++)
31899 {
31900 // Get a temporary representation of the polyline
31902
31903 // Is the initial vertex connected?
31904 if (tmp_polyline_pt->is_initial_vertex_connected())
31905 {
31906 // Get the boundary id of the current polyline
31907 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
31908
31909 // Include the boundary id to the set of boundaries with
31910 // connections
31912
31913 // Boundary id to which the curve is connecte
31914 const unsigned dst_bnd_id =
31915 tmp_polyline_pt->initial_vertex_connected_bnd_id();
31916
31917 // Include the destination boundary id to the set of
31918 // boundaries with connections
31920
31921 } // if (tmp_polyline_pt->is_initial_vertex_connected())
31922
31923 // Is the final vertex connected?
31924 if (tmp_polyline_pt->is_final_vertex_connected())
31925 {
31926 // Get the boundary id of the current polyline
31927 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
31928
31929 // Include the boundary id to the set of boundaries with
31930 // connections
31932
31933 // Boundary id to which the curve is connected
31934 const unsigned dst_bnd_id =
31935 tmp_polyline_pt->final_vertex_connected_bnd_id();
31936
31937 // Include the destination boundary id to the set of
31938 // boundaries with connections
31940
31941 } // if (tmp_polyline_pt->is_final_vertex_connected())
31942
31943 } // for (p < n_polyline)
31944
31945 } // for (i < n_outer_boundaries)
31946
31947 // ------------------------------------------------------------------
31948 // Internal boundaries
31949 // ------------------------------------------------------------------
31950
31951 // Get the number of internal boundaries (closed boundaries)
31952 const unsigned n_internal_boundaries = this->Internal_polygon_pt.size();
31953
31954 // Loop over the internal boundaries
31955 for (unsigned i = 0; i < n_internal_boundaries; i++)
31956 {
31957 // Get a temporary polygon representation
31958 TriangleMeshPolygon* tmp_polygon_pt = this->Internal_polygon_pt[i];
31959 // Get the number of polylines associated to the current internal
31960 // boundary
31961 const unsigned n_polyline = tmp_polygon_pt->npolyline();
31962 // Loop over the polylines
31963 for (unsigned p = 0; p < n_polyline; p++)
31964 {
31965 // Get a temporary representation of the polyline
31967
31968 // Is the initial vertex connected?
31969 if (tmp_polyline_pt->is_initial_vertex_connected())
31970 {
31971 // Get the boundary id of the current polyline
31972 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
31973
31974 // Include the boundary id to the set of boundaries with
31975 // connections
31977
31978 // Boundary id to which the curve is connecte
31979 const unsigned dst_bnd_id =
31980 tmp_polyline_pt->initial_vertex_connected_bnd_id();
31981
31982 // Include the destination boundary id to the set of
31983 // boundaries with connections
31985
31986 } // if (tmp_polyline_pt->is_initial_vertex_connected())
31987
31988 // Is the final vertex connected?
31989 if (tmp_polyline_pt->is_final_vertex_connected())
31990 {
31991 // Get the boundary id of the current polyline
31992 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
31993
31994 // Include the boundary id to the set of boundaries with
31995 // connections
31997
31998 // Boundary id to which the curve is connected
31999 const unsigned dst_bnd_id =
32000 tmp_polyline_pt->final_vertex_connected_bnd_id();
32001
32002 // Include the destination boundary id to the set of
32003 // boundaries with connections
32005
32006 } // if (tmp_polyline_pt->is_final_vertex_connected())
32007
32008 } // for (p < n_polyline)
32009
32010 } // for (i < n_internal_boundaries)
32011
32012 // ------------------------------------------------------------------
32013 // Open boundaries (nonclosed internal boundaries)
32014 // ------------------------------------------------------------------
32015
32016 // Get the number of internal boundaries (open boundaries)
32017 const unsigned n_open_boundaries = this->Internal_open_curve_pt.size();
32018
32019 // Loop over the internal open boundaries
32020 for (unsigned i = 0; i < n_open_boundaries; i++)
32021 {
32022 // Get a temporary representation for the open curve
32024 this->Internal_open_curve_pt[i];
32025
32026 // Get the number of curve sections associated to the current
32027 // internal open boundary
32028 const unsigned n_curve_section = tmp_open_curve_pt->ncurve_section();
32029
32030 // Loop over the curve section
32031 for (unsigned p = 0; p < n_curve_section; p++)
32032 {
32033 // Get a temporary representation of the curve section
32034 // (polyline)
32036 tmp_open_curve_pt->polyline_pt(p);
32037
32038 // Is the initial vertex connected?
32039 if (tmp_polyline_pt->is_initial_vertex_connected())
32040 {
32041 // Get the boundary id of the current polyline
32042 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32043
32044 // Include the boundary id to the set of boundaries with
32045 // connections
32047
32048 // Boundary id to which the curve is connecte
32049 const unsigned dst_bnd_id =
32050 tmp_polyline_pt->initial_vertex_connected_bnd_id();
32051
32052 // Include the destination boundary id to the set of
32053 // boundaries with connections
32055
32056 } // if (tmp_polyline_pt->is_initial_vertex_connected())
32057
32058 // Is the final vertex connected?
32059 if (tmp_polyline_pt->is_final_vertex_connected())
32060 {
32061 // Get the boundary id of the current polyline
32062 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32063
32064 // Include the boundary id to the set of boundaries with
32065 // connections
32067
32068 // Boundary id to which the curve is connected
32069 const unsigned dst_bnd_id =
32070 tmp_polyline_pt->final_vertex_connected_bnd_id();
32071
32072 // Include the destination boundary id to the set of
32073 // boundaries with connections
32075
32076 } // if (tmp_polyline_pt->is_final_vertex_connected())
32077
32078 } // for (p < n_curve_section)
32079
32080 } // for (i < n_open_boundaries)
32081
32082#ifdef OOMPH_HAS_MPI
32083 // ------------------------------------------------------------------
32084 // Shared boundaries (only for distributed meshes)
32085 // ------------------------------------------------------------------
32086
32087 // Check if we need to include any information associated with
32088 // shared boundaries
32089 if (this->is_mesh_distributed())
32090 {
32091 // Get the rank of the current processor
32092 const unsigned my_rank = this->communicator_pt()->my_rank();
32093
32094 // Get the number of shared curves in the current processor
32095 const unsigned n_shared_curves = this->nshared_boundary_curves(my_rank);
32096
32097 // Loop over the shared curves
32098 for (unsigned i = 0; i < n_shared_curves; i++)
32099 {
32100 // Get the number of polylines associated to the current shared
32101 // curve
32102 const unsigned n_polyline = this->nshared_boundary_polyline(my_rank, i);
32103
32104 // Loop over the polylines associated to the current shared
32105 // curve
32106 for (unsigned p = 0; p < n_polyline; p++)
32107 {
32108 // Get a temporary representation of the shared polyline
32110 this->shared_boundary_polyline_pt(my_rank, i, p);
32111
32112 // Is the initial vertex connected?
32113 if (tmp_polyline_pt->is_initial_vertex_connected())
32114 {
32115 // Get the boundary id of the current polyline
32116 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32117
32118 // Include the boundary id to the set of boundaries with
32119 // connections
32121
32122 // Boundary id to which the curve is connecte
32123 const unsigned dst_bnd_id =
32124 tmp_polyline_pt->initial_vertex_connected_bnd_id();
32125
32126 // Include the destination boundary id to the set of
32127 // boundaries with connections
32129
32130 } // if (tmp_polyline_pt->is_initial_vertex_connected())
32131
32132 // Is the final vertex connected?
32133 if (tmp_polyline_pt->is_final_vertex_connected())
32134 {
32135 // Get the boundary id of the current polyline
32136 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32137
32138 // Include the boundary id to the set of boundaries with
32139 // connections
32141
32142 // Boundary id to which the curve is connected
32143 const unsigned dst_bnd_id =
32144 tmp_polyline_pt->final_vertex_connected_bnd_id();
32145
32146 // Include the destination boundary id to the set of
32147 // boundaries with connections
32149
32150 } // if (tmp_polyline_pt->is_final_vertex_connected())
32151
32152 } // for (p < n_polyline)
32153
32154 } // for (i < n_shared_curves)
32155
32156 } // if (this->is_mesh_distributed())
32157
32158#endif // #ifdef OOMPH_HAS_MPI
32159
32160 // ---------------------------------------------------------------
32161 // Get the nodes sorted by segments of the boundaries with
32162 // connections
32163
32164 // Store the sorted nodes by segments of the boundaries with
32165 // connections
32166 std::map<unsigned, Vector<Vector<Node*>>> bnd_sorted_segment_node_pt;
32167
32168 // Loop over the boundaries with connections
32169 for (std::set<unsigned>::iterator it = boundary_id_with_connections.begin();
32171 it++)
32172 {
32173 // Get the boundary id
32174 const unsigned bnd_id = (*it);
32175#ifdef OOMPH_HAS_MPI
32176 // Working with a distributed mesh
32177 if (this->is_mesh_distributed())
32178 {
32179 // Get the initial shared boundary id
32180 const unsigned init_shd_bnd_id = this->initial_shared_boundary_id();
32181 // Is an original or shared boundary
32182 if (bnd_id >= init_shd_bnd_id)
32183 {
32184 // Is a shared boundary
32185
32186 // Temporary storage for the nodes on the shared boundary
32188
32189 // Get the nodes associated to the shared boundary
32190 get_shared_boundary_segment_nodes_helper(bnd_id, tmp_shared_nodes_pt);
32191
32192 // Store the nodes associated to the shared boundary
32194
32195 } // if (bnd_id >= init_shd_bnd_id)
32196 else
32197 {
32198 // Is an original boundary
32199
32200 // Temporary storage for the nodes on the original boundary
32202
32203 // Get the nodes associated to the shared boundary
32204 get_boundary_segment_nodes_helper(bnd_id, tmp_boundary_nodes_pt);
32205
32206 // Store the nodes associated to the shared boundary
32208
32209 } // if (bnd_id >= init_shd_bnd_id)
32210
32211 } // if (this->is_mesh_distributed())
32212 else
32213#endif // #ifdef OOMPH_HAS_MPI
32214 {
32215 // Is an original boundary
32216
32217 // Temporary storage for the nodes on the original boundary
32219
32220 // Get the nodes associated to the shared boundary
32221 get_boundary_segment_nodes_helper(bnd_id, tmp_boundary_nodes_pt);
32222
32223 // Store the nodes associated to the shared boundary
32225
32226 } // if (this->is_mesh_distributed())
32227
32228 } // Loop over boundaries with connections
32229
32230 // -----------------------------------------------------------------
32231 // Loop again over the boundaries (original and shared) and search
32232 // for the repeated nodes in those boundaries with connections
32233
32234 // ------------------------------------------------------------------
32235 // Outer boundaries
32236 // ------------------------------------------------------------------
32237 // Loop over the outer boundaries
32238 for (unsigned i = 0; i < n_outer_boundaries; i++)
32239 {
32240 // Get a temporary polygon representation
32241 TriangleMeshPolygon* tmp_polygon_pt = this->Outer_boundary_pt[i];
32242 // Get the number of polylines associated to the current outer
32243 // boundary
32244 const unsigned n_polyline = tmp_polygon_pt->npolyline();
32245 // Loop over the polylines
32246 for (unsigned p = 0; p < n_polyline; p++)
32247 {
32248 // Get a temporary representation of the polyline
32250
32251 // Is the initial vertex connected?
32252 if (tmp_polyline_pt->is_initial_vertex_connected())
32253 {
32254 // Get the boundary id of the current polyline
32255 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32256
32257 // Boundary id to which the curve is connected
32258 const unsigned dst_bnd_id =
32259 tmp_polyline_pt->initial_vertex_connected_bnd_id();
32260
32261 // Boundary chunk to which the curve is connected
32262 const unsigned dst_chunk =
32263 tmp_polyline_pt->initial_vertex_connected_n_chunk();
32264
32265 // Get the nodes representation of the current boundary
32268
32269 // Get the nodes representation of the boundary to connect
32272
32273 // Add the repeated node to the list of non delete-able
32274 // vertices
32275 add_non_delete_vertices_from_boundary_helper(
32277
32278 } // if (tmp_polyline_pt->is_initial_vertex_connected())
32279
32280 // Is the final vertex connected?
32281 if (tmp_polyline_pt->is_final_vertex_connected())
32282 {
32283 // Get the boundary id of the current polyline
32284 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32285
32286 // Boundary id to which the curve is connected
32287 const unsigned dst_bnd_id =
32288 tmp_polyline_pt->final_vertex_connected_bnd_id();
32289
32290 // Boundary chunk to which the curve is connected
32291 const unsigned dst_chunk =
32292 tmp_polyline_pt->final_vertex_connected_n_chunk();
32293
32294 // Get the nodes representation of the current boundary
32297
32298 // Get the nodes representation of the boundary to connect
32301
32302 // Add the repeated node to the list of non delete-able
32303 // vertices
32304 add_non_delete_vertices_from_boundary_helper(
32306
32307 } // if (tmp_polyline_pt->is_final_vertex_connected())
32308
32309 } // for (p < n_polyline)
32310
32311 } // for (i < n_outer_boundaries)
32312
32313 // ------------------------------------------------------------------
32314 // Internal boundaries
32315 // ------------------------------------------------------------------
32316 // Loop over the internal boundaries
32317 for (unsigned i = 0; i < n_internal_boundaries; i++)
32318 {
32319 // Get a temporary polygon representation
32320 TriangleMeshPolygon* tmp_polygon_pt = this->Internal_polygon_pt[i];
32321 // Get the number of polylines associated to the current internal
32322 // boundary
32323 const unsigned n_polyline = tmp_polygon_pt->npolyline();
32324 // Loop over the polylines
32325 for (unsigned p = 0; p < n_polyline; p++)
32326 {
32327 // Get a temporary representation of the polyline
32329
32330 // Is the initial vertex connected?
32331 if (tmp_polyline_pt->is_initial_vertex_connected())
32332 {
32333 // Get the boundary id of the current polyline
32334 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32335
32336 // Boundary id to which the curve is connected
32337 const unsigned dst_bnd_id =
32338 tmp_polyline_pt->initial_vertex_connected_bnd_id();
32339
32340 // Boundary chunk to which the curve is connected
32341 const unsigned dst_chunk =
32342 tmp_polyline_pt->initial_vertex_connected_n_chunk();
32343
32344 // Get the nodes representation of the current boundary
32347
32348 // Get the nodes representation of the boundary to connect
32351
32352 // Add the repeated node to the list of non delete-able
32353 // vertices
32354 add_non_delete_vertices_from_boundary_helper(
32356
32357 } // if (tmp_polyline_pt->is_initial_vertex_connected())
32358
32359 // Is the final vertex connected?
32360 if (tmp_polyline_pt->is_final_vertex_connected())
32361 {
32362 // Get the boundary id of the current polyline
32363 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32364
32365 // Boundary id to which the curve is connected
32366 const unsigned dst_bnd_id =
32367 tmp_polyline_pt->final_vertex_connected_bnd_id();
32368
32369 // Boundary chunk to which the curve is connected
32370 const unsigned dst_chunk =
32371 tmp_polyline_pt->final_vertex_connected_n_chunk();
32372
32373 // Get the nodes representation of the current boundary
32376
32377 // Get the nodes representation of the boundary to connect
32380
32381 // Add the repeated node to the list of non delete-able
32382 // vertices
32383 add_non_delete_vertices_from_boundary_helper(
32385
32386 } // if (tmp_polyline_pt->is_final_vertex_connected())
32387
32388 } // for (p < n_polyline)
32389
32390 } // for (i < n_internal_boundaries)
32391
32392 // ------------------------------------------------------------------
32393 // Open boundaries (nonclosed internal boundaries)
32394 // ------------------------------------------------------------------
32395 // Loop over the internal open boundaries
32396 for (unsigned i = 0; i < n_open_boundaries; i++)
32397 {
32398 // Get a temporary representation for the open curve
32400 this->Internal_open_curve_pt[i];
32401
32402 // Get the number of curve sections associated to the current
32403 // internal open boundary
32404 const unsigned n_curve_section = tmp_open_curve_pt->ncurve_section();
32405
32406 // Loop over the curve section
32407 for (unsigned p = 0; p < n_curve_section; p++)
32408 {
32409 // Get a temporary representation of the curve section
32410 // (polyline)
32412 tmp_open_curve_pt->polyline_pt(p);
32413
32414 // Is the initial vertex connected?
32415 if (tmp_polyline_pt->is_initial_vertex_connected())
32416 {
32417 // Get the boundary id of the current polyline
32418 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32419
32420 // Boundary id to which the curve is connected
32421 const unsigned dst_bnd_id =
32422 tmp_polyline_pt->initial_vertex_connected_bnd_id();
32423
32424 // Boundary chunk to which the curve is connected
32425 const unsigned dst_chunk =
32426 tmp_polyline_pt->initial_vertex_connected_n_chunk();
32427
32428 // Get the nodes representation of the current boundary
32431
32432 // Get the nodes representation of the boundary to connect
32435
32436 // Add the repeated node to the list of non delete-able
32437 // vertices
32438 add_non_delete_vertices_from_boundary_helper(
32440
32441 } // if (tmp_polyline_pt->is_initial_vertex_connected())
32442
32443 // Is the final vertex connected?
32444 if (tmp_polyline_pt->is_final_vertex_connected())
32445 {
32446 // Get the boundary id of the current polyline
32447 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32448
32449 // Boundary id to which the curve is connected
32450 const unsigned dst_bnd_id =
32451 tmp_polyline_pt->final_vertex_connected_bnd_id();
32452
32453 // Boundary chunk to which the curve is connected
32454 const unsigned dst_chunk =
32455 tmp_polyline_pt->final_vertex_connected_n_chunk();
32456
32457 // Get the nodes representation of the current boundary
32460
32461 // Get the nodes representation of the boundary to connect
32464
32465 // Add the repeated node to the list of non delete-able
32466 // vertices
32467 add_non_delete_vertices_from_boundary_helper(
32469
32470 } // if (tmp_polyline_pt->is_final_vertex_connected())
32471
32472 } // for (p < n_curve_section)
32473
32474 } // for (i < n_open_boundaries)
32475
32476#ifdef OOMPH_HAS_MPI
32477 // ------------------------------------------------------------------
32478 // Shared boundaries (only for distributed meshes)
32479 // ------------------------------------------------------------------
32480
32481 // Check if we need to include any information associated with
32482 // shared boundaries
32483 if (this->is_mesh_distributed())
32484 {
32485 // Get the rank of the current processor
32486 const unsigned my_rank = this->communicator_pt()->my_rank();
32487
32488 // Get the number of shared curves in the current processor
32489 const unsigned n_shared_curves = this->nshared_boundary_curves(my_rank);
32490
32491 // Loop over the shared curves
32492 for (unsigned i = 0; i < n_shared_curves; i++)
32493 {
32494 // Get the number of polylines associated to the current shared
32495 // curve
32496 const unsigned n_polyline = this->nshared_boundary_polyline(my_rank, i);
32497
32498 // Loop over the polylines associated to the current shared
32499 // curve
32500 for (unsigned p = 0; p < n_polyline; p++)
32501 {
32502 // Get a temporary representation of the shared polyline
32504 this->shared_boundary_polyline_pt(my_rank, i, p);
32505
32506 // Is the initial vertex connected?
32507 if (tmp_polyline_pt->is_initial_vertex_connected())
32508 {
32509 // Get the boundary id of the current polyline
32510 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32511
32512 // Boundary id to which the curve is connected
32513 const unsigned dst_bnd_id =
32514 tmp_polyline_pt->initial_vertex_connected_bnd_id();
32515
32516 // Boundary chunk to which the curve is connected
32517 const unsigned dst_chunk =
32518 tmp_polyline_pt->initial_vertex_connected_n_chunk();
32519
32520 // Get the nodes representation of the current boundary
32523
32524 // Get the nodes representation of the boundary to connect
32527
32528 // Add the repeated node to the list of non delete-able
32529 // vertices
32530 add_non_delete_vertices_from_boundary_helper(
32532
32533 } // if (tmp_polyline_pt->is_initial_vertex_connected())
32534
32535 // Is the final vertex connected?
32536 if (tmp_polyline_pt->is_final_vertex_connected())
32537 {
32538 // Get the boundary id of the current polyline
32539 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32540
32541 // Boundary id to which the curve is connected
32542 const unsigned dst_bnd_id =
32543 tmp_polyline_pt->final_vertex_connected_bnd_id();
32544
32545 // Boundary chunk to which the curve is connected
32546 const unsigned dst_chunk =
32547 tmp_polyline_pt->final_vertex_connected_n_chunk();
32548
32549 // Get the nodes representation of the current boundary
32552
32553 // Get the nodes representation of the boundary to connect
32556
32557 // Add the repeated node to the list of non delete-able
32558 // vertices
32559 add_non_delete_vertices_from_boundary_helper(
32561
32562 } // if (tmp_polyline_pt->is_final_vertex_connected())
32563
32564 } // for (p < n_polyline)
32565
32566 } // for (i < n_shared_curves)
32567
32568 } // if (this->is_mesh_distributed())
32569
32570#endif // #ifdef OOMPH_HAS_MPI
32571 }
32572
32573 //=========================================================================
32574 /// Adds the vertices from the sources boundary that are
32575 /// repeated in the destination boundary to the list of non
32576 /// delete-able vertices in the destination boundary
32577 //=========================================================================
32578 template<class ELEMENT>
32583 const unsigned& dst_bnd_id,
32584 const unsigned& dst_bnd_chunk)
32585 {
32586 // Get the number of segments in the source boundary
32587 const unsigned n_seg = src_bound_segment_node_pt.size();
32588 // Loop over the segments in the source boundary
32589 for (unsigned iseg = 0; iseg < n_seg; iseg++)
32590 {
32591 // Get the number of nodes in the current segment
32592 const unsigned nnode = src_bound_segment_node_pt[iseg].size();
32593 // Get the left and right node of the current segment
32596
32597 // Get the number of segments in the destination boundary
32598 const unsigned n_dst_seg = dst_bound_segment_node_pt.size();
32599 // Loop over the segments in the destination boundary
32600 for (unsigned jseg = 0; jseg < n_dst_seg; jseg++)
32601 {
32602 // Get the number of nodes on the current destination segment
32603 const unsigned n_dst_node = dst_bound_segment_node_pt[jseg].size();
32604 // Loop over the nodes until the node has been found or we have
32605 // visited all the nodes
32606 for (unsigned jnode = 0; jnode < n_dst_node; jnode++)
32607 {
32608 // Get a pointer to the jnode in the destination segment
32609 // boundary
32611 // Is the node the same as the left or right node if
32612 // the source segment boundary
32614 {
32615 // We have foud the node to connect, get the vertex of the node
32617 vertex[0] = tmp_node_pt->x(0);
32618 vertex[1] = tmp_node_pt->x(1);
32619
32620 // Establish the vertex coordinate as untouchable in the
32621 // destination boundary during the adaptation process. It
32622 // means that unrefinement can not take off the vertices
32623 // that receive connections in the destination boundary
32624 Boundary_connections_pt[dst_bnd_id].insert(vertex);
32625 // Boundary_chunk_connections_pt[dst_bnd_id][dst_bnd_chunk].
32626 // insert(vertex);
32627
32628 // return
32629 return;
32630
32631 } // if (tmp_node_pt == left_node_pt)
32632 else if (tmp_node_pt == right_node_pt)
32633 {
32634 // We have foud the node to connect, get the vertex of the node
32636 vertex[0] = tmp_node_pt->x(0);
32637 vertex[1] = tmp_node_pt->x(1);
32638
32639 // Establish the vertex coordinate as untouchable in the
32640 // destination boundary during the adaptation process. It
32641 // means that unrefinement can not take off the vertices
32642 // that receive connections in the destination boundary
32643 // Boundary_chunk_connections_pt[dst_bnd_id][dst_bnd_chunk].
32644 // insert(vertex);
32645 Boundary_connections_pt[dst_bnd_id].insert(vertex);
32646
32647 // return
32648 return;
32649
32650 } // else if (tmp_node_pt == right_node_pt)
32651
32652 } // for (jnode < n_dst_node)
32653
32654 } // for (jseg < n_dst_seg)
32655
32656 } // for (iseg < n_seg)
32657 }
32658
32659#ifdef OOMPH_HAS_MPI
32660 //=========================================================================
32661 /// Synchronise the vertices that are marked for non deletion
32662 // on the shared boundaries. Unrefinement of shared boundaries is
32663 // performed only if the candidate node is not marked for non deletion
32664 //=========================================================================
32665 template<class ELEMENT>
32666 const void RefineableTriangleMesh<
32667 ELEMENT>::synchronize_shared_boundary_connections()
32668 {
32669 // Get the number of processors
32670 const unsigned nproc = this->communicator_pt()->nproc();
32671 // Get my rank
32672 const unsigned my_rank = this->communicator_pt()->my_rank();
32673
32674 // loop over the processors
32675 for (unsigned jproc = 0; jproc < nproc; jproc++)
32676 {
32677 // The number of boundaries shared with the current processor
32678 // (if jproc==my_rank then there are no shared boundaries
32679 // between them)
32680 const unsigned n_shd_bnd_jproc = this->nshared_boundaries(my_rank, jproc);
32681
32682 // Are there shared boundaries with the jproc processor?
32683 // There are no info. with myself
32684 if (jproc != my_rank && n_shd_bnd_jproc > 0)
32685 {
32686 // Storage for the boundaries ids with vertices for non
32687 // deletion
32689
32690 // Storage for chunk numbers of boundaries with vertices
32691 // for non deletion
32693
32694 // The number of vertices for nondeletion in the shared
32695 // boundaries
32697
32698 // Vertices marked for nondeletion in shared boundaries
32700
32701 // Get the boundary ids of the shared boundaries with jproc
32702 // processor
32704 this->shared_boundaries_ids(my_rank, jproc);
32705
32706 // Get the number of shared boundaries with jproc
32707 const unsigned n_shd_bnd_jproc = shd_bnd_ids.size();
32708 // loop over the shared boundaries with jproc
32709 for (unsigned ishd_bnd = 0; ishd_bnd < n_shd_bnd_jproc; ishd_bnd++)
32710 {
32711 // Get the shared boudary id
32712 const unsigned shd_bnd_id = shd_bnd_ids[ishd_bnd];
32713 // Get the associated polyline
32715 this->boundary_polyline_pt(shd_bnd_id);
32716 // Get the chunk number
32717 const unsigned chunk = shd_polyline_pt->boundary_chunk();
32718
32719 // Store the vertices not allowed for deletion
32720 std::set<Vector<double>> no_delete_vertex;
32721
32722 // Does the boundary has vertives for nondeleteion?
32723 const bool boundary_receive_connections =
32724 this->boundary_connections(shd_bnd_id, chunk, no_delete_vertex);
32725
32726 // Get the number of vertices for nondeletion
32727 const unsigned n_non_delete_vertex = no_delete_vertex.size();
32728
32729 // Are there vertices for nondeletion?
32731 {
32732 // Add the shared boundary id
32734 // Add the chunk number
32735 chunk_for_non_deletion.push_back(chunk);
32736 // Add the number of vertices for non deletion
32738
32739 // The list of vertices to add
32741
32742 // Add the vertices for non deletion
32743 for (std::set<Vector<double>>::iterator it =
32744 no_delete_vertex.begin();
32745 it != no_delete_vertex.end();
32746 it++)
32747 {
32748 // Get the vertex coordinate
32749 Vector<double> vertex = (*it);
32750 tmp_vertices.push_back(vertex);
32751 }
32752
32753 // Add the vertices coordinates to a vector storage
32755
32756 } // if (boundary_receive_connections && n_non_delete_vertex > 0)
32757
32758 } // for (ishd_bnd<n_shd_bnd_jproc)
32759
32760 // ----------------------------------------------------------
32761 // ----------------------------------------------------------
32762 // ----------------------------------------------------------
32763 // Now send the info. to the other processor (jproc)
32764 // ----------------------------------------------------------
32765 // ----------------------------------------------------------
32766 // ----------------------------------------------------------
32767 // Get the communicator of the mesh
32768 OomphCommunicator* comm_pt = this->communicator_pt();
32769
32770 // Set MPI info
32773
32774 // -----------------------------------------------------------
32775 // Prepare the data
32776 // Get the number of shared boundaires with vertices marked
32777 // for non deletion
32778 const unsigned n_shd_bnd_with_non_delete_vertices =
32780
32781 // Size of the package
32782 const unsigned size_package = 3;
32783 // Ndata to send
32784 const unsigned n_unsigned_data_to_send =
32786 // The flat package to send the info.
32789
32792
32793 // Prepare the data to be sent
32794 unsigned j = 0;
32795 for (unsigned i = 0; i < n_shd_bnd_with_non_delete_vertices; i++)
32796 {
32797 // The shared boundary id
32799 // The chunk number
32801 // The number of vertices for nondeletion
32803 // Also package the vertices
32804 const unsigned n_vertices_non_deletion =
32806 // Loop over the vertices and store them in the flat
32807 // package to be sent
32808 for (unsigned h = 0; h < n_vertices_non_deletion; h++)
32809 {
32810 flat_package_double_send.push_back(
32812 flat_package_double_send.push_back(
32814 } // for (h<n_vertices_non_deletion)
32815
32816 } // for (i<n_shd_bnd_with_non_delete_vertices)
32817
32818 // ----------------------------------------------------------
32819 int send_proc = jproc;
32820 int recv_proc = jproc;
32823 //-----------------------------------------------------------
32824 // Do the transfering of info.
32825 //-----------------------------------------------------------
32826 // Start with UNSIGNED info.
32828 1,
32830 send_proc,
32831 1,
32832 comm_pt->mpi_comm(),
32833 &request);
32834
32835 unsigned receive_count_unsigned_values = 0;
32837 1,
32839 recv_proc,
32840 1,
32841 comm_pt->mpi_comm(),
32842 &status);
32843
32845
32846 // Send the actual data
32848 {
32852 send_proc,
32853 2,
32854 comm_pt->mpi_comm(),
32855 &request);
32856 }
32857
32858 // Receive the actual data
32860 {
32865 recv_proc,
32866 2,
32867 comm_pt->mpi_comm(),
32868 &status);
32869 }
32870
32871 // Wait for sending the data and the other processor
32872 // receives
32874 {
32876 }
32877
32878 //-----------------------------------------------------------
32879 // Then continue with DOUBLE info.
32881 1,
32883 send_proc,
32884 1,
32885 comm_pt->mpi_comm(),
32886 &request);
32887
32888 unsigned receive_count_double_values = 0;
32890 1,
32892 recv_proc,
32893 1,
32894 comm_pt->mpi_comm(),
32895 &status);
32896
32898
32899 // Send the actual data
32900 if (send_count_double_values != 0)
32901 {
32904 MPI_DOUBLE,
32905 send_proc,
32906 2,
32907 comm_pt->mpi_comm(),
32908 &request);
32909 }
32910
32911 // Receive the actual data
32913 {
32917 MPI_DOUBLE,
32918 recv_proc,
32919 2,
32920 comm_pt->mpi_comm(),
32921 &status);
32922 }
32923
32924 // Wait for sending the data and the other processor
32925 // receives
32926 if (send_count_double_values != 0)
32927 {
32929 }
32930
32931 // ------------------------------------------------------------
32932 // ------------------------------------------------------------
32933 // ------------------------------------------------------------
32934 // Now unpackage the data
32935 // ------------------------------------------------------------
32936 // ------------------------------------------------------------
32937 // ------------------------------------------------------------
32938
32939 // Storage for the boundaries ids with vertices for non
32940 // deletion
32942
32943 // Storage for chunk numbers of boundaries with vertices
32944 // for non deletion
32946
32947 // The number of vertices for nondeletion in the shared
32948 // boundaries
32950
32951 // Vertices marked for nondeletion in shared boundaries
32953
32954 // Counter
32955 j = 0;
32956 for (unsigned i = 0; i < receive_count_unsigned_values; i += 3)
32957 {
32958 // Get the shared boundary id
32961 // Get the chunk number
32962 const unsigned recv_chunk = flat_package_unsigned_recv[i + 1];
32964 // Get the number of vertices for non deletion
32965 const unsigned recv_num_vertices = flat_package_unsigned_recv[i + 2];
32967
32968 // Create a temporal storage
32970 // Now get the vertices
32971 for (unsigned h = 0; h < recv_num_vertices; h++)
32972 {
32976 // Add the vertex to the vector of vertices
32977 temp_recv_vertices.push_back(tmp_vertex);
32978 } // for (h<recv_num_vertices)
32979
32980 // Add the vertices to the vector of vertices
32982
32983 } // for(i<receive_count_unsigned_values)
32984
32985 // ---------------------------------------------------------
32986 // ---------------------------------------------------------
32987 // ---------------------------------------------------------
32988 // Now add the vertices to the data structures to mark them
32989 // as non delete-able
32990 // ---------------------------------------------------------
32991 // ---------------------------------------------------------
32992 // ---------------------------------------------------------
32993
32994 // Get the number of shd boundaries that have vertices
32995 // marked for non deletion
32996 const unsigned n_recv_shd_bnd_id_for_non_deletion =
32998 // loop over the shared boundaries and add the data for non
32999 // deletion
33000 for (unsigned i = 0; i < n_recv_shd_bnd_id_for_non_deletion; i++)
33001 {
33002 // Get the shared boundary id.
33004 // Get the chunk number
33006 // Increase and decrease the chunk number to avoid the
33007 // warning when compiling without PARANOID
33008 chunk++;
33009 chunk--;
33010
33011 // Get the number of vertices marked for non deletion
33013 // Add all the vertices
33014 for (unsigned h = 0; h < n_vertices; h++)
33015 {
33016 // Get the vertex
33020 // Add the vertex to the data structure for non
33021 // deletion
33022 // Boundary_chunk_connections_pt[shd_bnd_id][chunk].
33023 // insert(vertex);
33024 Boundary_connections_pt[shd_bnd_id].insert(vertex);
33025
33026 } // for (h<n_vertices)
33027
33028 } // for (i<n_recv_shd_bnd_id_for_non_deletion)
33029
33030 } // if (jproc != my_rank && n_shd_bnd_jproc > 0)
33031
33032 } // for (jproc < nproc)
33033 }
33034#endif // #ifdef OOMPH_HAS_MPI
33035
33036 //=========================================================================
33037 /// After unrefinement and refinement has taken place compute
33038 /// the new vertices numbers of the temporary representation of the
33039 // boundaries to connect.
33040 //=========================================================================
33041 template<class ELEMENT>
33045 {
33046 // Dummy storages
33049
33050 // Clear the storage
33053
33054 // Get the initial shared boundary id (to check whether the
33055 // polylines represent original or shared boundaries)
33056 const unsigned init_shd_bnd_id = this->initial_shared_boundary_id();
33057
33058 // ------------------------------------------------------------------
33059 // This seems unnecesary since the outer polygons does not create
33060 // connections with other boundaries (the original ones)
33061 // ------------------------------------------------------------------
33062 // Unnecessary?
33063 // ------------------------------------------------------------------
33064
33065 // Loop over the temporary outer polygons create the connection
33066 // information of those boundaries marked to be connected at their
33067 // ends
33068
33069 // ------------------------------------------------------------------
33070 // Temporary outer polygons
33071 // ------------------------------------------------------------------
33072
33073 // Get the number of outer boundaries (closed boundaries)
33075
33076 // Loop over the outer boundaries
33077 for (unsigned i = 0; i < n_outer_boundaries; i++)
33078 {
33079 // Get a temporary polygon representation
33081 // Get the number of polylines associated to the current outer
33082 // boundary
33083 const unsigned n_polyline = tmp_polygon_pt->npolyline();
33084 // Loop over the polylines
33085 for (unsigned p = 0; p < n_polyline; p++)
33086 {
33087 // Get a temporary representation of the polyline
33089
33090 // Get the boundary id
33091 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
33092
33093 // Is the boundary to connect a shared boundary
33094 if (bnd_id < init_shd_bnd_id)
33095 {
33096 // Restore the connections of the current polyline
33097 restore_polyline_connections_helper(
33101
33102 } // if (bnd_id < init_shd_bnd_id)
33103
33104 } // for (p < n_polyline)
33105
33106 } // for (i < n_outer_boundaries)
33107
33108 // ------------------------------------------------------------------
33109 // Unnecessary?
33110 // ------------------------------------------------------------------
33111
33112 // ------------------------------------------------------------------
33113 // Temporary open boundaries (nonclosed internal boundaries)
33114 // ------------------------------------------------------------------
33115
33116 // Get the number of internal boundaries (open boundaries)
33117 const unsigned n_open_boundaries = tmp_open_curves_pt.size();
33118
33119 // Loop over the internal open boundaries
33120 for (unsigned i = 0; i < n_open_boundaries; i++)
33121 {
33122 // Get a temporary representation for the open curve
33124
33125 // Get the number of curve sections associated to the current
33126 // internal open boundary
33127 const unsigned n_curve_section = tmp_open_curve_pt->ncurve_section();
33128
33129 // Loop over the curve section
33130 for (unsigned p = 0; p < n_curve_section; p++)
33131 {
33132 // Get a temporary representation of the curve section
33133 // (polyline)
33135 tmp_open_curve_pt->polyline_pt(p);
33136
33137 // Get the boundary id
33138 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
33139
33140 // Is the boundary to connect a shared boundary
33141 if (bnd_id < init_shd_bnd_id)
33142 {
33143 // Restore the connections of the current polyline
33144 restore_polyline_connections_helper(
33148
33149 } // if (bnd_id < init_shd_bnd_id)
33150
33151 } // for (p < n_curve_section)
33152
33153 } // for (i < n_open_boundaries)
33154 }
33155
33156 //=========================================================================
33157 /// After unrefinement and refinement has taken place compute
33158 /// the new vertices numbers of the boundaries to connect (in a
33159 /// distributed scheme it may be possible that the destination
33160 /// boundary does no longer exist, therefore the connection is
33161 /// suspended. It is not permanently deleted because if load balance
33162 /// takes place it may be possible that the boundary to connect be
33163 /// part of the new domain representation, so the connection would
33164 /// exist)
33165 //=========================================================================
33166 template<class ELEMENT>
33170 {
33171 // Clear the storage
33174
33175 // Loop over the boundaries in the domain (outer, internal -- closed
33176 // and open) and restore the connection information of those
33177 // boundaries marked to be connected at their ends
33178
33179 // ------------------------------------------------------------------
33180 // Outer boundaries
33181 // ------------------------------------------------------------------
33182
33183 // Get the number of outer boundaries (closed boundaries)
33184 const unsigned n_outer_boundaries = this->Outer_boundary_pt.size();
33185
33186 // Loop over the outer boundaries
33187 for (unsigned i = 0; i < n_outer_boundaries; i++)
33188 {
33189 // Get a temporary polygon representation
33190 TriangleMeshPolygon* tmp_polygon_pt = this->Outer_boundary_pt[i];
33191 // Get the number of polylines associated to the current outer
33192 // boundary
33193 const unsigned n_polyline = tmp_polygon_pt->npolyline();
33194 // Loop over the polylines
33195 for (unsigned p = 0; p < n_polyline; p++)
33196 {
33197 // Get a temporary representation of the polyline
33199
33200 // Restore the connections of the current polyline
33201 restore_polyline_connections_helper(
33205
33206 } // for (p < n_polyline)
33207
33208 } // for (i < n_outer_boundaries)
33209
33210 // ------------------------------------------------------------------
33211 // Internal boundaries
33212 // ------------------------------------------------------------------
33213
33214 // Get the number of internal boundaries (closed boundaries)
33215 const unsigned n_internal_boundaries = this->Internal_polygon_pt.size();
33216
33217 // Loop over the internal boundaries
33218 for (unsigned i = 0; i < n_internal_boundaries; i++)
33219 {
33220 // Get a temporary polygon representation
33221 TriangleMeshPolygon* tmp_polygon_pt = this->Internal_polygon_pt[i];
33222 // Get the number of polylines associated to the current internal
33223 // boundary
33224 const unsigned n_polyline = tmp_polygon_pt->npolyline();
33225 // Loop over the polylines
33226 for (unsigned p = 0; p < n_polyline; p++)
33227 {
33228 // Get a temporary representation of the polyline
33230
33231 // Restore the connections of the current polyline
33232 restore_polyline_connections_helper(
33236
33237 } // for (p < n_polyline)
33238
33239 } // for (i < n_internal_boundaries)
33240
33241 // ------------------------------------------------------------------
33242 // Open boundaries (nonclosed internal boundaries)
33243 // ------------------------------------------------------------------
33244
33245 // Get the number of internal boundaries (open boundaries)
33246 const unsigned n_open_boundaries = this->Internal_open_curve_pt.size();
33247
33248 // Loop over the internal open boundaries
33249 for (unsigned i = 0; i < n_open_boundaries; i++)
33250 {
33251 // Get a temporary representation for the open curve
33253 this->Internal_open_curve_pt[i];
33254
33255 // Get the number of curve sections associated to the current
33256 // internal open boundary
33257 const unsigned n_curve_section = tmp_open_curve_pt->ncurve_section();
33258
33259 // Loop over the curve section
33260 for (unsigned p = 0; p < n_curve_section; p++)
33261 {
33262 // Get a temporary representation of the curve section
33263 // (polyline)
33265 tmp_open_curve_pt->polyline_pt(p);
33266
33267 // Restore the connections of the current polyline
33268 restore_polyline_connections_helper(
33272
33273 } // for (p < n_curve_section)
33274
33275 } // for (i < n_open_boundaries)
33276 }
33277
33278 //=========================================================================
33279 /// Restore the connections of the specific polyline
33280 /// The vertices numbering on the destination boundaries may have
33281 /// change because of (un)refinement in the destination boundaries.
33282 /// Also deals with connection that do not longer exist because the
33283 /// destination boundary does no longer exist because of the distribution
33284 /// process
33285 //=========================================================================
33286 template<class ELEMENT>
33288 TriangleMeshPolyLine* polyline_pt,
33291 {
33292 // If the polyline is connected at any of its ends compute the new
33293 // vertex number on the destination boundary
33294
33295 // ------------------------------------------------------------------
33296 // Is the initial vertex connected?
33297 if (polyline_pt->is_initial_vertex_connected())
33298 {
33299 // The pointer to the boundary to connect
33301
33302 // Get the boundary id of the destination/connected boundary
33303 const unsigned dst_bnd_id_initial =
33304 polyline_pt->initial_vertex_connected_bnd_id();
33305
33306 // Get the initial vertex on the current boundary
33308 polyline_pt->vertex_coordinate(0);
33309
33310#ifdef PARANOID
33311 // Is the mesh distributed?
33312#ifdef OOMPH_HAS_MPI
33313 if (this->is_mesh_distributed())
33314 {
33315 // Get the initial shared boundary id
33316 const unsigned init_shd_bnd_id = this->initial_shared_boundary_id();
33317 // Is the boundary to connect a shared boundary
33319 {
33320 // Get the current polyline original boundary id
33321 const unsigned bnd_id = polyline_pt->boundary_id();
33322 std::ostringstream error_message;
33323 error_message
33324 << "INITIAL VERTEX CONNECTION\n"
33325 << "The current original boundary is trying to connect to a\n"
33326 << "shared boundary, this is not allowed. In this case the\n"
33327 << "shared boundary should be the one that connects with the\n"
33328 << "original boundary\n"
33329 << "The current original boundary (" << bnd_id << ") is marked\n"
33330 << "to have a connection at the\nINITIAL vertex ("
33332 << src_vertex_coordinates_initial[1] << ")\n"
33333 << "with the shared boundary (" << dst_bnd_id_initial << ")\n"
33334 << "This is the list of vertices on the shared destination "
33335 "boundary\n";
33336 // Get the pointer to the associated polyline by using the
33337 // boundary id
33339 this->boundary_polyline_pt(dst_bnd_id_initial);
33340 // The number of vertices on the destination boundary
33341 const unsigned n_vertex_dst_boundary = dst_polyline->nvertex();
33342 // Loop over the vertices print them
33343 for (unsigned i = 0; i < n_vertex_dst_boundary; i++)
33344 {
33345 Vector<double> current_vertex = dst_polyline->vertex_coordinate(i);
33346 error_message << "Vertex#(i): (" << current_vertex[0] << ", "
33347 << current_vertex[1] << ")\n";
33348 }
33349 throw OomphLibError(
33350 error_message.str(),
33351 "RefineableTriangleMesh::restore_polyline_connections_helper()",
33353 } // if (dst_bnd_id_initial >= init_shd_bnd_id)
33354
33355 } // if (this->is_mesh_distributed())
33356#endif // #ifdef OOMPH_HAS_MPI
33357
33358#endif // #ifdef PARANOID
33359
33360 // Flag to indicate if the vertex was found on the destination
33361 // boundary
33363
33364 // Flag that stores the chunk number to connect (only used in
33365 // distributed meshes)
33366 unsigned sub_poly_to_connect = 0;
33367
33368 // Store the vertex number on the destination boundary
33369 unsigned n_vertex_connection_initial = 0;
33370
33371 // Flags only used in a distributed mesh
33372 // ----------------------------------------
33373 // Flag to indicate we are trying to connect to an split boundary
33375
33376 // Flag to indicate we are trying to connecto to an internal
33377 // boundary that is overlaped by a shared boundary)
33379
33380#ifdef OOMPH_HAS_MPI
33381 if (this->is_mesh_distributed())
33382 {
33383 // We can only connect to an original boundary, check if the
33384 // boundary was splitted during the distribution process to
33385 // consider all the chunks (sub-polylines) of the boundary
33386 if (this->boundary_was_splitted(dst_bnd_id_initial))
33387 {
33389 } // if (this->boundary_was_splitted(dst_bnd_id_initial))
33390
33391 // Check if the destination boundary, or any of its chunks is
33392 // marked to be overlapped by a shared boundary, if that is the
33393 // case we can only connect to the chunks that are not
33394 // overlapped by shared boundaries (the shared boundaries are in
33395 // charge of generating the connections with original boundaries
33396 // and with themselves)
33398 {
33399 // Get the number of chucks that represent the destination
33400 // boundary
33401 const unsigned n_sub_poly =
33402 this->nboundary_subpolylines(dst_bnd_id_initial);
33403 // Now loop over the chunks of the destination boundary and if
33404 // any of them is marked to be overlaped by a shared boundary
33405 // then set the flag and break the loop
33406 for (unsigned ii = 0; ii < n_sub_poly; ii++)
33407 {
33408 if (this->boundary_marked_as_shared_boundary(dst_bnd_id_initial,
33409 ii))
33410 {
33411 // Mark the boundary as being overlaped by a shared
33412 // boundary
33414 // Break, no need to look for more overlapings
33415 break;
33416 } // if (boundary_marked_as_shared_boundary(...))
33417 } // for (ii < n_sub_poly)
33418 } // if (connecting_to_an_split_boundary)
33419 else
33420 {
33421 // If not connecting to an split boundary then check if the
33422 // whole destination boundary is overlaped by an internal
33423 // boundary
33424 if (this->boundary_marked_as_shared_boundary(dst_bnd_id_initial, 0))
33425 {
33426 // Mark the boundary as being overlaped by a shared boundary
33428 } // if (boundary_marked_as_shared_boundary(...))
33429 } // else if (connecting_to_an_split_boundary)
33430
33431 } // if (this->is_mesh_distributed())
33432
33433#endif // #ifdef OOMPH_HAS_MPI
33434
33435 // If we are connecting neither to an split boundary nor an
33436 // overlaped boundary then get the pointer to the original
33437 // boundary
33440 {
33441 // Get the polyline pointer representing the destination
33442 // boundary
33443 poly_to_connect_pt = this->boundary_polyline_pt(dst_bnd_id_initial);
33444 } // else if (NOT split, NOT overlaped)
33445
33446 // Now look for the vertex number on the destination boundary(ies)
33447 // -- in case that the boundary was split ---
33448
33449 // Do not check for same orientation, that was previously worked
33450 // by interchanging the connections boundaries (if necessary)
33451
33452 // If the boundary was not split then ...
33454 {
33455 // ... check if the boundary is marked to be overlaped by
33456 // a shared boundary
33458 {
33459 // If that is not the case then we can safely look for the
33460 // vertex number on the destination boundary
33462 this->get_connected_vertex_number_on_destination_polyline(
33466
33467 } // if (!connecting_to_an_overlaped_boundary)
33468 else
33469 {
33470 // If the whole boundary is marked to be overlaped by a shared
33471 // boundary then do nothing, the shared boundaries are already
33472 // in charge of performing the connection (it will be required
33473 // to disabled the connection) with the original boundary
33474
33475 } // else if (!connecting_to_an_overlaped_boundary)
33476
33477 } // if (!connecting_to_an_split_boundary)
33478#ifdef OOMPH_HAS_MPI
33479 else
33480 {
33481 // If the boundary was split then we need to look for the vertex
33482 // in the sub-polylines
33483
33484 // Get the sub-polylines vector
33486 this->boundary_subpolylines(dst_bnd_id_initial);
33487
33488 // Get the number of sub-polylines
33489 const unsigned nsub_poly = tmp_vector_subpolylines.size();
33490#ifdef PARANOID
33491 if (nsub_poly <= 1)
33492 {
33493 std::ostringstream error_message;
33494 error_message << "The boundary (" << dst_bnd_id_initial << ") was "
33495 << "marked to be splitted but\n"
33496 << "there are only (" << nsub_poly << ") polylines to "
33497 << "represent it.\n";
33498 throw OomphLibError(
33499 error_message.str(),
33500 "RefineableTriangleMesh::restore_polyline_connections_helper()",
33502 } // if (nsub_poly <= 1)
33503#endif
33504 // We need to check if the boundary is marked to be overlaped by
33505 // a shared boundary, if that is the case we need to check for
33506 // each indivual subpolyline, and for those overlaped by a
33507 // shared polyline do nothing, the shared polylines have already
33508 // deal with these connections
33509
33510 // ... check if the boundary is marked to be overlaped by
33511 // a shared boundary
33513 {
33514 // The boundary is not overlapped by shared boundaries, we can
33515 // work without checking the subpolylines individually (non of
33516 // them are overlapped by a shared boundary)
33517
33518 // Look for the vertex number to connect on each of the
33519 // subpolyines
33520 for (unsigned isub = 0; isub < nsub_poly; isub++)
33521 {
33522 // Assign the pointer to the sub-polyline
33524 // Search for the vertex in the current sub-polyline
33526 this->get_connected_vertex_number_on_destination_polyline(
33530
33531 // If we have found the vertex to connect then break the
33532 // loop
33534 {
33535 // But first save the subpoly number (chunk), that will be
33536 // used to perform the connection
33538 break;
33539 } // if (found_vertex_on_dst_boundary_initial)
33540
33541 } // for (isub < nsub_poly)
33542
33543 } // if (!connecting_to_an_overlaped_boundary)
33544 else
33545 {
33546 // If connecting to an overlapped boundary then we ignore the
33547 // subpolylines overlapped by shared boundaries and only look
33548 // on the sub-polylines that are not marked as being overlaped
33549 // by shared boundaries
33550
33551 // Look for the vertex number to connect on each of the
33552 // subpolyines
33553 for (unsigned isub = 0; isub < nsub_poly; isub++)
33554 {
33555 // Only work with those sub-polylines that are not overlaped
33556 // by shared boundaries
33557 if (!this->boundary_marked_as_shared_boundary(dst_bnd_id_initial,
33558 isub))
33559 {
33560 // Assign the pointer to the sub-polyline
33562
33563 // Search for the vertex in the current sub-polyline
33565 this->get_connected_vertex_number_on_destination_polyline(
33569
33570 // Was the vertex found?
33572 {
33573 // But first save the subpoly number (chunk), that will
33574 // be used to perform the connection
33576 break;
33577 } // if (found_vertex_on_dst_boundary_initial)
33578
33579 } // if (not overlaped by shared boundary)
33580
33581 } // for (isub < nsub_poly)
33582
33583 } // else if (!connecting_to_an_overlaped_boundary)
33584
33585 } // else if (!connecting_to_an_split_boundary)
33586#endif // #ifdef OOMPH_HAS_MPI
33587
33588 // If not found it may be that the connection information is
33589 // inverted
33591 {
33592 // Is the mesh distributed?
33593#ifdef OOMPH_HAS_MPI
33594 if (this->is_mesh_distributed())
33595 {
33596 // If the mesh is distributed and the vertex number was not
33597 // found, that means that the boundary (or vertex) to connect
33598 // in the destination boundary is not in the current
33599 // processor. In that case suspend the connection
33600 polyline_pt->suspend_initial_vertex_connected();
33601 // Add the polyline to the vector of polylines whose
33602 // connection will be resumed at the end of the adaptation
33603 // process
33604 resume_initial_connection_polyline_pt.push_back(polyline_pt);
33605 // The shared boundaries are marked to connect to the initial
33606 // vertex of the polyline (remember that a shared boundary
33607 // stops adding nodes when it finds a node on an original
33608 // boundary) -- The initial vertex is now a base node
33609 }
33610 else
33611#endif // #ifdef OOMPH_HAS_MPI
33612 {
33613#ifdef PARANOID
33614 // If not found then there is a problem with the vertices
33615 // Get the associated boundary id of the current polyline
33616 const unsigned bnd_id = polyline_pt->boundary_id();
33617 std::ostringstream error_message;
33618 error_message
33619 << "INITIAL VERTEX CONNECTION\n"
33620 << "It was not possible to find the associated "
33621 << "vertex number on the destination boundary\n"
33622 << "The current boundary (" << bnd_id << ") is marked to have"
33623 << "a connection at the\nINITIAL vertex ("
33625 << src_vertex_coordinates_initial[1] << ")\n"
33626 << "with boundary (" << dst_bnd_id_initial << ")\n"
33627 << "This is the list of vertices on the destination boundary\n";
33628 // Get the pointer to the associated polyline by using the
33629 // boundary id
33631 this->boundary_polyline_pt(dst_bnd_id_initial);
33632 // The number of vertices on the destination boundary
33633 const unsigned n_vertex_dst_boundary = dst_polyline->nvertex();
33634 // Loop over the vertices print them
33635 for (unsigned i = 0; i < n_vertex_dst_boundary; i++)
33636 {
33637 Vector<double> current_vertex = dst_polyline->vertex_coordinate(i);
33638 error_message << "Vertex#(i): (" << current_vertex[0] << ", "
33639 << current_vertex[1] << ")\n";
33640 }
33641 throw OomphLibError(
33642 error_message.str(),
33643 "RefineableTriangleMesh::restore_polyline_connections_helper()",
33645#endif
33646
33647 } // else if (this->is_mesh_distributed())
33648
33649 } // if (!found_vertex_on_dst_boundary_initial)
33650 else
33651 {
33652 // Set the vertex number on the destination boundary
33653 polyline_pt->initial_vertex_connected_n_vertex() =
33655
33656 // Set the chunk number on the destination boundary
33658
33659 } // else if (!found_vertex_on_dst_boundary_initial)
33660
33661 } // if (polyline_pt->is_initial_vertex_connected())
33662
33663 // ------------------------------------------------------------------
33664 // Is the final vertex connected?
33665 if (polyline_pt->is_final_vertex_connected())
33666 {
33667 // The pointer to the boundary to connect
33669
33670 // Get the boundary id of the destination/connected boundary
33671 const unsigned dst_bnd_id_final =
33672 polyline_pt->final_vertex_connected_bnd_id();
33673
33674 // Get the final vertex on the current boundary
33675 const unsigned tmp_n_vertices = polyline_pt->nvertex();
33677 polyline_pt->vertex_coordinate(tmp_n_vertices - 1);
33678
33679#ifdef PARANOID
33680 // Is the mesh distributed?
33681#ifdef OOMPH_HAS_MPI
33682 if (this->is_mesh_distributed())
33683 {
33684 // Get the initial shared boundary id
33685 const unsigned init_shd_bnd_id = this->initial_shared_boundary_id();
33686 // Is the boundary to connect a shared boundary
33688 {
33689 // Get the current polyline original boundary id
33690 const unsigned bnd_id = polyline_pt->boundary_id();
33691 std::ostringstream error_message;
33692 error_message
33693 << "FINAL VERTEX CONNECTION\n"
33694 << "The current original boundary is trying to connect to a\n"
33695 << "shared boundary, this is not allowed. In this case the\n"
33696 << "shared boundary should be the one that connects with the\n"
33697 << "original boundary\n"
33698 << "The current boundary (" << bnd_id << ") is marked to have "
33699 << "a connection at the\nFINAL vertex ("
33700 << src_vertex_coordinates_final[0] << ","
33701 << src_vertex_coordinates_final[1] << ")\n"
33702 << "with boundary (" << dst_bnd_id_final << ")\n"
33703 << "This is the list of vertices on the destination boundary\n";
33704 // Get the pointer to the associated polyline by using the
33705 // boundary id
33707 this->boundary_polyline_pt(dst_bnd_id_final);
33708 // The number of vertices on the destination boundary
33709 const unsigned n_vertex_dst_boundary = dst_polyline->nvertex();
33710 // Loop over the vertices print them
33711 for (unsigned i = 0; i < n_vertex_dst_boundary; i++)
33712 {
33713 Vector<double> current_vertex = dst_polyline->vertex_coordinate(i);
33714 error_message << "Vertex#(" << i << "): (" << current_vertex[0]
33715 << ", " << current_vertex[1] << ")\n";
33716 }
33717 throw OomphLibError(
33718 error_message.str(),
33719 "RefineableTriangleMesh::restore_polyline_connections_helper()",
33721 } // if (dst_bnd_id_initial >= init_shd_bnd_id)
33722
33723 } // if (this->is_mesh_distributed())
33724#endif // #ifdef OOMPH_HAS_MPI
33725
33726#endif // #ifdef PARANOID
33727
33728 // Flag to indicate if the vertex was found on the destination
33729 // boundary
33731
33732 // Flag that stores the chunk number to connect (only used in
33733 // distributed meshes)
33734 unsigned sub_poly_to_connect = 0;
33735
33736 // Store the vertex number on the destination boundary
33737 unsigned n_vertex_connection_final = 0;
33738
33739 // Flags only used in a distributed mesh
33740 // ----------------------------------------
33741 // Flag to indicate we are trying to connect to an split boundary
33743
33744 // Flag to indicate we are trying to connecto to an internal
33745 // boundary that is overlaped by a shared boundary)
33747
33748#ifdef OOMPH_HAS_MPI
33749 if (this->is_mesh_distributed())
33750 {
33751 // We can only connect to an original boundary, check if the
33752 // boundary was splitted during the distribution process to
33753 // consider all the chunks (sub-polylines) of the boundary
33754 if (this->boundary_was_splitted(dst_bnd_id_final))
33755 {
33757 } // if (this->boundary_was_splitted(dst_bnd_id_final))
33758
33759 // Check if the destination boundary, or any of its chunks is
33760 // marked to be overlapped by a shared boundary, if that is the
33761 // case we can only connect to the chunks that are not
33762 // overlapped by shared boundaries (the shared boundaries are in
33763 // charge of generating the connections with original boundaries
33764 // and with themselves)
33766 {
33767 // Get the number of chucks that represent the destination
33768 // boundary
33769 const unsigned n_sub_poly =
33770 this->nboundary_subpolylines(dst_bnd_id_final);
33771 // Now loop over the chunks of the destination boundary and if
33772 // any of them is marked to be overlaped by a shared boundary
33773 // then set the flag and break the loop
33774 for (unsigned ii = 0; ii < n_sub_poly; ii++)
33775 {
33776 if (this->boundary_marked_as_shared_boundary(dst_bnd_id_final, ii))
33777 {
33778 // Mark the boundary as being overlaped by a shared
33779 // boundary
33781 // Break, no need to look for more overlapings
33782 break;
33783 } // if (boundary_marked_as_shared_boundary(...))
33784 } // for (ii < n_sub_poly)
33785 } // if (connecting_to_an_split_boundary)
33786 else
33787 {
33788 // If not connecting to an split boundary then check if the
33789 // whole destination boundary is overlaped by an internal
33790 // boundary
33791 if (this->boundary_marked_as_shared_boundary(dst_bnd_id_final, 0))
33792 {
33793 // Mark the boundary as being overlaped by a shared boundary
33795 } // if (boundary_marked_as_shared_boundary(...))
33796 } // else if (connecting_to_an_split_boundary)
33797
33798 } // if (this->is_mesh_distributed())
33799
33800#endif // #ifdef OOMPH_HAS_MPI
33801
33802 // If we are connecting neither to an split boundary nor an
33803 // overlaped boundary then get the pointer to the original
33804 // boundary
33807 {
33808 // Get the polyline pointer representing the destination
33809 // boundary
33810 poly_to_connect_pt = this->boundary_polyline_pt(dst_bnd_id_final);
33811 } // else if (NOT split, NOT overlaped)
33812
33813 // Now look for the vertex number on the destination boundary(ies)
33814 // -- in case that the boundary was split ---
33815
33816 // Do not check for same orientation, that was previously worked
33817 // by interchanging the connections boundaries (if necessary)
33818
33819 // If the boundary was not split then ...
33821 {
33822 // ... check if the boundary is marked to be overlaped by
33823 // a shared boundary
33825 {
33826 // If that is not the case then we can safely look for the
33827 // vertex number on the destination boundary
33829 this->get_connected_vertex_number_on_destination_polyline(
33833
33834 } // if (!connecting_to_an_overlaped_boundary)
33835 else
33836 {
33837 // If the whole boundary is marked to be overlaped by a shared
33838 // boundary then do nothing, the shared boundaries are already
33839 // in charge of performing the connection (it will be required
33840 // to disabled the connection) with the original boundary
33841
33842 } // else if (!connecting_to_an_overlaped_boundary)
33843
33844 } // if (!connecting_to_an_split_boundary)
33845#ifdef OOMPH_HAS_MPI
33846 else
33847 {
33848 // If the boundary was split then we need to look for the vertex
33849 // in the sub-polylines
33850
33851 // Get the sub-polylines vector
33853 this->boundary_subpolylines(dst_bnd_id_final);
33854
33855 // Get the number of sub-polylines
33856 const unsigned nsub_poly = tmp_vector_subpolylines.size();
33857#ifdef PARANOID
33858 if (nsub_poly <= 1)
33859 {
33860 std::ostringstream error_message;
33861 error_message << "The boundary (" << dst_bnd_id_final << ") was "
33862 << "marked to be splitted but\n"
33863 << "there are only (" << nsub_poly << ") polylines to "
33864 << "represent it.\n";
33865 throw OomphLibError(
33866 error_message.str(),
33867 "RefineableTriangleMesh::restore_polyline_connections_helper()",
33869 } // if (nsub_poly <= 1)
33870#endif
33871 // We need to check if the boundary is marked to be overlaped by
33872 // a shared boundary, if that is the case we need to check for
33873 // each indivual subpolyline, and for those overlaped by a
33874 // shared polyline do nothing, the shared polylines have already
33875 // deal with these connections
33876
33877 // ... check if the boundary is marked to be overlaped by
33878 // a shared boundary
33880 {
33881 // The boundary is not overlapped by shared boundaries, we can
33882 // work without checking the subpolylines individually (non of
33883 // them are overlapped by a shared boundary)
33884
33885 // Look for the vertex number to connect on each of the
33886 // subpolyines
33887 for (unsigned isub = 0; isub < nsub_poly; isub++)
33888 {
33889 // Assign the pointer to the sub-polyline
33891 // Search for the vertex in the current sub-polyline
33893 this->get_connected_vertex_number_on_destination_polyline(
33897
33898 // If we have found the vertex to connect then break the
33899 // loop
33901 {
33902 // But first save the subpoly number (chunk), that will be
33903 // used to perform the connection
33905 break;
33906 } // if (found_vertex_on_dst_boundary_initial)
33907
33908 } // for (isub < nsub_poly)
33909
33910 } // if (!connecting_to_an_overlaped_boundary)
33911 else
33912 {
33913 // If connecting to an overlapped boundary then we ignore the
33914 // subpolylines overlapped by shared boundaries and only look
33915 // on the sub-polylines that are not marked as being overlaped
33916 // by shared boundaries
33917
33918 // Look for the vertex number to connect on each of the
33919 // subpolyines
33920 for (unsigned isub = 0; isub < nsub_poly; isub++)
33921 {
33922 // Only work with those sub-polylines that are not overlaped
33923 // by shared boundaries
33924 if (!this->boundary_marked_as_shared_boundary(dst_bnd_id_final,
33925 isub))
33926 {
33927 // Assign the pointer to the sub-polyline
33929
33930 // Search for the vertex in the current sub-polyline
33932 this->get_connected_vertex_number_on_destination_polyline(
33936
33937 // Was the vertex found?
33939 {
33940 // But first save the subpoly number (chunk), that will
33941 // be used to perform the connection
33943 break;
33944 } // if (found_vertex_on_dst_boundary_final)
33945
33946 } // if (not overlaped by shared boundary)
33947
33948 } // for (isub < nsub_poly)
33949
33950 } // else if (!connecting_to_an_overlaped_boundary)
33951
33952 } // else if (!connecting_to_an_split_boundary)
33953#endif // #ifdef OOMPH_HAS_MPI
33954
33955 // If not found it may be that the connection information is
33956 // inverted
33958 {
33959 // Is the mesh distributed?
33960#ifdef OOMPH_HAS_MPI
33961 if (this->is_mesh_distributed())
33962 {
33963 // If the mesh is distributed and the vertex number was not
33964 // found, that means that the boundary (or vertex) to connect
33965 // in the destination boundary is not in the current
33966 // processor. In that suspend the connection
33967 polyline_pt->suspend_final_vertex_connected();
33968 // Add the polyline to the vector of polylines whose
33969 // connection will be resumed at the end of the adaptation
33970 // process
33971 resume_final_connection_polyline_pt.push_back(polyline_pt);
33972 // The shared boundaries are marked to connect to the final
33973 // vertex of the polyline (remember that a shared boundary
33974 // stops adding nodes when it finds a node on an original
33975 // boundary) -- The final vertex is now a base node
33976 } // if (this->is_mesh_distributed())
33977 else
33978#endif // #ifdef OOMPH_HAS_MPI
33979 {
33980#ifdef PARANOID
33981 // If not found then there is a problem with the vertices
33982 // Get the associated boundary id of the current polyline
33983 const unsigned bnd_id = polyline_pt->boundary_id();
33984 std::ostringstream error_message;
33985 error_message
33986 << "FINAL VERTEX CONNECTION\n"
33987 << "It was not possible to find the associated "
33988 << "vertex number on the destination boundary\n"
33989 << "The current boundary (" << bnd_id << ") is marked to have "
33990 << "a connection at the\nFINAL vertex ("
33991 << src_vertex_coordinates_final[0] << ","
33992 << src_vertex_coordinates_final[1] << ")\n"
33993 << "with boundary (" << dst_bnd_id_final << ")\n"
33994 << "This is the list of vertices on the destination boundary\n";
33995 // Get the pointer to the associated polyline by using the
33996 // boundary id
33998 this->boundary_polyline_pt(dst_bnd_id_final);
33999 // The number of vertices on the destination boundary
34000 const unsigned n_vertex_dst_boundary = dst_polyline->nvertex();
34001 // Loop over the vertices print them
34002 for (unsigned i = 0; i < n_vertex_dst_boundary; i++)
34003 {
34004 Vector<double> current_vertex = dst_polyline->vertex_coordinate(i);
34005 error_message << "Vertex#(" << i << "): (" << current_vertex[0]
34006 << ", " << current_vertex[1] << ")\n";
34007 }
34008 throw OomphLibError(
34009 error_message.str(),
34010 "RefineableTriangleMesh::restore_polyline_connections_helper()",
34012#endif
34013 } // else if (this->is_mesh_distributed())
34014
34015 } // if (!found_vertex_on_dst_boundary_final)
34016 else
34017 {
34018 // Set the vertex number on the destination boundary
34019 polyline_pt->final_vertex_connected_n_vertex() =
34021
34022 // Set the chunk number on the destination boundary
34024
34025 } // else if (!found_vertex_on_dst_boundary_final)
34026
34027 } // if (polyline_pt->is_final_vertex_connected())
34028 }
34029
34030 //=========================================================================
34031 /// Resume the boundary connections that may have been
34032 /// suspended because the destination boundary is no part of the
34033 /// domain. The connections are no permanently suspended because if
34034 /// load balance takes place the destination boundary may be part of
34035 /// the new domain representation therefore the connection would
34036 /// exist
34037 //=========================================================================
34038 template<class ELEMENT>
34042 {
34043 // Get the number of polylines that require to resume the connection
34044 // at the initial vertex
34045 const unsigned n_initial_poly =
34047 // Loop over the polylines that require to resume the connection
34048 // at the initial vertex
34049 for (unsigned p = 0; p < n_initial_poly; p++)
34050 {
34051 // Get the polyline
34054 // Resume the connection with the initial vertex
34055 tmp_poly_pt->resume_initial_vertex_connected();
34056 } // for (p < n_initial_poly)
34057
34058 // Get the number of polylines that require to resume the connection
34059 // at the final vertex
34061 // Loop over the polylines that require to resume the connection at
34062 // the final vertex
34063 for (unsigned p = 0; p < n_final_poly; p++)
34064 {
34065 // Get the polyline
34068 // Resume the connection with the final vertex
34069 tmp_poly_pt->resume_final_vertex_connected();
34070 } // for (p < n_final_poly)
34071
34072 // Clear the storage
34075 }
34076
34077 //=========================================================================
34078 /// Gets the associated vertex number according to the vertex
34079 /// coordinates on the destination boundary
34080 //=========================================================================
34081 template<class ELEMENT>
34085 const unsigned& dst_bnd_id,
34086 unsigned& vertex_number)
34087 {
34088 bool found_associated_vertex_number = false;
34089
34090 // Get the pointer to the associated polyline by using the boundary id
34091 TriangleMeshPolyLine* dst_polyline = this->boundary_polyline_pt(dst_bnd_id);
34092
34093 const unsigned n_vertices = dst_polyline->nvertex();
34094
34095 // Loop over the vertices and return the closest vertex
34096 // to the given vertex coordinates
34097 for (unsigned i = 0; i < n_vertices; i++)
34098 {
34099 Vector<double> current_vertex = dst_polyline->vertex_coordinate(i);
34100
34101 double error = (vertex_coordinates[0] - current_vertex[0]) *
34105
34106 error = sqrt(error);
34107
34109 {
34110 vertex_number = i;
34112 break;
34113 }
34114 }
34115
34117 }
34118
34119 //=========================================================================
34120 /// Helper function that updates the input polygon's PSLG
34121 /// by using the end-points of elements from FaceMesh(es) that are
34122 /// constructed for the boundaries associated with the segments of the
34123 /// polygon. Optional boolean is used to run it as test only (if
34124 /// true is specified as input) in which case polygon isn't actually
34125 /// modified. Returned boolean indicates if polygon was (or would have
34126 /// been -- if called with check_only=false) changed.
34127 //=========================================================================
34128 template<class ELEMENT>
34131 {
34132#ifdef PARANOID
34133 // If the mesh is marked as distributed this method can not be
34134 // called since there is no guarantee of creating (distributed)
34135 // meshes that match in the number and position of nodes at their
34136 // shared boundaries. The only exececption is when called with
34137 // check_only=true, since no boundary updating is performed
34138 if (this->is_mesh_distributed() && !check_only)
34139 {
34140 std::stringstream error_message;
34141 error_message
34142 << "The updating of polygons of a distributed mesh can ONLY be\n"
34143 << "performed using the element's area associated to the halo(ed)\n"
34144 << "elements.\n"
34145 << "1) Make sure you have enabled the parallel mesh adaptation\n"
34146 << "option if you are working with a distributed mesh, OR\n"
34147 << "2) Make sure to call the update_..._using_elements_area() methods\n"
34148 << "if the mesh is marked as distributed\n\n";
34149 throw OomphLibError(
34150 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
34151 } // if (this->is_mesh_distributed())
34152#endif
34153
34154 // Boolean that indicates whether an actual update of the polygon
34155 // was performed or not
34156 bool unrefinement_was_performed = false;
34157 bool refinement_was_performed = false;
34158 bool max_length_applied = false;
34159
34160 // Loop over the number of polylines
34161 const unsigned n_polyline = polygon_pt->npolyline();
34162
34163 // Get face mesh representation of all polylines, possibly
34164 // with segments re-distributed to maintain an approximately
34165 // even sub-division of the polygon
34167 get_face_mesh_representation(polygon_pt, face_mesh_pt);
34168
34169 // Create vertices for the polylines by using the vertices
34170 // of the FaceElements
34171 Vector<double> vertex_coord(3); // zeta,x,y
34174
34175 for (unsigned p = 0; p < n_polyline; p++)
34176 {
34177 // Set of coordinates that will be placed on the boundary
34178 // Set entries are ordered on first entry in vector which stores
34179 // the boundary coordinate so the vertices come out in order!
34180 std::set<Vector<double>> vertex_nodes;
34181
34182 // Get the boundary id
34183 const unsigned bound = polygon_pt->curve_section_pt(p)->boundary_id();
34184
34185 // Get the chunk number
34186 const unsigned chunk = polygon_pt->curve_section_pt(p)->boundary_chunk();
34187
34188 // Loop over the face elements (ordered) and add their vertices
34189 unsigned n_face_element = face_mesh_pt[p]->nelement();
34190 for (unsigned e = 0; e < n_face_element; ++e)
34191 {
34192 FiniteElement* el_pt = face_mesh_pt[p]->finite_element_pt(e);
34193
34194#ifdef OOMPH_HAS_MPI
34195 // Only work with non-halo elements if the mesh is distributed
34196 if (this->is_mesh_distributed() && el_pt->is_halo())
34197 {
34198 continue;
34199 }
34200#endif
34201
34202 unsigned n_node = el_pt->nnode();
34203
34204 // Add the left-hand node to the set:
34205
34206 // Boundary coordinate
34208 vertex_coord[0] = bound_left[0];
34209
34210 // Actual coordinates
34211 for (unsigned i = 0; i < 2; i++)
34212 {
34213 vertex_coord[i + 1] = el_pt->node_pt(0)->x(i);
34214 }
34215 vertex_nodes.insert(vertex_coord);
34216
34217 // Add the right-hand nodes to the set:
34218
34219 // Boundary coordinate
34220 el_pt->node_pt(n_node - 1)
34222 vertex_coord[0] = bound_right[0];
34223
34224 // Actual coordinates
34225 for (unsigned i = 0; i < 2; i++)
34226 {
34227 vertex_coord[i + 1] = el_pt->node_pt(n_node - 1)->x(i);
34228 }
34229 vertex_nodes.insert(vertex_coord);
34230 }
34231
34232 // Now turn into vector for ease of handling...
34233 unsigned n_poly_vertex = vertex_nodes.size();
34235 unsigned count = 0;
34236 for (std::set<Vector<double>>::iterator it = vertex_nodes.begin();
34237 it != vertex_nodes.end();
34238 ++it)
34239 {
34240 tmp_vector_vertex_node[count].resize(3);
34241 tmp_vector_vertex_node[count][0] = (*it)[0];
34242 tmp_vector_vertex_node[count][1] = (*it)[1];
34243 tmp_vector_vertex_node[count][2] = (*it)[2];
34244 ++count;
34245 }
34246
34247 // Size of the vector
34249
34250 // Tolerance below which the middle point can be deleted
34251 // (ratio of deflection to element length)
34252 double unrefinement_tolerance =
34253 polygon_pt->polyline_pt(p)->unrefinement_tolerance();
34254
34255 //------------------------------------------------------
34256 // Unrefinement
34257 //------------------------------------------------------
34258 if (unrefinement_tolerance > 0.0 && n_vertex >= 3)
34259 {
34260 unrefinement_was_performed = unrefine_boundary(bound,
34261 chunk,
34263 unrefinement_tolerance,
34264 check_only);
34265
34266 // In this case the "unrefinement_was_performed" variable
34267 // tell us if the update had been performed when calling
34268 // with check_oly=false
34270 {
34271 // Cleanup (but only the elements -- the nodes still exist in
34272 // the bulk mesh!
34273 for (unsigned p = 0; p < n_polyline; p++)
34274 {
34275 face_mesh_pt[p]->flush_node_storage();
34276 delete face_mesh_pt[p];
34277 }
34278 return true;
34279 }
34280
34281 } // end of unrefinement
34282
34283 // Do not perform refinement if there are no more than two vertices
34284 // New size of the vector
34286
34287 //------------------------------------------------
34288 // Refinement
34289 //------------------------------------------------
34290 double refinement_tolerance =
34291 polygon_pt->polyline_pt(p)->refinement_tolerance();
34292 if (refinement_tolerance > 0.0 && n_vertex >= 2)
34293 {
34294 refinement_was_performed = refine_boundary(face_mesh_pt[p],
34296 refinement_tolerance,
34297 check_only);
34298
34299 // In this case the "refinement_was_performed" variable
34300 // tell us if the update had been performed when calling
34301 // with check_only=false
34303 {
34304 // Cleanup (but only the elements -- the nodes still exist in
34305 // the bulk mesh!
34306 for (unsigned p = 0; p < n_polyline; p++)
34307 {
34308 face_mesh_pt[p]->flush_node_storage();
34309 delete face_mesh_pt[p];
34310 }
34311 return true;
34312 }
34313
34314 } // end refinement
34315
34316 // Do not perform maximum length constraint if there are no more than
34317 // two vertices
34318 // New size of the vector
34320
34321 //------------------------------------------------
34322 // Maximum length constrait
34323 //-----------------------------------------------
34324 double maximum_length = polygon_pt->polyline_pt(p)->maximum_length();
34325 if (maximum_length > 0.0 && n_vertex >= 2)
34326 {
34327 max_length_applied = apply_max_length_constraint(
34328 face_mesh_pt[p], tmp_vector_vertex_node, maximum_length);
34329
34330 // In this case the max length criteria was applied, check if
34331 // check_only=false
34333 {
34334 // Cleanup (but only the elements -- the nodes still exist in
34335 // the bulk mesh!
34336 for (unsigned p = 0; p < n_polyline; p++)
34337 {
34338 face_mesh_pt[p]->flush_node_storage();
34339 delete face_mesh_pt[p];
34340 }
34341 return true;
34342 }
34343 }
34344
34345 // For further processing the three-dimensional vector
34346 // has to be reduced to a two-dimensional vector
34349
34350 for (unsigned i = 0; i < n_vertex; i++)
34351 {
34352 vector_vertex_node[i].resize(2);
34355 }
34356
34357#ifdef OOMPH_HAS_MPI
34358 // Only perform this checking if the mesh is not distributed. When
34359 // the mesh is distributed the polylines continuity is addressed in
34360 // the sort_polylines_helper() method
34361 if (!this->is_mesh_distributed())
34362#endif
34363 {
34364 if ((p > 0) && !check_only)
34365 {
34366 // Final end point of previous line
34368 unsigned n_prev_vertex =
34369 polygon_pt->curve_section_pt(p - 1)->nvertex();
34371 polygon_pt->polyline_pt(p - 1)->vertex_coordinate(n_prev_vertex -
34372 1);
34373
34374 unsigned prev_seg_boundary_id =
34375 polygon_pt->curve_section_pt(p - 1)->boundary_id();
34376
34377 // Find the error between the final vertex of the previous
34378 // line and the first vertex of the current line
34379 double error = 0.0;
34380 for (unsigned i = 0; i < 2; i++)
34381 {
34382 const double dist = final_vertex_of_previous_segment[i] -
34383 (*vector_vertex_node.begin())[i];
34384 error += dist * dist;
34385 }
34386 error = sqrt(error);
34387
34388 // If the error is bigger than the tolerance then
34389 // we probably need to reverse, but better check
34391 {
34392 // Find the error between the final vertex of the previous
34393 // line and the last vertex of the current line
34394 double rev_error = 0.0;
34395 for (unsigned i = 0; i < 2; i++)
34396 {
34397 const double dist = final_vertex_of_previous_segment[i] -
34398 (*--vector_vertex_node.end())[i];
34399 rev_error += dist * dist;
34400 }
34402
34403 if (rev_error >
34405 {
34406 // It could be possible that the first segment be reversed and we
34407 // did not notice it because this check does not apply for the
34408 // first segment. We can verify if the first segment is reversed
34409 // by using the vertex number 1
34410 if (p == 1)
34411 {
34412 // Initial end point of previous line
34414
34416 polygon_pt->polyline_pt(p - 1)->vertex_coordinate(0);
34417
34418 unsigned prev_seg_boundary_id =
34419 polygon_pt->curve_section_pt(p - 1)->boundary_id();
34420
34421 // Find the error between the initial vertex of the previous
34422 // line and the first vertex of the current line
34423 double error = 0.0;
34424 for (unsigned i = 0; i < 2; i++)
34425 {
34426 const double dist = initial_vertex_of_previous_segment[i] -
34427 (*vector_vertex_node.begin())[i];
34428 error += dist * dist;
34429 }
34430 error = sqrt(error); // Reversed only the previous one
34431
34432 // If the error is bigger than the tolerance then
34433 // we probably need to reverse, but better check
34434 if (error >
34436 {
34437 // Find the error between the final vertex of the previous
34438 // line and the last vertex of the current line
34439 double rev_error = 0.0;
34440 for (unsigned i = 0; i < 2; i++)
34441 {
34442 const double dist = initial_vertex_of_previous_segment[i] -
34443 (*--vector_vertex_node.end())[i];
34444 rev_error += dist * dist;
34445 }
34446 rev_error =
34447 sqrt(rev_error); // Reversed both the current one and
34448 // the previous one
34449
34450 if (rev_error >
34452 {
34453 std::ostringstream error_stream;
34455 << "The distance between the first node of the current\n"
34456 << "line segment (boundary " << bound
34457 << ") and either end of "
34458 << "the previous line segment\n"
34459 << "(boundary " << prev_seg_boundary_id
34460 << ") is bigger than "
34461 << "the desired tolerance "
34463 << ".\n"
34464 << "This suggests that the polylines defining the "
34465 "polygonal\n"
34466 << "representation are not properly ordered.\n"
34467 << "Fail on last vertex of polyline: ("
34469 << ") and\nfirst vertex of polyline (" << bound
34470 << ").\nThis should have failed when first trying to "
34471 << "construct the\npolygon.\n";
34472 throw OomphLibError(error_stream.str(),
34475 }
34476 else
34477 {
34478 // Reverse both
34479 // Reverse the current vector to line up with the previous
34480 // one
34481 std::reverse(vector_vertex_node.begin(),
34482 vector_vertex_node.end());
34483
34484 polygon_pt->polyline_pt(p - 1)->reverse();
34485 }
34486 }
34487 else
34488 {
34489 // Reverse the previous one
34490 polygon_pt->polyline_pt(p - 1)->reverse();
34491 }
34492
34493 } // if p == 1
34494 else
34495 {
34496 std::ostringstream error_stream;
34498 << "The distance between the first node of the current\n"
34499 << "line segment (boundary " << bound
34500 << ") and either end of "
34501 << "the previous line segment\n"
34502 << "(boundary " << prev_seg_boundary_id
34503 << ") is bigger than the "
34504 << "desired tolerance "
34506 << ".\n"
34507 << "This suggests that the polylines defining the polygonal\n"
34508 << "representation are not properly ordered.\n"
34509 << "Fail on last vertex of polyline: ("
34510 << prev_seg_boundary_id << ") and\nfirst vertex of polyline ("
34511 << bound << ").\n"
34512 << "This should have failed when first trying to construct "
34513 "the\n"
34514 << "polygon.\n";
34515 throw OomphLibError(error_stream.str(),
34518 }
34519 }
34520 else
34521 {
34522 // Reverse the current vector to line up with the previous one
34523 std::reverse(vector_vertex_node.begin(),
34524 vector_vertex_node.end());
34525 }
34526
34527 } // first error
34528 } // p > 0
34529 } // is mesh not distributed?
34530
34531 if (!check_only)
34532 {
34533 // Now update the polyline according to the new vertices
34534 // The new one representation
34537
34538 // Create a temporal "curve section" version of the recently created
34539 // polyline
34541
34542 // Establish refinement and unrefinement tolerance
34543 tmp_polyline_pt->set_unrefinement_tolerance(unrefinement_tolerance);
34544 tmp_polyline_pt->set_refinement_tolerance(refinement_tolerance);
34545
34546 // Establish the maximum length constraint
34547 tmp_polyline_pt->set_maximum_length(maximum_length);
34548
34549 // We pass the connection information from the old polyline to
34550 // the new one
34551 this->copy_connection_information(polygon_pt->polyline_pt(p),
34553
34554
34555 std::set<TriangleMeshCurveSection*>::iterator it =
34556 this->Free_curve_section_pt.find(polygon_pt->curve_section_pt(p));
34557
34558 if (it != this->Free_curve_section_pt.end())
34559 {
34560 this->Free_curve_section_pt.erase(it);
34561 delete polygon_pt->curve_section_pt(p);
34562 }
34563
34564 // ------------------------------------------------------------
34565 // Copying the new representation
34566 polygon_pt->curve_section_pt(p) = tmp_polyline_pt;
34567
34568 // Update the Boundary - Polyline map
34569 this->Boundary_curve_section_pt[bound] =
34570 polygon_pt->curve_section_pt(p);
34571
34572 // The new curve always needs to be added to the free section
34573 // because we created it internally
34574 this->Free_curve_section_pt.insert(polygon_pt->curve_section_pt(p));
34575
34576 } // if(!check_only)
34577
34578 } // for (p < n_polyline)
34579
34580 // Cleanup (but only the elements -- the nodes still exist in
34581 // the bulk mesh!
34582 for (unsigned p = 0; p < n_polyline; p++)
34583 {
34584 face_mesh_pt[p]->flush_node_storage();
34585 delete face_mesh_pt[p];
34586 }
34587
34588 if (check_only)
34589 {
34590 // if we end up all the way down here, no update of the internal
34591 // boundaries is necessary (in case we only check)
34592 return false;
34593 }
34594 else
34595 {
34596 // if we not only check, but actually perform the update and end up
34597 // all the way down here then we indicate whether an update was performed
34598 // or not
34601 }
34602 }
34603
34604 //=========================================================================
34605 /// Helper function that updates the input open curve by using
34606 /// end-points of elements from FaceMesh(es) that are constructed for the
34607 /// boundaries associated with the polylines. Optional boolean is used to
34608 /// run it as test only (if true is specified as input) in which case the
34609 /// polylines are not actually modified. Returned boolean indicates if
34610 /// polylines were (or would have been -- if called with check_only=false)
34611 /// changed.
34612 //=========================================================================
34613 template<class ELEMENT>
34616 {
34617#ifdef PARANOID
34618 // If the mesh is marked as distributed this method can not be
34619 // called since there is no guarantee of creating (distributed)
34620 // meshes that match in the number and position of nodes at their
34621 // shared boundaries. The only exececption is when called with
34622 // check_only=true, since no boundary updating is performed
34623 if (this->is_mesh_distributed() && !check_only)
34624 {
34625 std::stringstream error_message;
34626 error_message
34627 << "The updating of open curves of a distributed mesh can ONLY be\n"
34628 << "performed using the element's area associated to the halo(ed)\n"
34629 << "elements.\n"
34630 << "1) Make sure you have enabled the parallel mesh adaptation\n"
34631 << "option if you are working with a distributed mesh, OR\n"
34632 << "2) Make sure to call the update_..._using_elements_area() methods\n"
34633 << "if the mesh is marked as distributed\n\n";
34634 throw OomphLibError(
34635 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
34636 } // if (this->is_mesh_distributed())
34637#endif
34638
34639 // Boolean that indicates whether an actual update of the polylines
34640 // were performed or not
34641 bool unrefinement_was_performed = false;
34642 bool refinement_was_performed = false;
34643 bool max_length_applied = false;
34644
34645 // Loop over the number of polylines
34646 const unsigned n_polyline = open_polyline_pt->ncurve_section();
34647
34648 // Get face mesh representation of all polylines, possibly
34649 // with segments re-distributed to maintain an approximately
34650 // even sub-division of the polygon
34652 get_face_mesh_representation(open_polyline_pt, face_mesh_pt);
34653
34654 // Create vertices for the polylines by using the vertices
34655 // of the FaceElements
34656 Vector<double> vertex_coord(3); // zeta,x,y
34659
34660 for (unsigned p = 0; p < n_polyline; p++)
34661 {
34662 // Set of coordinates that will be placed on the boundary
34663 // Set entries are ordered on first entry in vector which stores
34664 // the boundary coordinate so the vertices come out in order!
34665 std::set<Vector<double>> vertex_nodes;
34666
34667 // Get the boundary id
34668 const unsigned bound =
34669 open_polyline_pt->curve_section_pt(p)->boundary_id();
34670
34671 // Get the chunk number
34672 const unsigned chunk =
34673 open_polyline_pt->curve_section_pt(p)->boundary_chunk();
34674
34675 // Loop over the face elements (ordered) and add their vertices
34676 unsigned n_face_element = face_mesh_pt[p]->nelement();
34677
34678 // n_count = 0;
34679 for (unsigned e = 0; e < n_face_element; ++e)
34680 {
34681 FiniteElement* el_pt = face_mesh_pt[p]->finite_element_pt(e);
34682 unsigned n_node = el_pt->nnode();
34683
34684 // Add the left-hand node to the set:
34685
34686 // Boundary coordinate
34688 vertex_coord[0] = bound_left[0];
34689
34690 // Actual coordinates
34691 for (unsigned i = 0; i < 2; i++)
34692 {
34693 vertex_coord[i + 1] = el_pt->node_pt(0)->x(i);
34694 }
34695 vertex_nodes.insert(vertex_coord);
34696
34697 // Add the right-hand nodes to the set:
34698
34699 // Boundary coordinate
34700 el_pt->node_pt(n_node - 1)
34702 vertex_coord[0] = bound_right[0];
34703
34704 // Actual coordinates
34705 for (unsigned i = 0; i < 2; i++)
34706 {
34707 vertex_coord[i + 1] = el_pt->node_pt(n_node - 1)->x(i);
34708 }
34709 vertex_nodes.insert(vertex_coord);
34710 }
34711
34712 // Now turn into vector for ease of handling...
34713 unsigned n_poly_vertex = vertex_nodes.size();
34715 unsigned count = 0;
34716 for (std::set<Vector<double>>::iterator it = vertex_nodes.begin();
34717 it != vertex_nodes.end();
34718 ++it)
34719 {
34720 tmp_vector_vertex_node[count].resize(3);
34721 tmp_vector_vertex_node[count][0] = (*it)[0];
34722 tmp_vector_vertex_node[count][1] = (*it)[1];
34723 tmp_vector_vertex_node[count][2] = (*it)[2];
34724 ++count;
34725 }
34726
34727 // Size of the vector
34729
34730 // Tolerance below which the middle point can be deleted
34731 // (ratio of deflection to element length)
34732 double unrefinement_tolerance =
34733 open_polyline_pt->polyline_pt(p)->unrefinement_tolerance();
34734
34735 //------------------------------------------------------
34736 // Unrefinement
34737 //------------------------------------------------------
34738 if (unrefinement_tolerance > 0.0 && n_vertex >= 3)
34739 {
34740 unrefinement_was_performed = unrefine_boundary(bound,
34741 chunk,
34743 unrefinement_tolerance,
34744 check_only);
34745
34746 // In this case the unrefinement_was_performed variable actually
34747 // tell us if the update had been performed when calling
34748 // with check_only=false
34750 {
34751 // Cleanup (but only the elements -- the nodes still exist in
34752 // the bulk mesh!
34753 for (unsigned p = 0; p < n_polyline; p++)
34754 {
34755 face_mesh_pt[p]->flush_node_storage();
34756 delete face_mesh_pt[p];
34757 }
34758 return true;
34759 }
34760
34761 } // end of unrefinement
34762
34763 // Do not perform refinement if there are no more than two vertices
34764 // (open curve version)
34765 // New size of the vector
34767
34768 //------------------------------------------------
34769 /// Refinement
34770 //------------------------------------------------
34771 double refinement_tolerance =
34772 open_polyline_pt->polyline_pt(p)->refinement_tolerance();
34773 if (refinement_tolerance > 0.0 && n_vertex >= 2)
34774 {
34775 refinement_was_performed = refine_boundary(face_mesh_pt[p],
34777 refinement_tolerance,
34778 check_only);
34779
34780 // In this case the unrefinement_was_performed variable actually
34781 // tell us if the update had been performed when calling
34782 // with check_only=false
34784 {
34785 // Cleanup (but only the elements -- the nodes still exist in
34786 // the bulk mesh!
34787 for (unsigned p = 0; p < n_polyline; p++)
34788 {
34789 face_mesh_pt[p]->flush_node_storage();
34790 delete face_mesh_pt[p];
34791 }
34792 return true;
34793 }
34794
34795 } // end refinement
34796
34797 // Do not perform maximum length constraint if there are no more than
34798 // two vertices
34799 // New size of the vector
34801
34802 //------------------------------------------------
34803 // Maximum length constraint
34804 //-----------------------------------------------
34805 double maximum_length =
34806 open_polyline_pt->polyline_pt(p)->maximum_length();
34807 if (maximum_length > 0.0 && n_vertex >= 2)
34808 {
34809 bool max_length_applied = false;
34810 max_length_applied = apply_max_length_constraint(
34811 face_mesh_pt[p], tmp_vector_vertex_node, maximum_length);
34812
34813 // In this case the max length criteria was applied, check if
34814 // check_only=false
34816 {
34817 // Cleanup (but only the elements -- the nodes still exist in
34818 // the bulk mesh!
34819 for (unsigned p = 0; p < n_polyline; p++)
34820 {
34821 face_mesh_pt[p]->flush_node_storage();
34822 delete face_mesh_pt[p];
34823 }
34824 return true;
34825 }
34826 }
34827
34828 // For further processing the three-dimensional vector
34829 // has to be reduced to a two-dimensional vector
34832
34833 for (unsigned i = 0; i < n_vertex; i++)
34834 {
34835 vector_vertex_node[i].resize(2);
34838 }
34839
34840#ifdef OOMPH_HAS_MPI
34841 // Only perform this checking if the mesh is not distributed. When
34842 // the mesh is distributed the polylines continuity is addressed
34843 // in the sort_polylines_helper() method
34844 if (!this->is_mesh_distributed())
34845#endif
34846 {
34847 // Check whether the segments are continguous (first vertex of this
34848 // segment is equal to last vertex of previous segment).
34849 // If not, we should reverse the order of the current segment.
34850 // This check only applies for segments other than the first.
34851 // We only bother with this check, if we actually perform an update
34852 // of the polyline, i.e. if it's not only a check
34853 if ((p > 0) && !check_only)
34854 {
34855 // Final end point of previous line
34857 open_polyline_pt->polyline_pt(p - 1)->final_vertex_coordinate(
34859
34860 unsigned prev_seg_boundary_id =
34861 open_polyline_pt->curve_section_pt(p - 1)->boundary_id();
34862
34863 // Find the error between the final vertex of the previous
34864 // line and the first vertex of the current line
34865 double error = 0.0;
34866 for (unsigned i = 0; i < 2; i++)
34867 {
34868 const double dist = final_vertex_of_previous_segment[i] -
34869 (*vector_vertex_node.begin())[i];
34870 error += dist * dist;
34871 }
34872 error = sqrt(error);
34873
34874 // If the error is bigger than the tolerance then
34875 // we probably need to reverse, but better check
34877 {
34878 // Find the error between the final vertex of the previous
34879 // line and the first vertex of the current line
34880 error = 0.0;
34881 for (unsigned i = 0; i < 2; i++)
34882 {
34883 const double dist = final_vertex_of_previous_segment[i] -
34884 (*--vector_vertex_node.end())[i];
34885 error += dist * dist;
34886 }
34887 error = sqrt(error);
34888
34890 {
34891 // It could be possible that the first segment be reversed
34892 // and we did not notice it because this check does not
34893 // apply for the first segment. We can verify if the first
34894 // segment is reversed by using the vertex number 1
34895 if (p == 1)
34896 {
34897 // If no found it is possible that the previous polyline
34898 // be reversed Check for that case Initial point of
34899 // previous line
34901 open_polyline_pt->polyline_pt(p - 1)->initial_vertex_coordinate(
34903
34904 // Find the error between the initial vertex of the previous
34905 // line and the first vertex of the current line
34906 error = 0.0;
34907 for (unsigned i = 0; i < 2; i++)
34908 {
34909 const double dist = initial_vertex_of_previous_segment[i] -
34910 (*vector_vertex_node.begin())[i];
34911 error += dist * dist;
34912 }
34913 error = sqrt(error);
34914
34915 // If the error is bigger than the tolerance then
34916 // we probably need to reverse, but better check
34917 if (error >
34919 {
34920 // Find the error between the final vertex of the previous
34921 // line and the first vertex of the current line
34922 error = 0.0;
34923 for (unsigned i = 0; i < 2; i++)
34924 {
34925 const double dist = initial_vertex_of_previous_segment[i] -
34926 (*--vector_vertex_node.end())[i];
34927 error += dist * dist;
34928 }
34929 error = sqrt(error);
34930
34931 if (error >
34933 {
34934 std::ostringstream error_stream;
34936 << "The distance between the first node of the current\n"
34937 << "line segment (boundary " << bound
34938 << ") and either end of the previous line segment\n"
34939 << "(boundary " << prev_seg_boundary_id
34940 << ") is bigger than "
34941 << "the desired tolerance "
34943 << ".\n"
34944 << "This suggests that the polylines defining the open "
34945 << "curve\n"
34946 << "representation are not properly ordered.\n"
34947 << "Fail on last vertex of polyline: ("
34949 << ") and\nfirst vertex of polyline (" << bound << ").\n"
34950 << "This should have failed when first trying to "
34951 "construct\n"
34952 << "the open curve.\n";
34953 throw OomphLibError(error_stream.str(),
34956 }
34957 else // We have to reverse both
34958 {
34959 // First reverse the previous polyline
34960 open_polyline_pt->polyline_pt(p - 1)->reverse();
34961 // Then reverse the current polyline
34962 std::reverse(vector_vertex_node.begin(),
34963 vector_vertex_node.end());
34964 }
34965 }
34966 else
34967 {
34968 // Reverse the previous polyline only
34969 open_polyline_pt->polyline_pt(p - 1)->reverse();
34970 }
34971 } // if (p == 1)
34972 else
34973 {
34974 std::ostringstream error_stream;
34976 << "The distance between the first node of the current\n"
34977 << "line segment (boundary " << bound
34978 << ") and either end of "
34979 << "the previous line segment\n"
34980 << "(boundary " << prev_seg_boundary_id
34981 << ") is bigger than the "
34982 << "desired tolerance "
34984 << ".\n"
34985 << "This suggests that the polylines defining the polygonal\n"
34986 << "representation are not properly ordered.\n"
34987 << "Fail on last vertex of polyline: ("
34988 << prev_seg_boundary_id << ") and\nfirst vertex of polyline ("
34989 << bound << ").\n"
34990 << "This should have failed when first trying to construct "
34991 "the\n"
34992 << "polygon.\n";
34993 throw OomphLibError(error_stream.str(),
34996 }
34997 }
34998 else
34999 {
35000 // Reverse the current vector to line up with the previous one
35001 std::reverse(vector_vertex_node.begin(),
35002 vector_vertex_node.end());
35003 }
35004 }
35005
35006 } // if p > 0
35007
35008 } // is mesh not distributed?
35009
35010 if (!check_only)
35011 {
35012 // Now update the polyline according to the new vertices The new
35013 // one representation
35016
35017 // Create a temporal "curve section" version of the recently
35018 // created polyline
35020
35021 // Copy the unrefinement and refinement information
35022 tmp_polyline->set_unrefinement_tolerance(unrefinement_tolerance);
35023 tmp_polyline->set_refinement_tolerance(refinement_tolerance);
35024
35025 // Establish the maximum length constraint
35026 tmp_polyline->set_maximum_length(maximum_length);
35027
35028 // Pass the connection information from the old polyline to the
35029 // new one
35030 this->copy_connection_information(open_polyline_pt->polyline_pt(p),
35032
35033 std::set<TriangleMeshCurveSection*>::iterator it =
35034 this->Free_curve_section_pt.find(
35035 open_polyline_pt->curve_section_pt(p));
35036
35037 bool delete_it_on_destructor = false;
35038
35039 if (it != this->Free_curve_section_pt.end())
35040 {
35041 // Free previous representation only if you created
35042 this->Free_curve_section_pt.erase(it);
35043 delete open_polyline_pt->curve_section_pt(p);
35045 }
35046
35047 // *****************************************************************
35048 // Copying the new representation
35049 open_polyline_pt->curve_section_pt(p) = tmp_polyline;
35050
35051 // Update the Boundary <--> PolyLine map
35052 this->Boundary_curve_section_pt[bound] =
35053 open_polyline_pt->curve_section_pt(p);
35054
35056 {
35057 this->Free_curve_section_pt.insert(
35058 open_polyline_pt->curve_section_pt(p));
35059 }
35060
35061 } // if(!check_only)
35062
35063 } // n_polylines
35064
35065 // Cleanup (but only the elements -- the nodes still exist in
35066 // the bulk mesh!
35067 for (unsigned p = 0; p < n_polyline; p++)
35068 {
35069 face_mesh_pt[p]->flush_node_storage();
35070 delete face_mesh_pt[p];
35071 }
35072
35073 if (check_only)
35074 {
35075 // if we end up all the way down here, no update of the internal
35076 // boundaries is necessary (in case we only check)
35077 return false;
35078 }
35079 else
35080 {
35081 // if we not only check, but actually perform the update and end
35082 // up all the way down here then we indicate whether an update was
35083 // performed or not
35086 }
35087 }
35088
35089 //=========================================================================
35090 /// Helper function that performs the unrefinement process
35091 /// on the specified boundary by using the provided vertices
35092 /// representation. Optional boolean is used to run it as test only (if
35093 /// true is specified as input) in which case vertex coordinates aren't
35094 /// actually modified. Returned boolean indicates if polyline was (or
35095 /// would have been -- if called with check_only=false) changed.
35096 //=========================================================================
35097 template<class ELEMENT>
35099 const unsigned& b,
35100 const unsigned& c,
35102 double& unrefinement_tolerance,
35103 const bool& check_only)
35104 {
35105 // Store the vertices not allowed for deletion
35106 std::set<Vector<double>> no_delete_vertex;
35107
35108 // Does the boundary receives connections?
35109 const bool boundary_receive_connections =
35110 this->boundary_connections(b, c, no_delete_vertex);
35111
35112 // Boolean that indicates whether an actual update of the vertex
35113 // coordinates was performed or not
35114 bool unrefinement_was_performed = false;
35115
35116 unsigned n_vertex = vector_bnd_vertices.size();
35117
35118 // Initialise counter that indicates at which vertex we're currently
35119 // considering for deletion
35120 unsigned counter = 1;
35121
35122 // Loop over the nodes; start with the second one and increment by two
35123 // this way a "pack" of three nodes will be considered for calculation:
35124 // the middle-node (which is to be deleted or not) and the adjacent
35125 // nodes
35126 for (unsigned i = 1; i <= n_vertex - 2; i += 2)
35127 {
35128 // Maths from http://www.cgafaq.info/wiki/Circle_Through_Three_Points
35129 double a_x = vector_bnd_vertices[i - 1][1];
35130 double a_y = vector_bnd_vertices[i - 1][2];
35131 double b_x = vector_bnd_vertices[i][1];
35132 double b_y = vector_bnd_vertices[i][2];
35133 double c_x = vector_bnd_vertices[i + 1][1];
35134 double c_y = vector_bnd_vertices[i + 1][2];
35135
35136 double a = b_x - a_x;
35137 double b = b_y - a_y;
35138 double c = c_x - a_x;
35139 double d = c_y - a_y;
35140
35141 double e = a * (a_x + b_x) + b * (a_y + b_y);
35142 double f = c * (a_x + c_x) + d * (a_y + c_y);
35143
35144 double g = 2.0 * (a * (c_y - b_y) - b * (c_x - b_x));
35145
35146 bool do_it = false;
35147 if (std::fabs(g) < 1.0e-14)
35148 {
35149 do_it = true;
35150 if (check_only)
35151 {
35152 return true;
35153 }
35154 }
35155 else
35156 {
35157 double p_x = (d * e - b * f) / g;
35158 double p_y = (a * f - c * e) / g;
35159
35160 double r = sqrt(pow((a_x - p_x), 2) + pow((a_y - p_y), 2));
35161
35162 double rhalfca_x = 0.5 * (a_x - c_x);
35163 double rhalfca_y = 0.5 * (a_y - c_y);
35164
35165 double halfca_squared = pow(rhalfca_x, 2) + pow(rhalfca_y, 2);
35166
35167 double sticky_out_bit = r - sqrt(std::fabs((r * r) - halfca_squared));
35168
35169 // If sticky out bit divided by distance between end nodes
35170 // is less than tolerance the boundary is so flat that we
35171 // can safely kill the node
35172 if ((sticky_out_bit / (2.0 * sqrt(halfca_squared))) <
35173 unrefinement_tolerance)
35174 {
35175 do_it = true;
35176 if (check_only)
35177 {
35178 return true;
35179 }
35180 }
35181 }
35182
35183 // If the vertex was proposed for deletion check that it is
35184 // allowed for being deleted
35186 {
35187 // Is the vertex one of the non deletable vertices
35188 for (std::set<Vector<double>>::iterator it = no_delete_vertex.begin();
35189 it != no_delete_vertex.end();
35190 it++)
35191 {
35192 // Compute the distance between the proposed node to delete
35193 // and the ones that should not be deleted
35194 const double x = (*it)[0];
35195 const double y = (*it)[1];
35196 double error = (b_x - x) * (b_x - x) + (b_y - y) * (b_y - y);
35197 error = sqrt(error);
35198
35200 {
35201 // Do not delete the vertex
35202 do_it = false;
35203 break;
35204 }
35205 }
35206
35207 } // if (do_it && boundary_receive_connections)
35208
35209 // Remove node?
35210 if (do_it)
35211 {
35212 vector_bnd_vertices[i].resize(0);
35213 }
35214
35215 // Increase the counter, that indicates the number of the
35216 // next middle node
35217 counter += 2;
35218 }
35219
35220 // coming out of here the value of counter is the index of the
35221 // last node on the polyline counter=n_vertex-1 (in case of an
35222 // even number of nodes) or counter has the value of the number
35223 // of nodes on the polyline counter=n_vertex (in case of an odd
35224 // number of nodes
35225
35226 // Special treatment for the end of the polyline:
35227 // If the number of nodes is even, then the previous loop stopped
35228 // at the last but second node, i.e. the current value of counter
35229 // is the index of the last node. If that's the case, the last but
35230 // one node needs to be treated separately
35231 if ((counter) == (n_vertex - 1))
35232 {
35233 // Set the last but one node as middle node
35234 unsigned i = vector_bnd_vertices.size() - 2;
35235
35236 // Index of the current! last but second node (considering any
35237 // previous deletion)
35238 unsigned n = 0;
35239
35240 if (vector_bnd_vertices[counter - 2].size() != 0)
35241 {
35242 // if the initial last but second node does still exist then
35243 // this one is obviously also the current last but second one
35244 n = counter - 2;
35245 }
35246 else
35247 {
35248 // if the initial last but second node was deleted then the
35249 // initial last but third node is the current last but second
35250 // node
35251 n = counter - 3;
35252 }
35253
35254 // CODE DUPLICATION -- CAN'T BE BOTHERED TO WRITE A SEPARATE
35255 // FUNCTION FOR THIS; PROBABLY WORTH DOING IF/WHEN THERE'S
35256 // A MISTAKE IN ANY OF THIS AND IT NEEDS TO BE FIXED...
35257
35258 // Maths from http://www.cgafaq.info/wiki/Circle_Through_Three_Points
35259 double a_x = vector_bnd_vertices[n][1];
35260 double a_y = vector_bnd_vertices[n][2];
35261 double b_x = vector_bnd_vertices[i][1];
35262 double b_y = vector_bnd_vertices[i][2];
35263 double c_x = vector_bnd_vertices[i + 1][1];
35264 double c_y = vector_bnd_vertices[i + 1][2];
35265
35266 double a = b_x - a_x;
35267 double b = b_y - a_y;
35268 double c = c_x - a_x;
35269 double d = c_y - a_y;
35270
35271 double e = a * (a_x + b_x) + b * (a_y + b_y);
35272 double f = c * (a_x + c_x) + d * (a_y + c_y);
35273
35274 double g = 2.0 * (a * (c_y - b_y) - b * (c_x - b_x));
35275
35276 bool do_it = false;
35277 if (std::fabs(g) < 1.0e-14)
35278 {
35279 do_it = true;
35280 if (check_only)
35281 {
35282 return true;
35283 }
35284 }
35285 else
35286 {
35287 double p_x = (d * e - b * f) / g;
35288 double p_y = (a * f - c * e) / g;
35289
35290 double r = sqrt(pow((a_x - p_x), 2) + pow((a_y - p_y), 2));
35291
35292 double rhalfca_x = 0.5 * (a_x - c_x);
35293 double rhalfca_y = 0.5 * (a_y - c_y);
35294
35295 double halfca_squared = pow(rhalfca_x, 2) + pow(rhalfca_y, 2);
35296
35297 double sticky_out_bit = r - sqrt(std::fabs((r * r) - halfca_squared));
35298
35299 // If sticky out bit divided by distance between end nodes
35300 // is less than tolerance the boundary is so flat that we
35301 // can safely kill the node
35302 if ((sticky_out_bit / (2.0 * sqrt(halfca_squared))) <
35303 unrefinement_tolerance)
35304 {
35305 do_it = true;
35306 if (check_only)
35307 {
35308 return true;
35309 }
35310 }
35311 }
35312
35313 // If the vertex was proposed for deletion check that it is
35314 // allowed for being deleted
35316 {
35317 // Is the vertex one of the non deletable vertices
35318 for (std::set<Vector<double>>::iterator it = no_delete_vertex.begin();
35319 it != no_delete_vertex.end();
35320 it++)
35321 {
35322 // Compute the distance between the proposed node to delete
35323 // and the ones that should not be deleted
35324 const double x = (*it)[0];
35325 const double y = (*it)[1];
35326 double error = (b_x - x) * (b_x - x) + (b_y - y) * (b_y - y);
35327 error = sqrt(error);
35328
35330 {
35331 // Do not delete the vertex
35332 do_it = false;
35333 break;
35334 }
35335 }
35336
35337 } // if (do_it && boundary_receive_connections)
35338
35339 // Remove node?
35340 if (do_it)
35341 {
35342 vector_bnd_vertices[i].resize(0);
35343 }
35344 }
35345
35346 // Create another vector, which will only contain entries of
35347 // nodes that still exist
35349 compact_vector.reserve(n_vertex);
35350 for (unsigned i = 0; i < n_vertex; i++)
35351 {
35352 // If the entry was not deleted include it in the new vector
35353 if (vector_bnd_vertices[i].size() != 0)
35354 {
35356 }
35357 }
35358
35359 /// Get the size of the vector that now includes all remaining nodes
35361
35362 // If the size of the vector containing the remaining nodes is
35363 // different from the size of the vector before the unrefinement
35364 // routine (with the original nodes)
35365 // then the polyline was obviously updated
35367 {
35369 }
35370
35371 /// Copy back
35373 for (unsigned i = 0; i < n_vertex; i++)
35374 {
35375 vector_bnd_vertices[i].resize(3);
35379 }
35380
35382 }
35383
35384 //=========================================================================
35385 /// Helper function that performs the refinement process
35386 /// on the specified boundary by using the provided vertices
35387 /// representation. Optional boolean is used to run it as test only (if
35388 /// true is specified as input) in which case vertex coordinates aren't
35389 /// actually modified. Returned boolean indicates if polyline was (or
35390 /// would have been -- if called with check_only=false) changed.
35391 //=========================================================================
35392 template<class ELEMENT>
35396 double& refinement_tolerance,
35397 const bool& check_only)
35398 {
35399 // Boolean that indicates whether an actual update of the vertex
35400 // coordinates was performed or not
35401 bool refinement_was_performed = false;
35402
35403 // Create a geometric object from the mesh to represent
35404 // the curvilinear boundary
35406
35407 // Get the total number of current vertices
35408 unsigned n_vertex = vector_bnd_vertices.size();
35409
35410 // Create a new (temporary) vector for the nodes, so
35411 // that new nodes can be stored
35413
35414 // Reserve memory space for twice the number of already
35415 // existing nodes (worst case)
35416 extended_vector.reserve(2 * n_vertex);
35417
35418 // Loop over the nodes until the last but one node
35419 for (unsigned inod = 0; inod < n_vertex - 1; inod++)
35420 {
35421 // Get local coordinate of "left" node
35422 double zeta_left = vector_bnd_vertices[inod][0];
35423
35424 // Get position vector of "left" node
35426 for (unsigned i = 0; i < 2; i++)
35427 {
35429 }
35430
35431 // Get local coordinate of "right" node
35432 double zeta_right = vector_bnd_vertices[inod + 1][0];
35433
35434 // Get position vector of "right" node
35436 for (unsigned i = 0; i < 2; i++)
35437 {
35438 R_right[i] = vector_bnd_vertices[inod + 1][i + 1];
35439 }
35440
35441 // Get the boundary coordinate of the midpoint
35443 zeta_mid[0] = 0.5 * (zeta_left + zeta_right);
35444
35445 // Get the position vector of the midpoint on the
35446 // curvilinear boundary
35449
35450 // Get the position vector of the midpoint on the straight
35451 // line connecting "left" and "right" node
35453 for (unsigned i = 0; i < 2; i++)
35454 {
35455 R_mid_polygon[i] = 0.5 * (R_right[i] + R_left[i]);
35456 }
35457
35458 // Calculate the distance between the midpoint on the curvilinear
35459 // boundary and the midpoint on the straight line
35460 double distance =
35461 sqrt((R_mid[0] - R_mid_polygon[0]) * (R_mid[0] - R_mid_polygon[0]) +
35462 (R_mid[1] - R_mid_polygon[1]) * (R_mid[1] - R_mid_polygon[1]));
35463
35464 // Calculating the length of the straight line
35465 double length = sqrt((R_right[0] - R_left[0]) * (R_right[0] - R_left[0]) +
35466 (R_right[1] - R_left[1]) * (R_right[1] - R_left[1]));
35467
35468 // If the ratio of distance between the midpoints to the length
35469 // of the straight line is larger than the tolerance
35470 // specified for the criterion when points can be deleted,
35471 // create a new node and add it to the (temporary) vector
35472 if ((distance / length) > refinement_tolerance)
35473 {
35474 if (check_only)
35475 {
35476 // Delete the allocated memory for the geometric object
35477 // that represents the curvilinear boundary
35478 delete mesh_geom_obj_pt;
35479 return true;
35480 }
35481
35483 new_node[0] = zeta_mid[0];
35484 new_node[1] = R_mid[0];
35485 new_node[2] = R_mid[1];
35486
35487 // Include the "left" node in the new "temporary" vector
35489
35490 // Include the new node as well
35491 extended_vector.push_back(new_node);
35492 }
35493 else
35494 {
35495 // Include the "left" node in the new "temporary" vector
35496 // and move on to the next node
35498 }
35499 } // end of loop over nodes
35500
35501 // Add the last node to the vector
35503
35504 /// Get the size of the vector that now includes all added nodes
35506
35507 // If the size of the vector including the added nodes is
35508 // different from the size of the vector before the refinement
35509 // routine then the polyline was obviously updated
35511 {
35513 }
35514
35515 // Copy across
35517 for (unsigned i = 0; i < n_vertex; i++)
35518 {
35519 vector_bnd_vertices[i].resize(3);
35523 }
35524
35525 // Delete the allocated memory for the geometric object
35526 // that represents the curvilinear boundary
35527 delete mesh_geom_obj_pt;
35528
35530 }
35531
35532 //=========================================================================
35533 // Helper function that applies the maximum length constraint
35534 // when it was specified. This will increase the number of points in
35535 // the current curve section in case that any segment on it does not
35536 // fulfils the requirement
35537 //=========================================================================
35538 template<class ELEMENT>
35542 double& max_length_constraint)
35543 {
35544 // Boolean that indicates whether an actual update of the vertex
35545 // coordinates was performed or not
35546 bool max_length_applied = false;
35547
35548 // Create a geometric object from the mesh to represent
35549 // the curvilinear boundary
35551
35552 // Get the total number of current vertices
35553 unsigned n_vertex = vector_bnd_vertices.size();
35554
35555 // Create a new (temporary) vector for the nodes, so
35556 // that new nodes can be stored
35558
35559 // Loop over the nodes until the last but one node
35560 for (unsigned inod = 0; inod < n_vertex - 1; inod++)
35561 {
35562 // Get local coordinate of "left" node
35563 double zeta_left = vector_bnd_vertices[inod][0];
35564
35565 // Get position vector of "left" node
35567 for (unsigned i = 0; i < 2; i++)
35568 {
35570 }
35571
35572 // Get local coordinate of "right" node
35573 double zeta_right = vector_bnd_vertices[inod + 1][0];
35574
35575 // Get position vector of "right" node
35577 for (unsigned i = 0; i < 2; i++)
35578 {
35579 R_right[i] = vector_bnd_vertices[inod + 1][i + 1];
35580 }
35581
35582 // Include the "left" node in the new "temporary" vector
35584
35585 // Check whether the current distance between the left and right node
35586 // is longer than the specified constraint or not
35587 double length = std::fabs(zeta_right - zeta_left);
35588
35589 // Do we need to introduce new nodes?
35591 {
35593 // We only want the integer part
35594 unsigned n_points = static_cast<unsigned>(n_pts);
35595 double zeta_increment =
35596 (zeta_right - zeta_left) / ((double)n_points + 1);
35597
35599 // Create the n_points+1 points inside the segment
35600 for (unsigned s = 1; s < n_points + 1; s++)
35601 {
35602 // Get the coordinates
35606
35607 // Create the new node
35609 new_node[0] = zeta[0];
35610 new_node[1] = vertex[0];
35611 new_node[2] = vertex[1];
35612
35613 // Include the new node
35614 extended_vector.push_back(new_node);
35615 }
35616 }
35617 }
35618
35619 // Add the last node to the vector
35621
35622 /// Get the size of the vector that now includes all added nodes
35624
35625 // If the size of the vector including the added nodes is
35626 // different from the size of the vector before applying the maximum length
35627 // constraint then the polyline was obviously updated
35629 {
35630 max_length_applied = true;
35631 }
35632
35633 // Copy across
35635 for (unsigned i = 0; i < n_vertex; i++)
35636 {
35637 vector_bnd_vertices[i].resize(3);
35641 }
35642
35643 // Delete the allocated memory for the geometric object
35644 // that represents the curvilinear boundary
35645 delete mesh_geom_obj_pt;
35646
35647 return max_length_applied;
35648 }
35649
35650 //=========================================================================
35651 /// Helper function
35652 /// Creates an unsorted face mesh representation from the specified
35653 /// boundary id. It means that the elements are not sorted along the
35654 /// boundary
35655 //=========================================================================
35656 template<class ELEMENT>
35658 create_unsorted_face_mesh_representation(const unsigned& boundary_id,
35660 {
35661 // Create a face mesh adjacent to specified boundary.
35662 // The face mesh consists of FaceElements that may also be
35663 // interpreted as GeomObjects
35664
35665 // Build the face mesh
35667 boundary_id, face_mesh_pt);
35668
35669 // Find the total number of added elements
35670 unsigned n_element = face_mesh_pt->nelement();
35671 // Loop over the elements
35672 for (unsigned e = 0; e < n_element; e++)
35673 {
35674 // Cast the element pointer to the correct thing!
35676 dynamic_cast<FaceElementAsGeomObject<ELEMENT>*>(
35677 face_mesh_pt->element_pt(e));
35678
35679 // Set bulk boundary number
35680 el_pt->set_boundary_number_in_bulk_mesh(boundary_id);
35681 }
35682 }
35683
35684 //=========================================================================
35685 /// Helper function
35686 /// Creates a sorted face mesh representation of the specified PolyLine
35687 /// It means that the elements are sorted along the boundary
35688 //=========================================================================
35689 template<class ELEMENT>
35691 const unsigned& boundary_id,
35693 std::map<FiniteElement*, bool>& is_inverted,
35694 bool& inverted_face_mesh)
35695 {
35697
35698 // First step we get the unsorted version of the face mesh
35699 create_unsorted_face_mesh_representation(boundary_id,
35701
35702 // Once with the unsorted version of the face mesh
35703 // only left to sort it out!!!
35704
35705 // Put all face elements in order
35706 //-------------------------------
35707
35708 // Put first element into ordered list
35709 // Temporal list for sorting the elements
35710 std::list<FiniteElement*> sorted_el_pt;
35711 FiniteElement* el_pt = tmp_unsorted_face_mesh_pt->finite_element_pt(0);
35712 sorted_el_pt.push_back(el_pt);
35713
35714 // Number of nodes
35715 unsigned nnod = el_pt->nnode();
35716
35717 // Count elements that have been done
35718 unsigned count_done = 0;
35719
35720 // How many face elements are there?
35721 unsigned n_face_element = tmp_unsorted_face_mesh_pt->nelement();
35722
35723 // Keep track of who's done
35724 std::map<FiniteElement*, bool> done_el;
35725
35726 is_inverted.clear();
35727
35728 // Fit in the other elements in at most nel^2 loops
35729 for (unsigned ee = 1; ee < n_face_element; ee++)
35730 {
35731 // Loop over all elements to check if they fit to the right
35732 // or the left of the current one
35733 for (unsigned e = 1; e < n_face_element; e++)
35734 {
35735 // Candidate element
35736 el_pt = tmp_unsorted_face_mesh_pt->finite_element_pt(e);
35737
35738 // Is it done yet?
35739 if (!done_el[el_pt])
35740 {
35741 // Left and rightmost elements
35743 std::list<FiniteElement*>::iterator it = sorted_el_pt.end();
35744 it--;
35746
35747 // Left and rightmost nodes
35750 {
35752 }
35755 {
35757 }
35758
35759 // New element fits at the left of first element and is not inverted
35760 if (left_node_pt == el_pt->node_pt(nnod - 1))
35761 {
35762 sorted_el_pt.push_front(el_pt);
35763 done_el[el_pt] = true;
35764 count_done++;
35765 is_inverted[el_pt] = false;
35766 }
35767 // New element fits at the left of first element and is inverted
35768
35769 else if (left_node_pt == el_pt->node_pt(0))
35770 {
35771 sorted_el_pt.push_front(el_pt);
35772 done_el[el_pt] = true;
35773 count_done++;
35774 is_inverted[el_pt] = true;
35775 }
35776 // New element fits on the right of last element and is not inverted
35777
35778 else if (right_node_pt == el_pt->node_pt(0))
35779 {
35780 sorted_el_pt.push_back(el_pt);
35781 done_el[el_pt] = true;
35782 count_done++;
35783 is_inverted[el_pt] = false;
35784 }
35785 // New element fits on the right of last element and is inverted
35786
35787 else if (right_node_pt == el_pt->node_pt(nnod - 1))
35788 {
35789 sorted_el_pt.push_back(el_pt);
35790 done_el[el_pt] = true;
35791 count_done++;
35792 is_inverted[el_pt] = true;
35793 }
35794
35795 if (done_el[el_pt])
35796 {
35797 break;
35798 }
35799 }
35800 }
35801 }
35802
35803 // Are we done?
35804 if (count_done != (n_face_element - 1))
35805 {
35806 std::ostringstream error_message;
35807 error_message << "When ordering FaceElements on "
35808 << "boundary " << boundary_id << " only managed to order \n"
35809 << count_done << " of " << n_face_element
35810 << " face elements.\n"
35811 << std::endl;
35812 throw OomphLibError(
35813 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
35814 }
35815
35816 // Now make a mesh that contains the FaceElements in order
35817 // Remember that we currently have a list, not a mesh of sorted elements
35818
35819 // Fill it
35820 for (std::list<FiniteElement*>::iterator it = sorted_el_pt.begin();
35821 it != sorted_el_pt.end();
35822 it++)
35823 {
35824 // Get element
35826
35827 // add this face element to the order original mesh
35828 face_mesh_pt->add_element_pt(el_pt);
35829 }
35830
35831 // Verify if face mesh representation is not inverted according to the
35832 // polyline specified by the user, it means that the initial and the
35833 // final vertex does really correspond to the first and last vertex
35834 // respectively, if not, state that the face mesh representation is
35835 // inverted
35836
35837 // Get the associated polyline representation to the boundary
35839 this->Boundary_curve_section_pt[boundary_id];
35840
35841 // Get the really first vertex
35842 Vector<double> first_vertex = bnd_polyline->vertex_coordinate(0);
35843
35844 // Now get the first node based on the face mesh representation
35845 // First get access to the first element
35846 FiniteElement* first_el_pt = face_mesh_pt->finite_element_pt(0);
35847
35848 // Now get access to the first node
35849 unsigned n_node = first_el_pt->nnode();
35850 // Get the very first node (taking into account if it is
35851 // inverted or not!!)
35854 {
35856 }
35857
35858 double error = (first_node_pt->x(0) - first_vertex[0]) *
35859 (first_node_pt->x(0) - first_vertex[0]) +
35860 (first_node_pt->x(1) - first_vertex[1]) *
35861 (first_node_pt->x(1) - first_vertex[1]);
35862
35863 error = sqrt(error);
35864
35866 {
35867 inverted_face_mesh = false;
35868 }
35869 else
35870 {
35871 inverted_face_mesh = true;
35872 }
35873 }
35874
35875 //=========================================================================
35876 /// Helper function to construct face mesh representation of all polylines,
35877 /// possibly with segments re-distributed between polylines
35878 /// to maintain an approximately even sub-division of the polygon
35879 //=========================================================================
35880 template<class ELEMENT>
35883 {
35884 // Number of polylines
35885 unsigned n_polyline = polygon_pt->npolyline();
35886 face_mesh_pt.resize(n_polyline);
35887
35888 // Are we eligible for re-distributing polyline segments between
35889 // polylines? We're not if any of the boundaries are associated
35890 // with a GeomObject because we're then tied to the start and
35891 // end coordinates along it.
35893
35894 // Loop over constituent polylines
35895 for (unsigned p = 0; p < n_polyline; p++)
35896 {
35897 // Get the boundary id of the polyline
35898 unsigned bound = polygon_pt->polyline_pt(p)->boundary_id();
35899
35900 // If the boundary has a geometric object representation then
35901 // we can't redistribute
35902 GeomObject* const geom_object_pt = this->boundary_geom_object_pt(bound);
35903 if (geom_object_pt != 0)
35904 {
35906 }
35907
35908 face_mesh_pt[p] = new Mesh();
35909 create_unsorted_face_mesh_representation(bound, face_mesh_pt[p]);
35910 }
35911
35912 if (!polygon_pt->is_redistribution_of_segments_between_polylines_enabled())
35913 {
35914 return;
35915 }
35916
35917 // If there is more than one region we have to think... Die for now.
35918 if (this->nregion() > 1)
35919 {
35920 std::ostringstream warn_message;
35922 << "Can't currently re-distribute segments between polylines if there\n"
35923 << "are multiple regions; returning..." << std::endl;
35925 "RefineableTriangleMesh::get_face_mesh_representation()",
35927 return;
35928 }
35929
35930 // Redistribution overruled
35932 {
35933 std::ostringstream warn_message;
35935 << "Over-ruling re-distribution of segments between polylines\n"
35936 << "because at least one boundary is associated with a GeomObject."
35937 << "Returning..." << std::endl;
35939 "RefineableTriangleMesh::get_face_mesh_representation()",
35941 return;
35942 }
35943
35944 // Create a vector for ordered face mesh
35946
35947 // Storage for the total arclength of polygon
35948 double s_total = 0.0;
35949
35950 // Storage for first and last nodes on polylines so we can figure
35951 // out if they are inverted relative to each other
35954 std::vector<bool> is_reversed(n_polyline, false);
35955
35956 // Loop over constituent polylines
35957 for (unsigned p = 0; p < n_polyline; p++)
35958 {
35959 // Put all face elements in order
35960 //-------------------------------
35961
35962 // Put first element into ordered list
35963 std::list<FiniteElement*> ordered_el_pt;
35964 FiniteElement* el_pt = face_mesh_pt[p]->finite_element_pt(0);
35965 ordered_el_pt.push_back(el_pt);
35966
35967 // Number of nodes
35968 unsigned nnod = el_pt->nnode();
35969
35970 // Default for first and last node on polyline
35973
35974 // Count elements that have been done
35975 unsigned count_done = 0;
35976
35977 // How many face elements are there?
35978 unsigned n_face_element = face_mesh_pt[p]->nelement();
35979
35980 // Get the boundary id of the polyline
35981 unsigned bound = polygon_pt->polyline_pt(p)->boundary_id();
35982
35983 // Keep track of who's done
35984 std::map<FiniteElement*, bool> done_el;
35985
35986 // Keep track of which element is inverted
35987 std::map<FiniteElement*, bool> is_inverted;
35988
35989 // Fit in the other elements in at most nel^2 loops
35990 for (unsigned ee = 1; ee < n_face_element; ee++)
35991 {
35992 // Loop over all elements to check if they fit to the right
35993 // or the left of the current one
35994 for (unsigned e = 1; e < n_face_element; e++)
35995 {
35996 // Candidate element
35997 el_pt = face_mesh_pt[p]->finite_element_pt(e);
35998
35999 // Is it done yet?
36000 if (!done_el[el_pt])
36001 {
36002 // Left and rightmost elements
36004 std::list<FiniteElement*>::iterator it = ordered_el_pt.end();
36005 it--;
36007
36008 // Left and rightmost nodes
36011 {
36013 }
36016 {
36018 }
36019
36020 // New element fits at the left of first element and is not inverted
36021 if (left_node_pt == el_pt->node_pt(nnod - 1))
36022 {
36023 ordered_el_pt.push_front(el_pt);
36024 done_el[el_pt] = true;
36025 count_done++;
36026 is_inverted[el_pt] = false;
36028 }
36029 // New element fits at the left of first element and is inverted
36030
36031 else if (left_node_pt == el_pt->node_pt(0))
36032 {
36033 ordered_el_pt.push_front(el_pt);
36034 done_el[el_pt] = true;
36035 count_done++;
36036 is_inverted[el_pt] = true;
36038 }
36039 // New element fits on the right of last element and is not inverted
36040
36041 else if (right_node_pt == el_pt->node_pt(0))
36042 {
36043 ordered_el_pt.push_back(el_pt);
36044 done_el[el_pt] = true;
36045 count_done++;
36046 is_inverted[el_pt] = false;
36048 }
36049 // New element fits on the right of last element and is inverted
36050
36051 else if (right_node_pt == el_pt->node_pt(nnod - 1))
36052 {
36053 ordered_el_pt.push_back(el_pt);
36054 done_el[el_pt] = true;
36055 count_done++;
36056 is_inverted[el_pt] = true;
36058 }
36059
36060 if (done_el[el_pt])
36061 {
36062 break;
36063 }
36064 }
36065 }
36066 }
36067
36068 // Are we done?
36069 if (count_done != (n_face_element - 1))
36070 {
36071 std::ostringstream error_message;
36072 error_message << "When ordering FaceElements on "
36073 << "boundary " << bound << " only managed to order \n"
36074 << count_done << " of " << n_face_element
36075 << " face elements.\n"
36076 << std::endl;
36077 throw OomphLibError(error_message.str(),
36080 }
36081
36082 // Now make a mesh that contains the FaceElements in order
36084
36085 // Fill it
36086 for (std::list<FiniteElement*>::iterator it = ordered_el_pt.begin();
36087 it != ordered_el_pt.end();
36088 it++)
36089 {
36090 // Get element
36092
36093 // add this face element to the order original mesh
36094 ordered_face_mesh_pt[p]->add_element_pt(el_pt);
36095 }
36096
36097 // Get the arclength along the polygon
36098 for (unsigned e = 0; e < n_face_element; ++e)
36099 {
36100 FiniteElement* el_pt = ordered_face_mesh_pt[p]->finite_element_pt(e);
36101 unsigned n_node = el_pt->nnode();
36102 double element_length_squared = 0.0;
36103 for (unsigned i = 0; i < 2; i++)
36104 {
36106 pow(el_pt->node_pt(n_node - 1)->x(i) - el_pt->node_pt(0)->x(i), 2);
36107 }
36108
36109 // Determine element length
36111
36112 // Add this length to the total arclength
36114 }
36115
36116 // Empty the original meshes
36117 face_mesh_pt[p]->flush_element_and_node_storage();
36118 }
36119
36120 // Is first one reversed?
36123 {
36124 is_reversed[0] = false;
36125 }
36126 else if ((first_polyline_node_pt[0] == first_polyline_node_pt[1]) ||
36128 {
36129 is_reversed[0] = true;
36130 }
36131
36132 // Reorder the face meshes so that they are contiguous
36134 std::vector<bool> mesh_done(n_polyline, false);
36136
36137 // Initial entry
36139 unsigned current = 0;
36140 old_polyline_number[0] = 0;
36141 unsigned count_found = 0;
36142
36143 // Fill in the next entries
36144 for (unsigned p = 1; p < n_polyline; p++)
36145 {
36147 if (is_reversed[current])
36148 {
36150 }
36151
36152 // Loop over all remaining face meshes to see which one fits
36153 for (unsigned pp = 1; pp < n_polyline; pp++)
36154 {
36155 if (!mesh_done[pp])
36156 {
36157 // Current one is not reversed, candidate is not reversed
36158 if ((!is_reversed[current]) &&
36160 {
36162 mesh_done[pp] = true;
36163 is_reversed[pp] = false;
36165 current = pp;
36166 count_found++;
36167 break;
36168 }
36169 // Current one is not reversed, candidate is reversed
36170
36171 else if ((!is_reversed[current]) &&
36173 {
36175 mesh_done[pp] = true;
36176 is_reversed[pp] = true;
36178 current = pp;
36179 count_found++;
36180 break;
36181 }
36182 // Current one is reversed, candidate is not reversed
36183
36184 else if ((is_reversed[current]) &&
36186 {
36188 mesh_done[pp] = true;
36189 is_reversed[pp] = false;
36191 current = pp;
36192 count_found++;
36193 break;
36194 }
36195 // Current one is reversed, candidate is reversed
36196
36197 else if ((is_reversed[current]) &&
36199 {
36201 mesh_done[pp] = true;
36202 is_reversed[pp] = true;
36204 current = pp;
36205 count_found++;
36206 break;
36207 }
36208 }
36209 }
36210 }
36211
36212#ifdef PARANOID
36213 if (count_found != n_polyline - 1)
36214 {
36215 std::ostringstream error_message;
36216 error_message << "Only found " << count_found << " out of "
36217 << n_polyline - 1 << " polylines to be fitted in.\n";
36218 throw OomphLibError(
36219 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
36220 }
36221#endif
36222
36223 // Now overwrite the re-ordered data
36224 for (unsigned i = 0; i < n_polyline; i++)
36225 {
36227 }
36228
36229 // Now do an approximate equidistribution of polylines
36230 //----------------------------------------------------
36231 double s = 0.0;
36232 unsigned new_face_id = 0;
36233
36234 // Matrix map to indicate if node must not be removed from specified
36235 // boundary (!=0) or not (=0). Initialises itself to zero
36236 std::map<Node*, std::map<unsigned, unsigned>>
36238
36239 // Loop over the old face mesh
36240 for (unsigned p = 0; p < n_polyline; p++)
36241 {
36242 // Loop over the face elements
36243 unsigned n_face_element = ordered_face_mesh_pt[p]->nelement();
36244 for (unsigned e = 0; e < n_face_element; e++)
36245 {
36246 unsigned el_number = e;
36247 if (is_reversed[p])
36248 {
36249 el_number = n_face_element - e - 1;
36250 }
36251
36253 ordered_face_mesh_pt[p]->finite_element_pt(el_number);
36254 unsigned n_node = el_pt->nnode();
36255
36256 // Determine element length
36257 double element_length_squared = 0.0;
36258 for (unsigned i = 0; i < 2; i++)
36259 {
36261 pow(el_pt->node_pt(n_node - 1)->x(i) - el_pt->node_pt(0)->x(i), 2);
36262 }
36264
36265 // Add this length to the total arclength
36266 s += element_length;
36267
36268 // Check if the current 'arclength' is less than the
36269 // whole 'arclength' divided by the number of polylines
36270 if (s < s_total / double(n_polyline) + 1e-6)
36271 {
36272 // If so add this face element to the new face mesh
36273 face_mesh_pt[new_face_id]->add_element_pt(el_pt);
36274
36275 unsigned bound_old =
36276 polygon_pt->polyline_pt(old_polyline_number[p])->boundary_id();
36277
36278 unsigned bound_new =
36279 polygon_pt->polyline_pt(new_face_id)->boundary_id();
36280
36281 // Loop over the nodes in the element
36282 for (unsigned i = 0; i < n_node; i++)
36283 {
36284 // Get the pointer to the node
36285 Node* nod_pt = el_pt->node_pt(i);
36286
36287 // If the two boundary id's are different, the face element's nodes
36288 // have to be added to the new boundary
36289 if (bound_new != bound_old)
36290 {
36291 // Add it to the new boundary
36292 add_boundary_node(bound_new, nod_pt);
36293
36294 // We are happy for this node to be removed from the
36295 // old boundary?
36297 0;
36298 }
36299
36300 // If the face element hasn't moved, its nodes MUST remain
36301 // on that boundary (incl. any nodes that ar shared by
36302 // FaceElements that have moved (see above)
36303
36304 else
36305 {
36307 1;
36308 }
36309 }
36310 }
36311
36312 // If not, reset the current 'arclength' to zero,
36313 // increase the new face id by one and go one element
36314 // back by decreasing e by one to make sure the current
36315 // element gets added to the next face mesh
36316
36317 else
36318 {
36319 if (new_face_id != n_polyline - 1)
36320 {
36321 s = 0.0;
36322 new_face_id++;
36323 --e;
36324 }
36325 else
36326 {
36327 s = 0.0;
36328 --e;
36329 }
36330 }
36331 }
36332 } // end of loop over all polylines -- they are now re-distributed
36333
36334 // Loop over all nodes on the boundaries of the polygon to remove
36335 // nodes from boundaries they are no longer on
36336 unsigned move_count = 0;
36337 for (std::map<Node*, std::map<unsigned, unsigned>>::iterator it =
36340 it++)
36341 {
36342 // Get the node
36343 Node* nod_pt = (*it).first;
36344
36345 // Now we loop over the boundaries that this node is on
36346 for (std::map<unsigned, unsigned>::iterator it_2 = (*it).second.begin();
36347 it_2 != (*it).second.end();
36348 it_2++)
36349 {
36350 // Get the boundary id
36351 unsigned bound = (*it_2).first;
36352
36353 // Remove it from that boundary?
36354 if ((*it_2).second == 0)
36355 {
36356 remove_boundary_node(bound, nod_pt);
36357 move_count++;
36358 }
36359 }
36360 }
36361
36362 // Loop over the new face mesh to assign new boundary IDs
36363 for (unsigned p = 0; p < n_polyline; p++)
36364 {
36365 // Get the boundary id of the polyline
36366 unsigned bound = polygon_pt->polyline_pt(p)->boundary_id();
36367
36368 // Loop over the face elements
36369 unsigned n_face_element = face_mesh_pt[p]->nelement();
36370 for (unsigned e = 0; e < n_face_element; e++)
36371 {
36372 // Cast the element pointer to the correct thing!
36374 dynamic_cast<FaceElementAsGeomObject<ELEMENT>*>(
36375 face_mesh_pt[p]->element_pt(e));
36376
36377 // Set bulk boundary number
36378 el_pt->set_boundary_number_in_bulk_mesh(bound);
36379 }
36380 }
36381
36382 // Update look-up for elements next to boundary
36383 setup_boundary_element_info();
36384
36385 // Now re-create the boundary coordinates
36386 for (unsigned p = 0; p < n_polyline; p++)
36387 {
36388 // Get the boundary id of the polyline
36389 unsigned bound = polygon_pt->polyline_pt(p)->boundary_id();
36390
36391 // Do it
36392 this->template setup_boundary_coordinates<ELEMENT>(bound);
36393 }
36394
36395 // Clean up
36396 for (unsigned p = 0; p < n_polyline; p++)
36397 {
36398 // Flush the nodes from the face mesh to make sure we
36399 // don't delete them (the face mesh that we're returning from here
36400 // still needs them!)
36401 ordered_face_mesh_pt[p]->flush_element_and_node_storage();
36402 delete ordered_face_mesh_pt[p];
36403 }
36404 }
36405
36406 //=========================================================================
36407 /// Helper function to construct face mesh representation of all polylines
36408 //=========================================================================
36409 template<class ELEMENT>
36412 {
36413 // Number of polylines
36414 unsigned n_polyline = open_polyline_pt->ncurve_section();
36415 face_mesh_pt.resize(n_polyline);
36416
36417 // Loop over constituent polylines
36418 for (unsigned p = 0; p < n_polyline; p++)
36419 {
36420 // Get the boundary id of the polyline
36421 unsigned bound = open_polyline_pt->curve_section_pt(p)->boundary_id();
36422
36423 face_mesh_pt[p] = new Mesh();
36424 create_unsorted_face_mesh_representation(bound, face_mesh_pt[p]);
36425 }
36426 }
36427
36428 //======================================================================
36429 /// Update the PSLG that define the inner boundaries of the mesh.
36430 /// Optional boolean is used to run it as test only (if
36431 /// true is specified as input) in which case PSLG isn't actually
36432 /// modified. Returned boolean indicates if PSLG was (or would have
36433 /// been -- if called with check_only=false) changed.
36434 //======================================================================
36435 template<class ELEMENT>
36437 ELEMENT>::surface_remesh_for_inner_hole_boundaries(Vector<Vector<double>>&
36439 const bool& check_only)
36440 {
36441 // Boolean to indicate whether an actual update of the internal
36442 // holes was performed
36443 bool update_was_performed = false;
36444 // Loop over the number of internal boundaries
36445 unsigned n_hole = internal_point_coord.size();
36446 for (unsigned ihole = 0; ihole < n_hole; ihole++)
36447 {
36448 // Cache the pointer to the polygon representation
36449 TriangleMeshPolygon* const poly_pt = this->Internal_polygon_pt[ihole];
36450
36451 // Can the polygon update its own configuration, in which case this
36452 // is easy
36453 if (poly_pt->can_update_reference_configuration())
36454 {
36455 poly_pt->reset_reference_configuration();
36456
36457 // Initialize Vector hole_coordinates
36458 internal_point_coord[ihole].resize(2);
36459
36460 // Get the vector of hole coordinates
36461 internal_point_coord[ihole] = poly_pt->internal_point();
36462 }
36463 // Otherwise we have to work much harder
36464
36465 else
36466 {
36467 // if we only want to check whether an update of the inner
36468 // hole is necessary
36469 if (check_only)
36470 {
36471 // is it necessary?
36472 bool update_necessary =
36473 this->update_polygon_using_face_mesh(poly_pt, check_only);
36474
36475 // Yes?
36476 if (update_necessary)
36477 {
36478 // then we have to adaptand return 'true'
36479 return true;
36480 }
36481 }
36482 // if we not only want to check, then we actually perform
36483 // the update
36484 else
36485 {
36486 update_was_performed = this->update_polygon_using_face_mesh(poly_pt);
36487 }
36488
36489 // Now we need to sort out the hole coordinates
36490 if (!poly_pt->internal_point().empty())
36491 {
36492 // If fixed don't update and simply
36493 // Read out the existing value
36494 if (poly_pt->is_internal_point_fixed())
36495 {
36496 // Get the vector of hole coordinates
36497 internal_point_coord[ihole] = poly_pt->internal_point();
36498 }
36499 // This is where the work starts and this could be made much
36500 // better than the current hack
36501 else
36502 {
36503 // If the user has set their own function then use that
36504 if (this->Internal_hole_point_update_fct_pt != 0)
36505 {
36506 this->Internal_hole_point_update_fct_pt(ihole, poly_pt);
36507 }
36508 // Otherwise use our clunky default
36509 else
36510 {
36511 // Now sort out the hole coordinates
36513 unsigned n_polyline = poly_pt->npolyline();
36514
36515 // Initialize Vector hole_coordinates
36516 vertex_coord.resize(2);
36517 internal_point_coord[ihole].resize(2);
36518
36519 // Hole centre will be found by averaging the position of
36520 // all vertex nodes
36521 internal_point_coord[ihole][0] = 0.0;
36522 internal_point_coord[ihole][1] = 0.0;
36523
36524 for (unsigned p = 0; p < n_polyline; p++)
36525 {
36526 Vector<double> poly_ave(2, 0.0);
36527 // How many vertices are there in the segment
36528 unsigned n_vertex = poly_pt->polyline_pt(p)->nvertex();
36529 for (unsigned v = 0; v < n_vertex; v++)
36530 {
36531 vertex_coord = poly_pt->polyline_pt(p)->vertex_coordinate(v);
36532 for (unsigned i = 0; i < 2; i++)
36533 {
36534 poly_ave[i] += vertex_coord[i];
36535 }
36536 }
36537
36538 // Add the average polyline coordinate to the hole centre
36539 for (unsigned i = 0; i < 2; i++)
36540 {
36542 }
36543 }
36544
36545 // Now average out the hole centre
36546 for (unsigned i = 0; i < 2; i++)
36547 {
36549 }
36550
36551 // We have now found the hole centre stored in
36552 // internal_point_coordinate[ihole][i]
36553
36554 // Find polylines that intersect at y average value
36555 // Alice's version but this does not work if the end point of a
36556 // segment is the intersection point (i.e. at the y average value)
36557 /*Vector<double> vertex_coord2;
36558 unsigned n_intersect=0;
36559 double x_average=0.0;
36560
36561 for(unsigned p=0;p<n_polyline;p++)
36562 {
36563 //How many vertices are there in the segment
36564 unsigned n_vertex = poly_pt->polyline_pt(p)->nvertex();
36565 for(unsigned v=0;v<n_vertex-1;v++)
36566 {
36567 vertex_coord = poly_pt->polyline_pt(p)->vertex_coordinate(v);
36568 vertex_coord2 = poly_pt->polyline_pt(p)->vertex_coordinate(v+1);
36569 std::cout << vertex_coord[0] << " " << vertex_coord[1]
36570 << " " <<
36571 vertex_coord2[0] << " " <<
36572
36573 vertex_coord2[1] << "\n";
36574 //Does the line between vertices intersect the vertical position
36575 if((vertex_coord[1] -internal_point_coord[ihole][1])*
36576 (vertex_coord2[1] - internal_point_coord[ihole][1]) < 0.0)
36577 {
36578 ++n_intersect; x_average += 0.5*(vertex_coord[0] +
36579 vertex_coord2[0]);
36580 }
36581 }
36582 }
36583
36584 //Now just report the value if we have had intersections
36585 if(n_intersect != 0)
36586 {
36587 //Report
36588 std::cout << "I have computed a hole " << x_average << " " <<
36589 n_intersect << " "
36590 << x_average/((double)n_intersect) << std::endl;
36591 internal_point_coord[ihole][0] =
36592 x_average/((double)n_intersect);
36593 }
36594 */
36595
36596 // Set the new hole centre
36597 poly_pt->internal_point() = internal_point_coord[ihole];
36598 // std::cout << "I've had my centre updated to "
36599 // << internal_point_coord[ihole][0]
36600 // << " " << internal_point_coord[ihole][1] << "\n";
36601 }
36602 }
36603 }
36604 }
36605 } // End of the action (n_hole for)
36606
36607 if (check_only)
36608 {
36609 // If we make it up to here and we only check then no update is required
36610 return false;
36611 }
36612 else
36613 {
36614 // otherwise indicate whether an actual update was performed
36615 return update_was_performed;
36616 }
36617
36618 } // End of the loop of internal boundaries
36619
36620 //======================================================================
36621 /// Create the polylines and fill associate data structures, used when
36622 /// creating from a mesh from polyfiles
36623 //======================================================================
36624 template<class ELEMENT>
36626 const std::string& node_file_name, const std::string& poly_file_name)
36627 {
36628 // Get the nodes coordinates (the index of the nodes to build the
36629 // polylines is the one used in the node_file_name file)
36630 // Process node file
36631 // -----------------
36632 std::ifstream node_file(node_file_name.c_str(), std::ios_base::in);
36633
36634 // Check that the file actually opened correctly
36635 if (!node_file.is_open())
36636 {
36637 std::string error_msg("Failed to open node file: ");
36638 error_msg += "\"" + node_file_name + "\".";
36639 throw OomphLibError(
36641 }
36642
36643 // Read number of nodes
36644 unsigned nnodes;
36645 node_file >> nnodes;
36646
36647 // Spatial dimension of nodes
36648 unsigned dimension;
36650
36651#ifdef PARANOID
36652 if (dimension != 2)
36653 {
36654 throw OomphLibError("The dimension must be 2\n",
36657 }
36658#endif
36659
36660 // Storage the nodes vertices
36663
36664 // Number of attributes
36665 unsigned npoint_attributes;
36667 ;
36668
36669 // Flag for boundary markers
36670 unsigned boundary_markers_flag = 0;
36672
36673 // Dummy for node number
36674 unsigned dummy_node_number;
36675 // Dummy for node attribute
36676 unsigned dummy_node_attribute;
36677 // Dummy for node boundary
36678 unsigned dummy_node_boundary;
36679
36680 // Load in nodal posititions, point attributes
36681 // and boundary markers
36682 for (unsigned i = 0; i < nnodes; i++)
36683 {
36685 node_file >> x_node[i];
36686 node_file >> y_node[i];
36687 for (unsigned j = 0; j < npoint_attributes; ++j)
36688 {
36690 }
36692 {
36694 }
36695 }
36696 node_file.close();
36697
36698 // Get the segments information and use that info. to create the
36699 // polylines
36700
36701 // A map to store the segments associated to a boundary, non sorted
36702 std::map<unsigned, Vector<std::pair<unsigned, unsigned>>>
36704
36705 // Independent storage for the boundaries ids found in the segments so that
36706 // the polylines, and therefore polygons be created in the order they appear
36707 // in the polyfile
36709
36710 // Process poly file to extract edges
36711 //-----------------------------------
36712
36713 // Open poly file
36714 std::ifstream poly_file(poly_file_name.c_str(), std::ios_base::in);
36715
36716 // Check that the file actually opened correctly
36717 if (!poly_file.is_open())
36718 {
36719 std::string error_msg("Failed to open poly file: ");
36720 error_msg += "\"" + poly_file_name + "\".";
36721 throw OomphLibError(
36723 }
36724
36725 // Number of nodes in poly file --- these will be ignore
36726 unsigned n_node_poly;
36728
36729 // Dimension
36731
36732 // Attribute flag
36733 unsigned attribute_flag;
36735
36736 // Flag for boundary markers
36738
36739 // Ignore node information: Note: No, we can't extract the
36740 // actual nodes themselves from here!
36741 unsigned dummy;
36742 for (unsigned i = 0; i < n_node_poly; i++)
36743 {
36744 // Read in (and discard) node number and x and y coordinates
36745 poly_file >> dummy;
36746 poly_file >> dummy;
36747 poly_file >> dummy;
36748 // read in the attributes
36749 for (unsigned j = 0; j < attribute_flag; ++j)
36750 {
36751 poly_file >> dummy;
36752 }
36753 // read in the boundary marker
36754 if (boundary_markers_flag == 1)
36755 {
36756 poly_file >> dummy;
36757 }
36758 }
36759
36760 // Variable used to read the values from the input file
36761 unsigned read_value;
36762
36763 // Number of segments
36765 const unsigned nglobal_segments = read_value;
36766
36767 // Boundary marker flag
36769
36770 // Global segment number
36771 unsigned global_segment_number;
36772
36773 // Node identifier set (used to identify possible internal boundaries)
36774 std::set<unsigned> nodes_ids;
36775
36776 // Extract information for each segment
36777 for (unsigned i = 0; i < nglobal_segments; i++)
36778 {
36779 // Node id on the edge of the segment
36780 unsigned lnode_id = 0; // left node
36781 unsigned rnode_id = 0; // right node
36782 unsigned bnd_id = 0; // boundary id associated to the current segment
36786 nodes_ids.insert(lnode_id);
36787 nodes_ids.insert(rnode_id);
36789 {
36790 poly_file >> bnd_id;
36791 }
36792
36793 // Store the segments info. (use bnd_id - 1 because the nodes and
36794 // elements associated the bnd_id have been associated by external
36795 // methods to bnd_id - 1)
36796 unsorted_boundary_segments[bnd_id - 1].push_back(
36797 std::make_pair(lnode_id, rnode_id));
36798
36799 // Add the boundary id to the vector of boundaries ids only if it
36800 // has not been added, the polylines will be created using this
36801 // order
36802
36803 // Get the number of boundaries ids currently sorted
36805 // Flag to know if the boundary id was found
36806 bool boundary_id_found = false;
36807 for (unsigned ib = 0; ib < nsorted_boundaries_ids; ib++)
36808 {
36809 if (sorted_boundaries_ids[ib] == bnd_id - 1)
36810 {
36811 boundary_id_found = true;
36812 break;
36813 } // if (sorted_boundaries_ids[ib] == bnd_id - 1)
36814 } // for (ib < nsorted_boundaries_ids)
36815
36816 // If th boundary id has not been added, then add it!!!
36817 if (!boundary_id_found)
36818 {
36819 sorted_boundaries_ids.push_back(bnd_id - 1);
36820 } // if (!boundary_id_found)
36821 }
36822
36823 // Verify if there are internal boundaries defined, if that is the
36824 // case we can not continue since we are not yet supporting internal
36825 // boundaries defined in polyfiles to created a mesh that may be
36826 // adapted
36827#ifdef PARANOID
36829 {
36830 std::ostringstream error_message;
36831 error_message
36832 << "The number of nodes (" << nodes_ids.size() << ") and segments ("
36833 << nglobal_segments << ") is different.\nThis may mean that there "
36834 << "are internal non-closed boundaries defined in\nthe polyfile. "
36835 << "If you need this feature please use the TriangleMeshPoyLine\n"
36836 << "and TriangleMeshCurviLine objects to define your domain.\n\n";
36837 throw OomphLibError(
36838 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
36839 }
36840#endif
36841
36842 // Now sort the segments associated to a boundary to create a contiguous
36843 // polyline, but first check that the number of found boundaries be the
36844 // same as the current number of boundaries in the mesh
36845 const unsigned nboundary = unsorted_boundary_segments.size();
36846
36847#ifdef PARANOID
36848 if (nboundary != this->nboundary())
36849 {
36850 std::ostringstream error_message;
36851 error_message
36852 << "The number of boundaries on the mesh (" << this->nboundary()
36853 << ") is different from the number of\nboundaries read from the "
36854 << "polyfiles (" << unsorted_boundary_segments.size() << ")!!!\n\n\n";
36855 throw OomphLibError(
36856 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
36857 }
36858#endif
36859
36860 // Get the number of sorted boundaries ids and check that it matches
36861 // with the total number of boundaries
36863#ifdef PARANOID
36864 if (nsorted_boundaries_ids != this->nboundary())
36865 {
36866 std::ostringstream error_message;
36867 error_message
36868 << "The number of boundaries on the mesh (" << this->nboundary()
36869 << ") is different from the number of\nsorted boundaries ids read "
36870 << "from the polyfiles (" << nsorted_boundaries_ids << ")!!!\n\n\n";
36871 throw OomphLibError(
36872 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
36873 }
36874#endif
36875
36876 // Sorted segments (to create a polyline -- boundary)
36877 std::map<unsigned, std::list<unsigned>> sorted_boundary_segments;
36878
36879 // Go through all the found boundaries
36880 std::map<unsigned, Vector<std::pair<unsigned, unsigned>>>::iterator it;
36881
36882 for (it = unsorted_boundary_segments.begin();
36884 it++)
36885 {
36886 // Get the current boundary id, only look for the segments
36887 // associated with this boundary
36888 const unsigned bnd_id = (*it).first;
36890
36891 // Now sort the segments associated to this boundary
36892 std::map<std::pair<unsigned, unsigned>, bool> segment_done;
36893 const unsigned nsegments = segments_edges.size();
36894
36895 // Sorted nodes for the current segment
36896 std::list<unsigned> sorted_segments;
36897
36898 // Get the left and right node of the zero segment
36899 unsigned left_node_id = segments_edges[0].first;
36900 unsigned right_node_id = segments_edges[0].second;
36901
36902 // ... and add it to the sorted segments structure
36903 sorted_segments.push_back(left_node_id);
36904 sorted_segments.push_back(right_node_id);
36905
36906 // Mark the current segment as done
36907 segment_done[segments_edges[0]] = true;
36908
36909 // Set the number of sorted segments
36910 unsigned nsorted_segments = 1;
36911
36912 while (nsorted_segments < nsegments)
36913 {
36914 for (unsigned i = 1; i < nsegments; i++)
36915 {
36916 // Check if the i-th segments has been done
36918 {
36919 // Get the left and right node id
36920 unsigned current_left_node_id = segments_edges[i].first;
36921 unsigned current_right_node_id = segments_edges[i].second;
36922
36923 // Now check if the current segment can be added to the left
36924 // or right side of the sorted segments
36926 {
36927 // Add the current_right_node_id to the right of the sorted
36928 // segments
36930 // Increase the number of sorted segments
36932 // Mark the segment as done
36933 segment_done[segments_edges[i]] = true;
36934 // Update the right most node
36936 // Break the for loop
36937 break;
36938 }
36940 {
36941 // Add the current_left_node_id to the left of the sorted
36942 // segments
36944 // Increase the number of sorted segments
36946 // Mark the segment as done
36947 segment_done[segments_edges[i]] = true;
36948 // Update the left most node
36950 // Break the for loop
36951 break;
36952 }
36954 {
36955 // Add the current_right_node_id to the left of the sorted
36956 // segments
36958 // Increase the number of sorted segments
36960 // Mark the segment as done
36961 segment_done[segments_edges[i]] = true;
36962 // Update the left most node
36964 // Break the for loop
36965 break;
36966 }
36968 {
36969 // Add the current_left_node_id to the right of the sorted
36970 // segments
36972 // Increase the number of sorted segments
36974 // Mark the segment as done
36975 segment_done[segments_edges[i]] = true;
36976 // Update the left most node
36978 // Break the for loop
36979 break;
36980 }
36981 } // if (!segment_done[segments_edges[i]])
36982 } // for (i < nsegments)
36983 } // while(nsorted_segments < nsegments)
36984
36986
36987 } // for (unsorted_boundary_segments.begin();
36988 // unsorted_boundary_segments.end())
36989
36990#ifdef PARANOID
36991 if (sorted_boundary_segments.size() != this->nboundary())
36992 {
36993 std::ostringstream error_message;
36994 error_message
36995 << "The number of boundaries on the mesh (" << this->nboundary()
36996 << ") is different from the number\nof sorted boundaries to create the "
36997 << "polylines (" << sorted_boundary_segments.size() << ")\n\n";
36998 throw OomphLibError(
36999 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
37000 }
37001#endif
37002
37003 // Now we have the sorted nodes, we can create the polylines by
37004 // getting the vertices of the nodes
37006 unsigned current_polyline = 0;
37007
37008 // Go through the sorted boundaries using the sorted boundaries ids
37009 for (unsigned ib = 0; ib < nsorted_boundaries_ids; ib++)
37010 {
37011 // Get the boundary id from the vector of sorted boundaries ids
37012 const unsigned bnd_id = sorted_boundaries_ids[ib];
37013
37014 // Create a vector representation for ease to use
37015 // Get the vertices of the nodes that create the boundary / polyline
37017 for (std::list<unsigned>::iterator it_list =
37020 it_list++)
37021 {
37022 nodes_ids.push_back((*it_list));
37023 }
37024
37025 // Get the number of vertices for the polyline
37026 const unsigned nvertices = nodes_ids.size();
37027
37028 // The storage for the vertices
37029 Vector<Vector<double>> vertices(nvertices);
37030
37031 // Now get the vertices of the nodes of the current boundary
37032 for (unsigned i = 0; i < nvertices; i++)
37033 {
37034 // Get the vertices
37035 vertices[i].resize(2);
37036 vertices[i][0] = x_node[nodes_ids[i] - 1];
37037 vertices[i][1] = y_node[nodes_ids[i] - 1];
37038 }
37039
37040 // Now create the polyline
37041
37042 // Note: The bnd_id is the real bnd_id (from the input file) - 1
37043 // since nodes and elements of the current boundary have been
37044 // associated to bnd_id - 1)
37047
37048 // Updates bnd_id<--->curve section map
37049 this->Boundary_curve_section_pt[bnd_id] =
37051
37052 // Increase the index for the polyline storage
37054
37055 } // for (it_sorted = sorted_boundary_segments.begin();
37056 // it_sorted != sorted_boundary_segments.end())
37057
37058 // Now create the polygons or closed curves
37059 // Sort the polylines to create polygons
37060 unsigned nsorted_polylines = 0;
37061
37062 // Number of created polygons
37063 unsigned npolygons = 0;
37064
37065 // Storage for the polygons
37067
37068 // Mark the already done polylines
37069 std::map<unsigned, bool> polyline_done;
37070 while (nsorted_polylines < nboundary)
37071 {
37072 // Storage for the curve sections that create a polygon
37073 std::list<TriangleMeshCurveSection*> sorted_curve_sections_pt;
37074
37075 unsigned init_poly = 0;
37076#ifdef PARANOID
37077 bool found_root_polyline = false;
37078#endif
37079 // Get the left and right node of the current polyline
37080 for (unsigned i = 0; i < nboundary; i++)
37081 {
37082 if (!polyline_done[i])
37083 {
37084 init_poly = i;
37085 // Increase the number of sorted polylines
37087#ifdef PARANOID
37088 // Mark as found the root polyline
37089 found_root_polyline = true;
37090#endif
37091 // Mark the polyline as done
37092 polyline_done[i] = true;
37093 // Add the polyline to the curve sections storage
37095 // Break the loop to set we have found a root polyline
37096 break;
37097 }
37098 }
37099
37100#ifdef PARANOID
37102 {
37103 std::ostringstream error_message;
37104 error_message << "Was not possible to found the root polyline to "
37105 "create polygons\n\n";
37106 throw OomphLibError(error_message.str(),
37109 }
37110#endif
37111
37112 // Get the associated boundary to the current polyline
37113 const unsigned bnd_id = polylines_pt[init_poly]->boundary_id();
37114 // Get the initial and final node id of the current polyline
37115 unsigned left_node_id = sorted_boundary_segments[bnd_id].front();
37117
37118 // Flag to know that we already have a closed polygon
37119 bool closed_polygon = false;
37120
37121 do
37122 {
37123 // Go through all the polylines
37124 for (unsigned i = init_poly; i < nboundary; i++)
37125 {
37126 // Check that the polyline has not been currently done
37127 if (!polyline_done[i])
37128 {
37129 // Get the initial and final nodes id of the current polyline
37130
37131 // Get the associated boundary to the current polyline
37132 const unsigned cbnd_id = polylines_pt[i]->boundary_id();
37133 // Get the initial and final node id of the current polyline
37136
37137 // Check if the polyline goes to the left or right of the
37138 // current sorted polylines
37140 {
37141 // Add the polyline to the curve section storage
37143 // Mark the polyline as done
37144 polyline_done[i] = true;
37145 // Update the right node
37147 // Increase the number of done polyines
37149 // Break the for loop
37150 break;
37151 }
37152 else if (cright_node_id == left_node_id)
37153 {
37154 // Add the polyline to the curve section storage
37156 // Mark the polyline as done
37157 polyline_done[i] = true;
37158 // Update the right node
37160 // Increase the number of done polyines
37162 // Break the for loop
37163 break;
37164 }
37165 else if (cleft_node_id == left_node_id)
37166 {
37167 // First reverse the polyline
37168 polylines_pt[i]->reverse();
37169 // Add the polyline to the curve section storage
37171 // Mark the polyline as done
37172 polyline_done[i] = true;
37173 // Update the right node
37175 // Increase the number of done polyines
37177 // Break the for loop
37178 break;
37179 }
37180 else if (cright_node_id == right_node_id)
37181 {
37182 // First reverse the polyline
37183 polylines_pt[i]->reverse();
37184 // Add the polyline to the curve section storage
37186 // Mark the polyline as done
37187 polyline_done[i] = true;
37188 // Update the right node
37190 // Increase the number of done polyines
37192 // Break the for loop
37193 break;
37194 }
37195 } // if (!polyline_done[i])
37196
37197 } // for (i < nboundary)
37198
37199 // We have created a polygon
37201 {
37202 // Set the flag as true
37203 closed_polygon = true;
37204 }
37205
37206 } while (nsorted_polylines < nboundary && !closed_polygon);
37207
37208#ifdef PARANOID
37209 if (!closed_polygon)
37210 {
37211 std::ostringstream error_message;
37212 error_message
37213 << "It was not possible to create a closed curve, these are the "
37214 << "vertices of the already sorted polylines\n\n";
37215 unsigned cpolyline = 0;
37216 for (std::list<TriangleMeshCurveSection*>::iterator it_list =
37219 it_list++)
37220 {
37221 error_message << "Polyline (" << cpolyline << ")\n";
37223 dynamic_cast<TriangleMeshPolyLine*>((*it_list));
37224 const unsigned nvertex = tmp_poly_pt->nvertex();
37225 for (unsigned v = 0; v < nvertex; v++)
37226 {
37227 error_message << "(" << tmp_poly_pt->vertex_coordinate(v)[0] << ", "
37228 << tmp_poly_pt->vertex_coordinate(v)[1] << ")\n";
37229 }
37230 error_message << "\n";
37231 cpolyline++;
37232 }
37233 throw OomphLibError(error_message.str(),
37236 }
37237#endif
37238
37239 // Create a vector version to create the polygon from the sorted
37240 // polyines
37242 for (std::list<TriangleMeshCurveSection*>::iterator it_list =
37245 it_list++)
37246 {
37248 }
37249
37250 // Create a new polygon by using the new created polylines
37253
37254 // Keep track of new created polygons that need to be deleted!!!
37255 this->Free_polygon_pt.insert(polygon_pt);
37256
37257 // Store the polygon in the polygons storages
37258 polygons_pt.push_back(polygon_pt);
37259
37260 npolygons++;
37261
37262 } // while(nsorted_polylines < nboundary)
37263
37264 // ------------------------------------------------------------------
37265 // Before filling the data structures we need to identify the outer
37266 // closed boundary and the inner closed boundaries.
37267 // If the nodes are not in order we throw a warning message
37268
37269 // Index for the polygon that is currently considered as the outer
37270 // boundary
37271 unsigned index_outer = 0;
37272
37273 for (unsigned idx_outer = 0; idx_outer < npolygons; idx_outer++)
37274 {
37275 // Get the vertices of the outer boundary
37277
37278 // Flag to know if ALL the inner closed boundaries are inside the
37279 // outer closed boundary
37280 bool all_inner_inside = true;
37281
37282 // Number of polylines of the outer boundary
37283 const unsigned nouter_polylines = polygons_pt[idx_outer]->npolyline();
37284 for (unsigned p = 0; p < nouter_polylines; p++)
37285 {
37287 polygons_pt[idx_outer]->polyline_pt(p);
37288 const unsigned nvertex = tmp_poly_pt->nvertex();
37289 for (unsigned v = 0; v < nvertex; v++)
37290 {
37291 Vector<double> current_vertex = tmp_poly_pt->vertex_coordinate(v);
37293 } // for (v < nvertex)
37294 } // for (p < nouter_polylines)
37295
37296 // Now get the vertices for the inner boundaries
37297
37298 // First get the number of inner closed boundaries (polygons size
37299 // minus one because one of the polygons is considered to be the
37300 // outer closed boundary
37301 const unsigned ninner_polygons = polygons_pt.size() - 1;
37302
37303 // Store the vertices of the inner closed boundaries
37305 // Get all the vertices of the inner closed boundaries
37306 for (unsigned i = 0; i <= ninner_polygons; i++)
37307 {
37308 if (i != idx_outer)
37309 {
37310 // Number of polylines of the current internal closed boundary
37311 const unsigned ninner_polylines = polygons_pt[i]->npolyline();
37312 for (unsigned p = 0; p < ninner_polylines; p++)
37313 {
37315 const unsigned nvertex = tmp_poly_pt->nvertex();
37316 for (unsigned v = 0; v < nvertex; v++)
37317 {
37318 Vector<double> current_vertex = tmp_poly_pt->vertex_coordinate(v);
37319 if (i < idx_outer)
37320 {
37322 }
37323 else if (i > idx_outer)
37324 {
37326 }
37327 } // for (v < nvertex)
37328
37329 } // for (p < ninner_polylines)
37330
37331 } // if (i != index_outer)
37332
37333 } // for (i <= ninner_polygons)
37334
37335 // Now check that ALL the vertices of ALL the internal closed
37336 // boundaries are inside the outer closed boundary
37337 for (unsigned i = 0; i < ninner_polygons; i++)
37338 {
37339 // Get the number of vertices in the current internal closed
37340 // boundary
37341 const unsigned nvertex_internal = inner_vertex_coordinates[i].size();
37342 for (unsigned v = 0; v < nvertex_internal; v++)
37343 {
37344 // Get a vertex in the current internal closed boundary
37346 all_inner_inside &= this->is_point_inside_polygon_helper(
37348
37349 // Check if we should continue checking for more points inside
37350 // the current proposed outer boundary
37351 if (!all_inner_inside)
37352 {
37353 // Break the "for" for the vertices
37354 break;
37355 }
37356
37357 } // for (v < nvertex_internal)
37358
37359 // Check if we should continue checking for more inner closed
37360 // boundaries inside the current proposed outer boundary
37361 if (!all_inner_inside)
37362 {
37363 // Break the "for" for the inner boundaries
37364 break;
37365 }
37366
37367 } // for (i < ninner_polygons)
37368
37369 // Check if all the vertices of all the polygones are inside the
37370 // current proposed outer boundary
37371 if (all_inner_inside)
37372 {
37374 break;
37375 }
37376
37377 } // for (idx_outer < npolygons)
37378
37379#ifdef PARANOID
37380 // Check if the first nodes listed in the polyfiles correspond to
37381 // the outer boundary, if that is not the case then throw a warning
37382 // message
37383 if (index_outer != 0)
37384 {
37385 std::ostringstream warning_message;
37387 << "The first set of nodes listed in the input polyfiles does not\n"
37388 << "correspond to the outer closed boundary. This may lead to\n"
37389 << "problems at the adaptation stage if the holes coordinates\n"
37390 << "are no correctly associated to the inner closed boundaries.\n"
37391 << "You can check the generated mesh by calling the output() method\n"
37392 << "from the mesh object '(problem.mesh_pt()->output(string))'\n\n";
37396 } // if (index_outer != 0)
37397#endif
37398
37399 // ------------------------------------------------------------------
37400 // Now fill the data structures
37401
37402 // Store outer polygon
37403 // We are assuming there is only one outer polygon
37404 this->Outer_boundary_pt.resize(1);
37405 this->Outer_boundary_pt[0] = polygons_pt[index_outer];
37406
37407 this->Internal_polygon_pt.resize(npolygons - 1);
37408 for (unsigned i = 0; i < npolygons; i++)
37409 {
37410 if (i != index_outer)
37411 {
37412 if (i < index_outer)
37413 {
37414 // Store internal polygons by copy constructor
37415 this->Internal_polygon_pt[i] = polygons_pt[i];
37416 }
37417 else if (i > index_outer)
37418 {
37419 // Store internal polygons by copy constructor
37420 this->Internal_polygon_pt[i - 1] = polygons_pt[i];
37421 }
37422 } // if (i != index_outer)
37423 } // for (i < npolygons)
37424
37425 // Before assigning the hole vertex coordinate to the inner closed
37426 // boundaries check that the holes are listed in orderm if that is
37427 // not the case the associate each hole vertex coordinate to the
37428 // inner closed boundaries
37429
37430 // Store the vertices of the inner closed boundaries
37432 // Get all the vertices of the inner closed boundaries
37433 for (unsigned i = 0; i < npolygons - 1; i++)
37434 {
37435 // Number of polylines of the current internal closed boundary
37436 const unsigned ninner_polylines =
37437 this->Internal_polygon_pt[i]->npolyline();
37438 for (unsigned p = 0; p < ninner_polylines; p++)
37439 {
37441 this->Internal_polygon_pt[i]->polyline_pt(p);
37442 // Number of vertices of the current polyline in the current
37443 // internal closed polygon
37444 const unsigned nvertex = tmp_poly_pt->nvertex();
37445 for (unsigned v = 0; v < nvertex; v++)
37446 {
37447 Vector<double> current_vertex = tmp_poly_pt->vertex_coordinate(v);
37449 } // for (v < nvertex)
37450
37451 } // for (p < ninner_polylines)
37452
37453 } // for (i <= ninner_polygons)
37454
37455 // Holes information
37456 unsigned nholes;
37457 poly_file >> nholes;
37458
37459#ifdef PARANOID
37460 if (npolygons > 1 && (npolygons - 1) != nholes)
37461 {
37462 std::ostringstream error_message;
37463 error_message
37464 << "The number of holes (" << nholes << ") does not correspond "
37465 << "with the number\nof internal polygons (" << npolygons - 1 << ")\n\n"
37466 << "Using polyfiles as input does not currently allows the\n"
37467 << "definition of more than one outer polygon\n\n";
37468 throw OomphLibError(
37469 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
37470 }
37471#endif
37472
37473 // Storage for the holes
37475
37476 // Dummy for hole number
37477 unsigned dummy_hole;
37478 // Loop over the holes to get centre coords
37479 for (unsigned ihole = 0; ihole < nholes; ihole++)
37480 {
37481 hole_coordinates[ihole].resize(2);
37482 // Read the centre value
37486 }
37487
37488 // Vector that store the index of the hole coordinate that
37489 // correspond to each internal closed polygon
37491 std::map<unsigned, bool> hole_done;
37492
37493 // Now associate each hole vertex to a corresponding internal closed
37494 // polygon
37495 for (unsigned i = 0; i < npolygons - 1; i++)
37496 {
37497 // Find which hole is associated to each internal closed boundary
37498 for (unsigned h = 0; h < nholes; h++)
37499 {
37500 // If the hole has not been previously associated
37501 if (!hole_done[h])
37502 {
37503 // Get the hole coordinate
37505
37506 const bool hole_in_polygon = this->is_point_inside_polygon_helper(
37508
37509 // If the hole is inside the polygon
37510 if (hole_in_polygon)
37511 {
37512 // Mark the hole as done
37513 hole_done[h] = true;
37514 // Associate the current hole with the current inner closed
37515 // boundary
37517 // Break the search
37518 break;
37519 }
37520
37521 } // if (!hole_done[h])
37522
37523 } // for (h < nholes)
37524
37525 } // for (i < npolygons-1)
37526
37527#ifdef PARANOID
37528 if (hole_done.size() != npolygons - 1)
37529 {
37530 std::ostringstream error_message;
37531 error_message
37532 << "Not all the holes were associated to an internal closed boundary\n"
37533 << "Only (" << hole_done.size()
37534 << ") holes were assigned for a total of\n"
37535 << "(" << npolygons - 1 << ") internal closed boundaries.\n"
37536 << "You can check the generated mesh by calling the output() method\n"
37537 << "from the mesh object '(problem.mesh_pt()->output(string))'\n\n";
37538 throw OomphLibError(
37539 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
37540 } // if (index_hole != ihole)
37541#endif
37542
37543 // Assign the holes coordinates to the internal polygons
37544 for (unsigned ihole = 0; ihole < nholes; ihole++)
37545 {
37546 // Get the index hole of the current internal closed polygon
37548#ifdef PARANOID
37549 // Check if the hole index is the same as the internal closed
37550 // boundary, it means that the holes were listed in the same order
37551 // as the nodes of the internal closed boundaries
37552 if (index_hole != ihole)
37553 {
37554 std::ostringstream error_message;
37555 error_message
37556 << "The hole vertices coordinates are not listed in the same order\n"
37557 << "as the nodes that define the internal closed boundaries.\n"
37558 << "This may lead to problems in case that the holes coordinates\n"
37559 << "were no properly assigned to the internal closed boundaries.\n"
37560 << "You can check the generated mesh by calling the output() method\n"
37561 << "from the mesh object '(problem.mesh_pt()->output(string))'\n\n";
37562 throw OomphLibError(error_message.str(),
37565 } // if (index_hole != ihole)
37566#endif
37567
37568 // Set the hole coordinate for the internal polygon
37569 this->Internal_polygon_pt[ihole]->internal_point() =
37571 }
37572
37573 // Ignore the first line with structure description
37574 poly_file.ignore(80, '\n');
37575
37576 // Regions information
37577 unsigned nregions;
37578
37579 // Extract regions information
37580 // But first check if there are regions or not
37581 std::string regions_info_string;
37582
37583 // Read line up to termination sign
37585
37586 // Check if the read string is a number or a comment wrote by triangle,
37587 // if it is a number then that is the number of regions
37588 if (isdigit(regions_info_string.c_str()[0]))
37589 {
37590 nregions = std::atoi(regions_info_string.c_str());
37591 }
37592 else
37593 {
37594 nregions = 0;
37595 }
37596
37597 // The regions coordinates
37598 std::map<unsigned, Vector<double>> regions_coordinates;
37599
37600 // Dummy for regions number
37601 unsigned dummy_region;
37602
37603 unsigned region_id;
37604
37605 // Loop over the regions to get their coords
37606 for (unsigned iregion = 0; iregion < nregions; iregion++)
37607 {
37609 // Read the regions coordinates
37614 regions_coordinates[region_id].resize(2);
37615 regions_coordinates[region_id][0] = tmp_region_coordinates[0];
37616 regions_coordinates[region_id][1] = tmp_region_coordinates[1];
37617
37618 // Ignore the first line with structure description
37619 poly_file.ignore(80, '\n');
37620
37621 // Verify if not using the default region number (zero)
37622 if (region_id == 0)
37623 {
37624 std::ostringstream error_message;
37625 error_message
37626 << "Please use another region id different from zero.\n"
37627 << "It is internally used as the default region number.\n";
37628 throw OomphLibError(error_message.str(),
37631 }
37632 }
37633
37634 // Store the extra regions coordinates
37635 this->Regions_coordinates = regions_coordinates;
37636
37637 poly_file.close();
37638 }
37639
37640 //======================================================================
37641 /// Updates the polygon but using the elements area instead of
37642 /// the default refinement and unrefinement methods
37643 //======================================================================
37644 template<class ELEMENT>
37647 {
37648 // Verify that there was a change on the polygon representation
37649 unsigned update_was_performed = false;
37650
37651 const unsigned nele = this->nelement();
37652
37653 // - Get the vertices along the boundaries and for each element identify
37654 // its associated target error.
37655 // - Get face mesh representation of each polyline.
37656 // - Get the vertices with the help of face elements.
37657 // - Find the global index in the mesh of the face element and use
37658 // it to get its associated target area
37659
37660 // Get the face mesh representation
37662 get_face_mesh_representation(polygon_pt, face_mesh_pt);
37663
37664 // Create vertices of the polylines by using the vertices of the
37665 // FaceElements
37666 Vector<double> vertex_coord(3); // zeta,x,y
37669
37670 unsigned n_polyline = polygon_pt->npolyline();
37671
37672 // Go for each polyline
37673 for (unsigned p = 0; p < n_polyline; p++)
37674 {
37675 // Get the MeshAsGeomObject representation just once per polyline,
37676 // this object is only used by the
37677 // refine_boundary_constrained_by_target_area() method. We get it
37678 // here to ensure that all processors (in a distributed context)
37679 // get this representation just once, and because an AllToAll MPI
37680 // communication is used in this calling
37683
37684 // Set of coordinates on the boundary
37685 // Set entries are ordered on first entry in vector which stores
37686 // the boundary coordinate so the vertices come out in order!
37687 std::set<Vector<double>> vertex_nodes;
37688
37689 // Vector to store the vertices, transfer the sorted vertices from the
37690 // set to this vector, --- including the z-value ---
37692
37693 // Vector to store the coordinates of the polylines, same as the
37694 // tmp_vector_vertex_node vector (after adding more nodes) but
37695 // --- without the z-value ---, used to re-generate the polylines
37697
37698#ifdef OOMPH_HAS_MPI
37699 // --------- Stuff to deal with splitted boundaries ---------- Begin -----
37700 // Set of coordinates that are on the boundary (splitted boundary version)
37701 // The first vector is used to allocate the points for each sub-boundary
37702 // Set entries are ordered on first entry in vector which stores
37703 // the boundary coordinate so the vertices come out in order!
37705
37706 // Vector to store the vertices, transfer the sorted vertices from the
37707 // set (sub_vertex_nodes) to this vector, --- including the z-value ---
37709
37710 // Vector to store the coordinates of the polylines that will represent
37711 // the splitted boundary. Used to pass the info. from sub_vertex_nodes
37712 // but --- without the z-value ---, used to generate the sub-polylines
37714 // --------- Stuff to deal with splitted boundaries ----------- End
37715 // ------
37716#endif
37717
37718 // Get the boundary id
37719 const unsigned bound = polygon_pt->curve_section_pt(p)->boundary_id();
37720
37721 // Get the chunk number
37722 const unsigned chunk = polygon_pt->curve_section_pt(p)->boundary_chunk();
37723
37724 /// Use a vector of vector for vertices and target areas to deal
37725 /// with the cases when the boundaries are split by the
37726 /// distribution process
37727
37728 // Loop over the face elements (ordered) and add their vertices
37729 const unsigned nface_element = face_mesh_pt[p]->nelement();
37730
37731 // Store the non halo face elements, the ones from which we will
37732 // get the vertices
37734
37735 // Map to store the index of the face element on a boundary
37736 std::map<FiniteElement*, unsigned> face_element_index_on_boundary;
37737
37738 for (unsigned ef = 0; ef < nface_element; ++ef)
37739 {
37740 FiniteElement* ele_face_pt = face_mesh_pt[p]->finite_element_pt(ef);
37741#ifdef OOMPH_HAS_MPI
37742 // Skip the halo elements if working with a distributed mesh
37743 if (this->is_mesh_distributed() && ele_face_pt->is_halo())
37744 {
37745 continue;
37746 }
37747#endif
37748 // Add the face element to the vector
37751 }
37752
37753 // Get the number of non halo face element
37755
37756 // Map to know the already sorted face elements
37757 std::map<FiniteElement*, bool> face_element_done;
37758
37759 // Number of done face elements
37760 unsigned nsorted_face_elements = 0;
37761
37762#ifdef OOMPH_HAS_MPI
37763 // Counter for sub_boundaries
37764 unsigned nsub_boundaries = 0;
37765#endif // #ifdef OOMPH_HAS_MPI
37766
37767 // Continue until all the face elements have been sorted
37768 // While to deal with split boundaries cases
37770 {
37771 // Get and initial face element
37773#ifdef PARANOID
37774 bool found_initial_face_element = false;
37775#endif
37776
37777 unsigned iface = 0;
37778 for (iface = 0; iface < nnon_halo_face_element; iface++)
37779 {
37781 // If not done then take it as initial face element
37783 {
37784#ifdef PARANOID
37786#endif
37788 iface++;
37789 break;
37790 }
37791 }
37792
37793#ifdef PARANOID
37795 {
37796 std::ostringstream error_message;
37797 error_message << "Could not find an initial face element for the "
37798 "current segment\n";
37799 // << "----- Possible memory leak -----\n";
37800 throw OomphLibError(
37801 error_message.str(),
37802 "RefineableTriangleMesh::update_polygon_using_elements_area()",
37804 }
37805#endif
37806
37807 // Local set of coordinates that are on the boundary
37808 // Set entries are ordered on first entry in vector which stores
37809 // the boundary coordinate so the vertices come out in order!
37810 std::set<Vector<double>> local_vertex_nodes;
37811
37812 // Vector to store the vertices, transfer the sorted vertices from the
37813 // set (local) to this vector (local), --- including the z-value ---
37815
37816 // Vector to store the target areas, uses the same approach as the
37817 // set for the local_vertex_nodes, ordered on first entry
37818 std::set<Vector<double>> sorted_target_areas;
37819
37820 // Vector to store the target areas, used to transfer the sorted target
37821 // areas from "local_sorted_target_areas" set
37823
37824 // -----------------------------------------------------------------
37825 // Add the vertices of the initial face element to the set of
37826 // local sorted vertices
37827 // -----------------------------------------------------------------
37828 unsigned nnode = ele_face_pt->nnode();
37829 // Add the left-hand node to the set:
37830 // Boundary coordinate
37832 vertex_coord[0] = bound_left[0];
37833
37834 // Actual coordinates
37835 for (unsigned i = 0; i < 2; i++)
37836 {
37837 vertex_coord[i + 1] = ele_face_pt->node_pt(0)->x(i);
37838 }
37840
37841 // Add the right-hand nodes to the set:
37842 // Boundary coordinate
37845 vertex_coord[0] = bound_right[0];
37846
37847 // Actual coordinates
37848 for (unsigned i = 0; i < 2; i++)
37849 {
37850 vertex_coord[i + 1] = ele_face_pt->node_pt(nnode - 1)->x(i);
37851 }
37853
37854 // The initial and final node on the set
37857
37858 // Mark the current face element as done
37860
37861 // -------------------------------------------------------
37862 // Find the global index in the mesh of the face element
37863 // and use it to get its associated target area
37864 // -------------------------------------------------------
37865 // Container to store the zeta value (used as index) and
37866 // the associated target area of the element
37868
37869 // Use the minimum zeta value to sort the target areas
37870 // along the boundary
37871 zeta_target_area_values[0] = std::min(bound_left[0], bound_right[0]);
37872
37873 // Get the index of the face element on the current boundary
37875 // Get the "ef"-th element on the boundary
37876 FiniteElement* el_pt = this->boundary_element_pt(bound, ef);
37877
37878#ifdef PARANOID
37879 bool found_global_element_index = false;
37880#endif
37881 for (unsigned eg = 0; eg < nele; eg++)
37882 {
37883 // Get the "eg-th" element
37884 FiniteElement* el_compare_pt = this->finite_element_pt(eg);
37885
37886 // Compare with the element on the boundary, if equal then
37887 // store the target area
37888 if (el_pt == el_compare_pt)
37889 {
37891#ifdef PARANOID
37893#endif
37894 break; // break the for (e < nele) global element
37895 } // if element_pt == element_compare_pt
37896 } // for nele (on complete mesh)
37897
37898#ifdef PARANOID
37900 {
37901 std::ostringstream error_message;
37902 error_message << "The global index for the (" << ef
37903 << ")-th face element "
37904 << "on\nthe (" << bound
37905 << ")-th boundary was not found!!!";
37906 throw OomphLibError(
37907 error_message.str(),
37908 "RefineableTriangleMesh::update_polygon_using_elements_area()",
37910 }
37911#endif
37912
37913 // Add the target areas to the sorted set
37915 // ------------------------------------------------------------------
37916
37917 // Continue iterating if a new face element has been added to the
37918 // list
37919 bool face_element_added = false;
37920
37921 // While a new face element has been added to the set of sorted
37922 // face elements then re-iterate
37923 do
37924 {
37925 // Start from the next face elements since we have already
37926 // added the previous one as the initial face element (any
37927 // previous face element had to be added on previous
37928 // iterations)
37929 for (unsigned iiface = iface; iiface < nnon_halo_face_element;
37930 iiface++)
37931 {
37932 face_element_added = false;
37935 {
37936 // Get each individual node to check if they are contiguous
37937 nnode = ele_face_pt->nnode();
37940
37942 {
37944 face_element_added = true;
37945 }
37946 else if (left_node_pt == last_node_pt)
37947 {
37949 face_element_added = true;
37950 }
37951 else if (right_node_pt == first_node_pt)
37952 {
37954 face_element_added = true;
37955 }
37956 else if (right_node_pt == last_node_pt)
37957 {
37959 face_element_added = true;
37960 }
37961
37963 {
37964 // Add the left-hand node to the set:
37965 // Boundary coordinate
37966 left_node_pt->get_coordinates_on_boundary(bound, bound_left);
37967 vertex_coord[0] = bound_left[0];
37968
37969 // Actual coordinates
37970 for (unsigned i = 0; i < 2; i++)
37971 {
37972 vertex_coord[i + 1] = left_node_pt->x(i);
37973 }
37975
37976 // Add the right-hand nodes to the set:
37977 // Boundary coordinate
37978 right_node_pt->get_coordinates_on_boundary(bound, bound_right);
37979 vertex_coord[0] = bound_right[0];
37980
37981 // Actual coordinates
37982 for (unsigned i = 0; i < 2; i++)
37983 {
37984 vertex_coord[i + 1] = right_node_pt->x(i);
37985 }
37987
37988 // Mark as done only if one of its nodes has been
37989 // added to the list
37992
37993 // -----------------------------------------------------
37994 // Find the global index in the mesh of the face element
37995 // and use it to get its associated target area
37996 // -----------------------------------------------------
37997 // Use the minimum zeta value to sort the target areas
37998 // along the boundary
38000 std::min(bound_left[0], bound_right[0]);
38001
38002 // Get the "ef"-th element on the boundary
38004 FiniteElement* lel_pt = this->boundary_element_pt(bound, ef);
38005
38006#ifdef PARANOID
38008#endif
38009 for (unsigned eg = 0; eg < nele; eg++)
38010 {
38011 // Get the "eg-th" element
38012 FiniteElement* lel_compare_pt = this->finite_element_pt(eg);
38013
38014 // Compare with the element on the boundary, if equal then
38015 // store the target area
38016 if (lel_pt == lel_compare_pt)
38017 {
38019#ifdef PARANOID
38021#endif
38022 break; // break the for (e < nele) global element
38023 } // if element_pt == element_compare_pt
38024 } // for nele (on complete mesh)
38025
38026#ifdef PARANOID
38028 {
38029 std::ostringstream error_message;
38030 error_message << "The global index for the (" << ef
38031 << ")-th face element "
38032 << "on\nthe (" << bound
38033 << ")-th boundary was not found!!!";
38034 throw OomphLibError(error_message.str(),
38035 "RefineableTriangleMesh::update_polygon_"
38036 "using_elements_area()",
38038 }
38039#endif
38040
38041 // Add the target areas to the sorted set
38043
38044 break;
38045 }
38046
38047 } // if (!edge_done[edge])
38048 } // for (iiedge < nedges)
38049 } while (face_element_added &&
38051
38052 // -----------------------------------------------------------------
38053 // At this point we already have a sorted set of nodes and
38054 // can be used to peform the unrefinement and refinement procedures
38055 // -----------------------------------------------------------------
38056
38057 // Get the number of nodes on the list
38058 const unsigned nlocal_nodes = local_vertex_nodes.size();
38059 // Change representation to vector for easy of handling ...
38061
38062 // Copy the vertices of the nodes
38063 unsigned counter = 0;
38064 std::set<Vector<double>>::iterator it_vertex;
38065 for (it_vertex = local_vertex_nodes.begin();
38067 it_vertex++)
38068 {
38070 local_tmp_vector_vertex_node[counter][0] = (*it_vertex)[0];
38071 local_tmp_vector_vertex_node[counter][1] = (*it_vertex)[1];
38072 local_tmp_vector_vertex_node[counter][2] = (*it_vertex)[2];
38073 counter++;
38074 }
38075
38076 // ... same for the info. related with the target areas (turn
38077 // into vector)
38078 const unsigned ntarget_areas = sorted_target_areas.size();
38080 counter = 0;
38081 std::set<Vector<double>>::iterator it_area;
38082 for (it_area = sorted_target_areas.begin();
38084 ++it_area)
38085 {
38086 tmp_sorted_target_areas[counter] = (*it_area)[1];
38087 ++counter;
38088 }
38089
38090#ifdef PARANOID
38091 if (nlocal_nodes > 0 && (ntarget_areas != nlocal_nodes - 1))
38092 {
38093 std::ostringstream error_message;
38094 error_message
38095 << "The boundary (" << bound << ") was split during the "
38096 << "distribution process.\n"
38097 << "The problem is in the association of the target areas with "
38098 "the\n"
38099 << "elements that gave rise to the vertex coordinates.\n"
38100 << "The number of local nodes (" << nlocal_nodes
38101 << "), on the 'sub-polyline', is not\n"
38102 << "according with the number of target "
38103 << "areas (" << ntarget_areas << ")\nfor that number of nodes.\n"
38104 << "The target areas number MUST be equal to the number of\n"
38105 << "local nodes minus one\n\n";
38106 throw OomphLibError(error_message.str(),
38109 }
38110#endif
38111
38112 // -------------------------------------------------------------------
38113 // Update the vertices along the boundary using the target area
38114 // to define the distance among them
38115 // -------------------------------------------------------------------
38116
38117 // Tolerance below which the middle point can be deleted
38118 // (ratio of deflection to element length)
38119 double unrefinement_tolerance =
38120 polygon_pt->polyline_pt(p)->unrefinement_tolerance();
38121
38122 // Apply unrefinement
38124 unrefine_boundary_constrained_by_target_area(
38125 bound,
38126 chunk,
38128 unrefinement_tolerance,
38130
38131 // Tolerance for refinement
38132 double refinement_tolerance =
38133 polygon_pt->polyline_pt(p)->refinement_tolerance();
38134
38135 // Apply refinement
38136 bool refinement_applied = refine_boundary_constrained_by_target_area(
38139 refinement_tolerance,
38141
38142 // Clear the local containter to recover the nodes ordered using the
38143 // zeta value
38144 local_vertex_nodes.clear();
38145
38146 // At the end of each unrefinement/refinement step store the new nodes
38147 // on the set that will give rise to the vertices of the new polyline
38148 // representation
38150 for (unsigned i = 0; i < nnew_nodes; i++)
38151 {
38155 vertex_nodes.insert(vertex_coord); // Global container
38157 }
38158
38159 // Update the flag to indicate whether an unrefinement or
38160 // refinement was applied
38162
38163#ifdef OOMPH_HAS_MPI
38164 if (this->is_mesh_distributed())
38165 {
38166 // Add the set of vertices for the boundary, this will help to
38167 // detect if we need to deal with sub-boundaries
38169 // Increase the counter for sub-boundaries
38171 }
38172#endif
38173
38174 } // while(nsorted_face_elements < nnon_halo_face_element)
38175
38176 // Now turn into vector for ease of handling...
38177 unsigned npoly_vertex = vertex_nodes.size();
38178 // This will store all the vertices whether the boundary was split
38179 // or not
38181 unsigned count = 0;
38182 for (std::set<Vector<double>>::iterator it = vertex_nodes.begin();
38183 it != vertex_nodes.end();
38184 ++it)
38185 {
38186 tmp_vector_vertex_node[count].resize(3);
38187 tmp_vector_vertex_node[count][0] = (*it)[0];
38188 tmp_vector_vertex_node[count][1] = (*it)[1];
38189 tmp_vector_vertex_node[count][2] = (*it)[2];
38190 ++count;
38191 }
38192
38193#ifdef OOMPH_HAS_MPI
38194 // --------- Stuff for the sub_boundaries ----- Begin section
38195 // ---------
38196#ifdef PARANOID
38199 {
38200 std::ostringstream error_message;
38201 error_message
38202 << "The number of found sub-boundaries and the number of counted\n"
38203 << "sub-boundaries are different:\n"
38204 << "Number of found sub-boundaries: (" << nsub_boundaries_set << ")\n"
38205 << "Number of counted sub-boundaries: (" << nsub_boundaries << ")\n";
38206 throw OomphLibError(error_message.str(),
38209 }
38210#endif
38211
38212 // Are there sub-boundaries (only appear in distributed meshes)
38213 if (this->is_mesh_distributed() && nsub_boundaries > 1)
38214 {
38215 // Mark the boundary as been splitted in the partition process
38216 this->Boundary_was_splitted[bound] = true;
38217 // Resize the vector to store the info. of sub-boundaries
38219 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
38220 {
38221 // Turn info. into vector for ease of handling...
38222 const unsigned nsubpoly_vertex = sub_vertex_nodes[isub].size();
38224 unsigned subcount = 0;
38225 std::set<Vector<double>>::iterator subit;
38226 for (subit = sub_vertex_nodes[isub].begin();
38227 subit != sub_vertex_nodes[isub].end();
38228 ++subit)
38229 {
38231 sub_tmp_vector_vertex_node[isub][subcount][0] = (*subit)[0];
38232 sub_tmp_vector_vertex_node[isub][subcount][1] = (*subit)[1];
38233 sub_tmp_vector_vertex_node[isub][subcount][2] = (*subit)[2];
38234 ++subcount;
38235 }
38236 }
38237 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
38238 // --------- Stuff for the sub_boundaries ----- End section
38239 // ------------
38240#endif // OOMPH_HAS_MPI
38241
38242 // For further processing the three-dimensional vector has to be
38243 // reduced to a two-dimensional vector
38245
38246 // Resize the vector for vectices
38248 for (unsigned i = 0; i < n_vertex; i++)
38249 {
38250 vector_vertex_node[i].resize(2);
38253 }
38254
38255#ifdef OOMPH_HAS_MPI
38256 // --------- Stuff for the sub_boundaries ----- Begin section ----------
38257 // Verify if need to deal with sub_boundaries
38258 if (this->is_mesh_distributed() && nsub_boundaries > 1)
38259 {
38260 // For further processing the three-dimensional vector
38261 // has to be reduced to a two-dimensional vector
38262 // Resize the vector to store the info. of sub-boundaries
38264 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
38265 {
38266 const unsigned subn_vertex = sub_tmp_vector_vertex_node[isub].size();
38267 // Resize the vector for vectices
38269 for (unsigned i = 0; i < subn_vertex; i++)
38270 {
38271 sub_vector_vertex_node[isub][i].resize(2);
38276 }
38277 }
38278 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
38279
38280 // We already have the info. for the sub-boundaries (if necessary)
38281 // and then we can create the sub-boundaries representations to
38282 // ease the generation of the mesh by Triangle
38283
38284 // --------- Stuff for the sub_boundaries ----- End section
38285 // ------------
38286#endif // OOMPH_HAS_MPI
38287
38288 // --------------------------------------------------------------------
38289 // Check for contiguousness
38290 // --------------------------------------------------------------------
38291#ifdef OOMPH_HAS_MPI
38292 // Only perform this checking if the mesh is not distributed. When
38293 // the mesh is distributed the polylines continuity is addressed
38294 // by the sort_polylines_helper() method
38295 if (!this->is_mesh_distributed())
38296#endif
38297 {
38298 if (p > 0)
38299 {
38300 // Final end point of previous line
38302 unsigned n_prev_vertex =
38303 polygon_pt->curve_section_pt(p - 1)->nvertex();
38305 polygon_pt->polyline_pt(p - 1)->vertex_coordinate(n_prev_vertex -
38306 1);
38307
38308 unsigned prev_seg_boundary_id =
38309 polygon_pt->curve_section_pt(p - 1)->boundary_id();
38310
38311 // Find the error between the final vertex of the previous
38312 // line and the first vertex of the current line
38313 double error = 0.0;
38314 for (unsigned i = 0; i < 2; i++)
38315 {
38316 const double dist = final_vertex_of_previous_segment[i] -
38317 (*vector_vertex_node.begin())[i];
38318 error += dist * dist;
38319 }
38320 error = sqrt(error);
38321
38322 // If the error is bigger than the tolerance then
38323 // we probably need to reverse, but better check
38325 {
38326 // Find the error between the final vertex of the previous
38327 // line and the last vertex of the current line
38328 double rev_error = 0.0;
38329 for (unsigned i = 0; i < 2; i++)
38330 {
38331 const double dist = final_vertex_of_previous_segment[i] -
38332 (*--vector_vertex_node.end())[i];
38333 rev_error += dist * dist;
38334 }
38336
38337 if (rev_error >
38339 {
38340 // It could be possible that the first segment be reversed
38341 // and we did not notice it because this check does not
38342 // apply for the first segment. We can verify if the first
38343 // segment is reversed by using the vertex number 1
38344 if (p == 1)
38345 {
38346 // Initial end point of previous line
38348
38350 polygon_pt->polyline_pt(p - 1)->vertex_coordinate(0);
38351
38352 unsigned prev_seg_boundary_id =
38353 polygon_pt->curve_section_pt(p - 1)->boundary_id();
38354
38355 // Find the error between the initial vertex of the previous
38356 // line and the first vertex of the current line
38357 double error = 0.0;
38358 for (unsigned i = 0; i < 2; i++)
38359 {
38360 const double dist = initial_vertex_of_previous_segment[i] -
38361 (*vector_vertex_node.begin())[i];
38362 error += dist * dist;
38363 }
38364 error = sqrt(error); // Reversed only the previous one
38365
38366 // If the error is bigger than the tolerance then
38367 // we probably need to reverse, but better check
38368 if (error >
38370 {
38371 // Find the error between the final vertex of the previous
38372 // line and the last vertex of the current line
38373 double rev_error = 0.0;
38374 for (unsigned i = 0; i < 2; i++)
38375 {
38376 const double dist = initial_vertex_of_previous_segment[i] -
38377 (*--vector_vertex_node.end())[i];
38378 rev_error += dist * dist;
38379 }
38380 rev_error =
38381 sqrt(rev_error); // Reversed both the current one and
38382 // the previous one
38383
38384 if (rev_error >
38386 {
38387 std::ostringstream error_stream;
38389 << "The distance between the first node of the current\n"
38390 << "line segment (boundary " << bound
38391 << ") and either end of "
38392 << "the previous line segment\n"
38393 << "(boundary " << prev_seg_boundary_id
38394 << ") is bigger than "
38395 << "the desired tolerance "
38397 << ".\n"
38398 << "This suggests that the polylines defining the "
38399 << "polygonal\n"
38400 << "representation are not properly ordered.\n"
38401 << "Fail on last vertex of polyline: ("
38402 << prev_seg_boundary_id << ") and\n"
38403 << "first vertex of polyline (" << bound << ").\n"
38404 << "This should have failed when first trying to "
38405 << "construct the\npolygon.\n";
38406 throw OomphLibError(error_stream.str(),
38409 }
38410 else
38411 {
38412 // Reverse both
38413 // Reverse the current vector to line up with the
38414 // previous one
38415 std::reverse(vector_vertex_node.begin(),
38416 vector_vertex_node.end());
38417
38418 polygon_pt->polyline_pt(p - 1)->reverse();
38419 }
38420 }
38421 else
38422 {
38423 // Reverse the previous one
38424 polygon_pt->polyline_pt(p - 1)->reverse();
38425 }
38426
38427 } // if p == 1
38428 else
38429 {
38430 std::ostringstream error_stream;
38432 << "The distance between the first node of the current\n"
38433 << "line segment (boundary " << bound
38434 << ") and either end of "
38435 << "the previous line segment\n"
38436 << "(boundary " << prev_seg_boundary_id
38437 << ") is bigger than the "
38438 << "desired tolerance "
38440 << ".\n"
38441 << "This suggests that the polylines defining the polygonal\n"
38442 << "representation are not properly ordered.\n"
38443 << "Fail on last vertex of polyline: ("
38444 << prev_seg_boundary_id << ") and\nfirst vertex of polyline ("
38445 << bound << ").\n"
38446 << "This should have failed when first trying to construct"
38447 << " the polygon.\n";
38448 throw OomphLibError(error_stream.str(),
38451 }
38452 }
38453 else
38454 {
38455 // Reverse the current vector to line up with the previous one
38456 std::reverse(vector_vertex_node.begin(),
38457 vector_vertex_node.end());
38458 }
38459 } // error
38460
38461 } // if ( p > 0 )
38462
38463 } // if (!this->is_mesh_distributed())
38464
38465 // --------------------------------------------------------------------
38466 // Update the polylines representation
38467 // --------------------------------------------------------------------
38468
38469 // Always update the polylines representation, in a distributed
38470 // mesh it is necessary to update the polyline representation since
38471 // it may no longer have vertices (the boundary may not be part of
38472 // the domain in the current processor)
38473
38474 // The new nunber of vertices
38476
38477 // Now update the polyline according to the new vertices
38480
38481 // Create a temporal "curve section" version of the recently
38482 // created polyline
38484
38485 // Tolerance below which the middle point can be deleted (ratio of
38486 // deflection to element length)
38487 double unrefinement_tolerance =
38488 polygon_pt->polyline_pt(p)->unrefinement_tolerance();
38489
38490 // Tolerance to add points
38491 double refinement_tolerance =
38492 polygon_pt->polyline_pt(p)->refinement_tolerance();
38493
38494 // Establish refinement and unrefinement tolerance
38495 tmp_polyline_pt->set_unrefinement_tolerance(unrefinement_tolerance);
38496 tmp_polyline_pt->set_refinement_tolerance(refinement_tolerance);
38497
38498 // Establish the maximum length constraint
38499 double maximum_length = polygon_pt->polyline_pt(p)->maximum_length();
38500 tmp_polyline_pt->set_maximum_length(maximum_length);
38501
38502#ifdef OOMPH_HAS_MPI
38503 // If the mesh is distributed check that the polyline still has
38504 // vertices
38505 if (this->is_mesh_distributed())
38506 {
38507 if (n_vertex >= 2)
38508 {
38509 // Pass the connection information from the old polyline to the
38510 // new one
38511 this->copy_connection_information(polygon_pt->polyline_pt(p),
38513 } // if (n_vertex >= 2)
38514 } // if (this->is_mesh_distributed())
38515 else
38516#endif
38517 {
38518 // Pass the connection information from the old polyline to the
38519 // new one
38520 this->copy_connection_information(polygon_pt->polyline_pt(p),
38522 }
38523
38524 // Now update the polyline according to the new vertices but first
38525 // check if the object is allowed to delete the representation or
38526 // if it should be done by other object
38527 bool delete_it_on_destructor = false;
38528
38529 std::set<TriangleMeshCurveSection*>::iterator it =
38530 this->Free_curve_section_pt.find(polygon_pt->curve_section_pt(p));
38531
38532 if (it != this->Free_curve_section_pt.end())
38533 {
38534 this->Free_curve_section_pt.erase(it);
38535 delete polygon_pt->curve_section_pt(p);
38537 }
38538
38539 // -------------------------------------------------------
38540 // Copying the new representation
38541 polygon_pt->curve_section_pt(p) = tmp_polyline_pt;
38542
38543 // Update the Boundary - Polyline map
38544 this->Boundary_curve_section_pt[bound] = polygon_pt->curve_section_pt(p);
38545
38547 {
38548 this->Free_curve_section_pt.insert(polygon_pt->curve_section_pt(p));
38549 }
38550
38551#ifdef OOMPH_HAS_MPI
38552 // --------- Stuff for the sub_boundaries ----- Begin section --------
38553 // Verify if need to deal with sub_boundaries
38554 if (this->is_mesh_distributed() && nsub_boundaries > 1)
38555 {
38556 // Create temporary representations for the boundaries, only to
38557 // create the mesh when calling Triangle
38558
38559 // Clear all previous stored data
38560 this->Boundary_subpolylines[bound].clear();
38561
38562 // Create storage for the sub-boundaries
38563 this->Boundary_subpolylines[bound].resize(nsub_boundaries);
38564 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
38565 {
38566 // Update the polyline according to the sub set of vertices,
38569
38570 // Add the sub-polyline to the container to represent the
38571 // boundary in parts
38572 this->Boundary_subpolylines[bound][isub] = sub_tmp_polyline_pt;
38573
38574 // No need to send the unrefinement/refinement and maximum
38575 // length constraints since these are only temporary
38576 // representations. These polylines can be deleted once the new
38577 // polygons that represent the distributed domain have been
38578 // created
38579
38580 } // for (isub < nsub_boundaries)
38581
38582 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
38583 // --------- Stuff for the sub_boundaries ----- End section ---------
38584#endif // OOMPH_HAS_MPI
38585
38586 // Delete the allocated memory for the geometric object that
38587 // represents the boundary
38588 delete mesh_geom_obj_pt;
38589
38590 } // for (p < n_polyline)
38591
38592 // Cleanup the face mesh
38593 for (unsigned p = 0; p < n_polyline; p++)
38594 {
38595 face_mesh_pt[p]->flush_node_storage();
38596 delete face_mesh_pt[p];
38597 }
38598
38599 return update_was_performed;
38600 }
38601
38602 //======================================================================
38603 /// Updates the open curve but using the elements area instead
38604 /// of the default refinement and unrefinement methods
38605 //======================================================================
38606 template<class ELEMENT>
38609 {
38610 // Verify if there was a change on the open curve representation
38611 unsigned update_was_performed = false;
38612
38613 const unsigned nele = this->nelement();
38614
38615 // - Get the vertices along the boundaries and for each element identify
38616 // its associated target error.
38617 // - Get face mesh representation of each polyline.
38618 // - Get the vertices with the help of face elements.
38619 // - Find the global index in the mesh of the face element
38620 // and use it to get its associated target area.
38621
38622 // Get the face mesh representation
38624 get_face_mesh_representation(open_curve_pt, face_mesh_pt);
38625
38626 // Create vertices of the polylines by using the vertices of the
38627 // FaceElements
38628 Vector<double> vertex_coord(3); // zeta,x,y
38631
38632 const unsigned ncurve_section = open_curve_pt->ncurve_section();
38633
38634 // Go for each curve section
38635 for (unsigned cs = 0; cs < ncurve_section; cs++)
38636 {
38637 // Get the MeshAsGeomObject representation just once per polyline,
38638 // this object is only used by the
38639 // refine_boundary_constrained_by_target_area() method. We get it
38640 // here to ensure that all processors (in a distributed context)
38641 // get this representation just once, and because an AllToAll MPI
38642 // communication is used in this calling
38645
38646 // Get the boundary id
38647 const unsigned bound = open_curve_pt->curve_section_pt(cs)->boundary_id();
38648
38649 // Get the chunk number
38650 const unsigned chunk =
38651 open_curve_pt->curve_section_pt(cs)->boundary_chunk();
38652
38653 /// Use a vector of vector for vertices and target areas to deal
38654 /// with the cases when the boundaries are split by the
38655 /// distribution process. Internal boundaries may be completely or
38656 /// partially overlapped by shared boundaries
38657
38658 // Loop over the face elements and add their vertices (they are
38659 // automatically sorted because of the set)
38660 const unsigned nface_element = face_mesh_pt[cs]->nelement();
38661
38662 // Store the non halo elements and the element at the other side of
38663 // the boundary (whatever it be halo or not), the first will be the
38664 // ones from which we will get the vertices (in even position)
38666
38667 // Map to store the index of the face element on a boundary
38668 std::map<FiniteElement*, unsigned> face_element_index_on_boundary;
38669
38670 // Map to know the already sorted face elements
38671 std::map<FiniteElement*, bool> face_element_done;
38672
38673 for (unsigned ef = 0; ef < nface_element; ++ef)
38674 {
38675 FiniteElement* ele_face_pt = face_mesh_pt[cs]->finite_element_pt(ef);
38676
38677 // Skip the halo elements (not used as base elements, only
38678 // include those elements whose element at the other side of the
38679 // boundary is non halo)
38680#ifdef OOMPH_HAS_MPI
38681 if (this->is_mesh_distributed())
38682 {
38683 // Only work with non-halo elements
38684 if (ele_face_pt->is_halo())
38685 {
38686 continue;
38687 }
38688 }
38689#endif
38690
38691 // Check if not already done
38693 {
38694 // Add the element and look for the element at the other side
38695 // of the boundary to add it immediately after the new added
38696 // element
38698 // Create the map of the face element with the index
38700 // Mark the current element as done
38702 // Get the number of nodes
38703 const unsigned nnodes = ele_face_pt->nnode();
38704 // Get the left and right node to look for the elements at the
38705 // other side of the boundary
38708#ifdef PARANOID
38709 // Flag to know if the element at the other side of the
38710 // boundary was found
38711 bool found_other_side_face_ele = false;
38712#endif
38713 for (unsigned iface = 0; iface < nface_element; iface++)
38714 {
38715 // Get the candidate face element
38717 face_mesh_pt[cs]->finite_element_pt(iface);
38718 // Check if not already done
38720 {
38723 // Check if the nodes are the same
38724 if ((left_node_pt == cleft_node_pt &&
38728 {
38729 // Add the element to the storage
38731 // ... and mark the element as done
38733 // Create the map of the face element with the index
38735#ifdef PARANOID
38736 // Set the flag of found other side face element
38738#endif
38739 break;
38740 }
38741 }
38742 } // (iface < nface_element)
38743
38744#ifdef PARANOID
38746 {
38747 std::ostringstream error_message;
38748 error_message
38749 << "The face element at the other side of the boundary (" << bound
38750 << ") was not found!!\n"
38751 << "These are the nodes of the face element:\n"
38752 << "(" << left_node_pt->x(0) << ", " << left_node_pt->x(1) << ") "
38753 << "and (" << right_node_pt->x(0) << "," << right_node_pt->x(1)
38754 << ")\n\n";
38755 throw OomphLibError(
38756 error_message.str(),
38757 "RefineableTriangleMesh::update_open_curve_using_elements_area()",
38759 }
38760#endif
38761 } // if (!face_ele_done[ele_face_pt])
38762
38763 } // (ef < nface_element)
38764
38765 // Clear the map of the already done face elements
38766 // This will be used to help sorting the face elements
38767 face_element_done.clear();
38768
38769 // Set of coordinates that are on the boundary
38770 // The entries are sorted on first entry in vector which stores
38771 // the boundary coordinate so the vertices come out in order!
38772 std::set<Vector<double>> vertex_nodes;
38773
38774 // Vector to store the vertices, transfer the sorted vertices from the
38775 // set to this vector, --- including the z-value ---
38777
38778 // Vector to store the coordinates of the polylines, same as the
38779 // tmp_vector_vertex_node vector (after adding more nodes) but
38780 // --- without the z-value ---, used to re-generate the polylines
38782
38783#ifdef OOMPH_HAS_MPI
38784 // Indicates if the set of vertices give rise to a internal
38785 // boundary that will be used as shared boundary or as normal
38786 // internal boundary -- Only used to deal with internal boundaries
38787 // in a distributed scheme
38788 std::vector<bool> internal_to_shared_boundary;
38789
38790 // --------- Stuff to deal with splitted boundaries ---------- Begin -----
38791 // Set of coordinates that are on the boundary (splitted boundary version)
38792 // The first vector is used to allocate the points for each sub-boundary
38793 // Set entries are ordered on first entry in vector which stores
38794 // the boundary coordinate so the vertices come out in order!
38796
38797 // Vector to store the vertices, transfer the sorted vertices from the
38798 // set (sub_vertex_nodes) to this vector, --- including the z-value ---
38800
38801 // Vector to store the coordinates of the polylines that will represent
38802 // the splitted boundary. Used to pass the info. from sub_vertex_nodes
38803 // but --- without the z-value ---, used to generate the sub-polylines
38805
38806 // --------- Stuff to deal with splitted boundaries ----------- End
38807 // ------
38808
38809#endif // #ifdef OOMPH_HAS_MPI
38810
38811 // Sort the face element, those that have both elements (one at
38812 // each side of the boundary) marked as nonhalo, and those with one
38813 // nonhalo an the other as halo
38814
38815 // Number of done face elements
38816 unsigned nsorted_face_elements = 0;
38817
38818#ifdef OOMPH_HAS_MPI
38819 // Counter for sub_boundaries
38820 unsigned nsub_boundaries = 0;
38821#endif // #ifdef OOMPH_HAS_MPI
38822
38823 // Total number of non halo double face element
38824 const unsigned nnon_halo_doubled_face_ele =
38826
38827 // Continue until all the face elements have been sorted
38828 // This while is to deal with the cases of splitted boundaries
38830 {
38831 // Get and initial face element
38834#ifdef PARANOID
38835 bool found_initial_face_element = false;
38836#endif
38837
38838 // Flag to know if we are working with a face element which the
38839 // face element at the other side of the boundary is also non
38840 // halo
38842
38843 unsigned iface = 0;
38844 for (iface = 0; iface < nnon_halo_doubled_face_ele; iface += 2)
38845 {
38847 // If not done then take it as initial face element
38849 {
38850 // Mark it as done
38852 // Get the other side boundary face element
38854 // ... also mark as done the repeated face element
38856
38857#ifdef OOMPH_HAS_MPI
38859 {
38861 }
38862#endif // #ifdef OOMPH_HAS_MPI
38863
38864 // Plus two because internal boundaries have
38865 // two face elements per each edge
38867 iface += 2;
38868#ifdef PARANOID
38869 // And set the flag to true
38871#endif
38872 break;
38873 }
38874 }
38875
38876#ifdef PARANOID
38878 {
38879 std::ostringstream error_message;
38880 error_message << "Could not find an initial face element for the "
38881 "current segment\n";
38882 throw OomphLibError(error_message.str(),
38885 }
38886#endif
38887
38888 // Local set of coordinates that are on the boundary Set entries
38889 // are ordered on first entry in vector which stores the boundary
38890 // coordinate so the vertices come out in order
38891 std::set<Vector<double>> local_vertex_nodes;
38892
38893 // Vector to store the vertices, transfer the sorted vertices from the
38894 // set (local) to this vector (local), --- including the z-value ---
38896
38897 // Vector to store the target areas, uses the same approach as the
38898 // set for the local_vertex_nodes, ordered on first entry
38899 std::set<Vector<double>> sorted_target_areas;
38900
38901 // Vector to store the target areas, used to transfer the sorted target
38902 // areas from "sorted_target_areas" set
38904
38905 // ------------------------------------------------------------------
38906 // Add the vertices of the initial face element to the set of local
38907 // sorted vertices
38908 // ------------------------------------------------------------------
38909 const unsigned nnode = ele_face_pt->nnode();
38910 // Add the left-hand node to the set:
38911 // Boundary coordinate
38913 vertex_coord[0] = bound_left[0];
38914
38915 // Actual coordinates
38916 for (unsigned i = 0; i < 2; i++)
38917 {
38918 vertex_coord[i + 1] = ele_face_pt->node_pt(0)->x(i);
38919 }
38921
38922 // Add the right-hand node to the set:
38923 // Boundary coordinate
38926 vertex_coord[0] = bound_right[0];
38927
38928 // Actual coordinates
38929 for (unsigned i = 0; i < 2; i++)
38930 {
38931 vertex_coord[i + 1] = ele_face_pt->node_pt(nnode - 1)->x(i);
38932 }
38934
38935 // The initial and final node on the set
38938
38939 // -----------------------------------------------------
38940 // Find the global index in the mesh of the face element
38941 // and use it to get its associated target area
38942 // -----------------------------------------------------
38943 // Container to store the zeta value (used as index) and
38944 // the associated target area of the element
38946
38947 // Use the minimum zeta value to sort the target areas
38948 // along the boundary
38949 zeta_target_area_values[0] = std::min(bound_left[0], bound_right[0]);
38950
38951 // Get the index of the face element on the current boundary
38953 // Get the "ef"-th element on the boundary
38954 FiniteElement* el_pt = this->boundary_element_pt(bound, ef);
38955 double target_area_face_element = 0.0;
38956
38957#ifdef PARANOID
38958 bool found_global_element_index = false;
38959#endif
38960 for (unsigned eg = 0; eg < nele; eg++)
38961 {
38962 // Get the "eg-th" element
38963 FiniteElement* el_compare_pt = this->finite_element_pt(eg);
38964
38965 // Compare with the element on the boundary, if equal then
38966 // store the target area
38967 if (el_pt == el_compare_pt)
38968 {
38970#ifdef PARANOID
38972#endif
38973 break; // break the for (eg < nele) global element
38974 } // if el_pt == el_compare_pt
38975 } // for nele (on complete mesh)
38976
38977#ifdef PARANOID
38979 {
38980 std::ostringstream error_message;
38981 error_message << "The global index for the (" << ef
38982 << ")-th face element "
38983 << "on\nthe (" << bound
38984 << ")-th boundary was not found!!!";
38985 throw OomphLibError(error_message.str(),
38988 }
38989#endif
38990
38991 // Get the index of the repeated face element on the current boundary
38992 const unsigned ref =
38994 FiniteElement* rel_pt = this->boundary_element_pt(bound, ref);
38996
38997#ifdef PARANOID
38999#endif
39000 for (unsigned eg = 0; eg < nele; eg++)
39001 {
39002 // Get the "eg-th" element
39003 FiniteElement* el_compare_pt = this->finite_element_pt(eg);
39004
39005 // Compare with the element on the boundary, if equal then
39006 // store the target area
39007 if (rel_pt == el_compare_pt)
39008 {
39010#ifdef PARANOID
39012#endif
39013 break; // break the for (eg < nele) global element
39014 } // if rel_pt == el_compare_pt
39015 } // for nele (on complete mesh)
39016
39017#ifdef PARANOID
39019 {
39020 std::ostringstream error_message;
39021 error_message << "The global index for the (" << ref
39022 << ")-th face element "
39023 << "on\nthe (" << bound
39024 << ")-th boundary was not found (repeated "
39025 << "face element)!!!";
39026 throw OomphLibError(error_message.str(),
39029 }
39030#endif
39031
39032 // Choose the minimum target area from both elements, one at each side
39033 // of the edge on the boundary
39036
39037 // Add the target areas to the sorted set
39039 // ------------------------------------------------------------------
39040
39041 // Continue iterating if a new face element has been added to the
39042 // list
39043 bool face_element_added = false;
39044
39045 // While a new face element has been added to the set of sorted
39046 // face elements then re-iterate
39047 do
39048 {
39049 // Start from the next face elements since we have already
39050 // added the previous one as the initial face element (any
39051 // previous face element had to be added on previous
39052 // iterations)
39053 for (unsigned iiface = iface; iiface < nnon_halo_doubled_face_ele;
39054 iiface += 2)
39055 {
39056 face_element_added = false;
39058
39059 // Check that the face element with which we are working has
39060 // the same conditions as the root face element (both faces
39061 // are nonhalo or one face is halo and the other nonhalo)
39062
39063 // Get the face element at the other side of the boundary
39065 bool both_face_elements_are_nonhalo = false;
39066
39067#ifdef OOMPH_HAS_MPI
39069 {
39071 }
39072#endif // #ifdef OOMPH_HAS_MPI
39073
39077 {
39078 // Get each individual node to check if they are contiguous
39079 const unsigned nlnode = ele_face_pt->nnode();
39082
39084 {
39086 face_element_added = true;
39087 }
39088 else if (left_node_pt == last_node_pt)
39089 {
39091 face_element_added = true;
39092 }
39093 else if (right_node_pt == first_node_pt)
39094 {
39096 face_element_added = true;
39097 }
39098 else if (right_node_pt == last_node_pt)
39099 {
39101 face_element_added = true;
39102 }
39103
39105 {
39106 // Add the left-hand node to the set:
39107 // Boundary coordinate
39108 left_node_pt->get_coordinates_on_boundary(bound, bound_left);
39109 vertex_coord[0] = bound_left[0];
39110
39111 // Actual coordinates
39112 for (unsigned i = 0; i < 2; i++)
39113 {
39114 vertex_coord[i + 1] = left_node_pt->x(i);
39115 }
39117
39118 // Add the right-hand nodes to the set:
39119 // Boundary coordinate
39120 right_node_pt->get_coordinates_on_boundary(bound, bound_right);
39121 vertex_coord[0] = bound_right[0];
39122
39123 // Actual coordinates
39124 for (unsigned i = 0; i < 2; i++)
39125 {
39126 vertex_coord[i + 1] = right_node_pt->x(i);
39127 }
39129
39130 // Mark as done only if one of its nodes has been
39131 // added to the list
39133 // .. also mark as done the face element at the othe side of
39134 // the boundary
39138 // ... and increase the number of sorted face elements
39140
39141 // -----------------------------------------------------
39142 // Find the global index in the mesh of the face element
39143 // and use it to get its associated target area
39144 // -----------------------------------------------------
39145 // Use the minimum zeta value to sort the target areas
39146 // along the boundary
39148 std::min(bound_left[0], bound_right[0]);
39149
39150 // Get the "ef"-th element on the boundary
39151 const unsigned lef =
39153 FiniteElement* lel_pt = this->boundary_element_pt(bound, lef);
39154
39155#ifdef PARANOID
39157#endif
39158 for (unsigned eg = 0; eg < nele; eg++)
39159 {
39160 // Get the "eg-th" element
39161 FiniteElement* lel_compare_pt = this->finite_element_pt(eg);
39162
39163 // Compare with the element on the boundary, if equal then
39164 // store the target area
39165 if (lel_pt == lel_compare_pt)
39166 {
39168#ifdef PARANOID
39170#endif
39171 break; // break the for (eg < nele) global element
39172 } // if lel_pt == lel_compare_pt
39173 } // for nele (on complete mesh)
39174
39175#ifdef PARANOID
39177 {
39178 std::ostringstream error_message;
39179 error_message << "The global index for the (" << lef
39180 << ")-th face element "
39181 << "on\nthe (" << bound
39182 << ")-th boundary was not found!!!";
39183 throw OomphLibError(error_message.str(),
39186 }
39187#endif
39188
39189 // Get the index of the repeated face element on the boundary
39190 const unsigned rlef =
39192 FiniteElement* rlel_pt = this->boundary_element_pt(bound, rlef);
39193
39194#ifdef PARANOID
39196#endif
39197 for (unsigned eg = 0; eg < nele; eg++)
39198 {
39199 // Get the "eg-th" element
39200 FiniteElement* lel_compare_pt = this->finite_element_pt(eg);
39201
39202 // Compare with the element on the boundary, if equal then
39203 // store the target area
39204 if (rlel_pt == lel_compare_pt)
39205 {
39207#ifdef PARANOID
39209#endif
39210 break; // break the for (eg < nele) global element
39211 } // if rlel_pt == el_compare_pt
39212 } // for nele (on complete mesh)
39213
39214#ifdef PARANOID
39216 {
39217 std::ostringstream error_message;
39218 error_message << "The global index for the (" << rlef
39219 << ")-th face element "
39220 << "on\nthe (" << bound
39221 << ")-th boundary was not found "
39222 << "(repeated face element)!!!";
39223 throw OomphLibError(error_message.str(),
39226 }
39227#endif
39228
39229 // Choose the minimum target area from both elements, one
39230 // at each side of the edge on the boundary
39231 zeta_target_area_values[1] = std::min(
39233
39234 // Add the target areas to the sorted set
39236
39237 break;
39238 }
39239
39240 } // if (!face_element_done[[ele_face_pt])
39241 } // for (iiface<nnon_halo_doubled_face_ele)
39242 } while (face_element_added &&
39244
39245 // -------------------------------------------------------------
39246 // At this point we already have a sorted set of nodes and can
39247 // be used to peform the unrefinement and refinement procedures
39248 // -------------------------------------------------------------
39249
39250 // Get the number of nodes on the list
39251 const unsigned nlocal_nodes = local_vertex_nodes.size();
39252 // Change representation to vector for easy of handling ...
39254
39255 // Copy the vertices of the nodes
39256 unsigned counter = 0;
39257 std::set<Vector<double>>::iterator it_vertex;
39258 for (it_vertex = local_vertex_nodes.begin();
39260 it_vertex++)
39261 {
39263 local_tmp_vector_vertex_node[counter][0] = (*it_vertex)[0];
39264 local_tmp_vector_vertex_node[counter][1] = (*it_vertex)[1];
39265 local_tmp_vector_vertex_node[counter][2] = (*it_vertex)[2];
39266 counter++;
39267 }
39268
39269 // ... same for the info. related with the target areas (turn
39270 // into vector)
39271 const unsigned ntarget_areas = sorted_target_areas.size();
39273 counter = 0;
39274 std::set<Vector<double>>::iterator it_area;
39275 for (it_area = sorted_target_areas.begin();
39277 ++it_area)
39278 {
39279 tmp_sorted_target_areas[counter] = (*it_area)[1];
39280 ++counter;
39281 }
39282
39283#ifdef PARANOID
39284 if (nlocal_nodes > 0 && (ntarget_areas != nlocal_nodes - 1))
39285 {
39286 std::ostringstream error_message;
39287 error_message
39288 << "The boundary (" << bound << ") was split during the "
39289 << "distribution process.\n"
39290 << "The problem comes when associating the target areas with the "
39291 << "elements that gave\nrise to the vertex coordinates.\n"
39292 << "The number of local nodes on the 'sub-polyline' ("
39293 << nlocal_nodes << ") is not according with the number of target\n"
39294 << "areas (" << ntarget_areas << ") for that number of nodes.\n"
39295 << "The target areas number must be equal to the number of "
39296 "nodes-1\n";
39297 throw OomphLibError(error_message.str(),
39300 }
39301#endif
39302
39303 // The unrefinement and refinement process needs to be applied
39304 // from the bottom-left node since the internal open curve could
39305 // lie on the shared boundaries
39308 {
39309 std::reverse(local_tmp_vector_vertex_node.begin(),
39311 std::reverse(tmp_sorted_target_areas.begin(),
39313 }
39314 else if (local_tmp_vector_vertex_node[nlocal_nodes - 1][2] ==
39316 {
39319 {
39320 std::reverse(local_tmp_vector_vertex_node.begin(),
39322 std::reverse(tmp_sorted_target_areas.begin(),
39324 }
39325 }
39326
39327 // ------------------------------------------------------------
39328 // Create the vertices along the boundary using the target
39329 // area to define the distance among them
39330 // ------------------------------------------------------------
39331
39332 // Tolerance below which the middle point can be deleted
39333 // (ratio of deflection to element length)
39334 double unrefinement_tolerance =
39335 open_curve_pt->polyline_pt(cs)->unrefinement_tolerance();
39336
39337 // Apply unrefinement
39339 unrefine_boundary_constrained_by_target_area(
39340 bound,
39341 chunk,
39343 unrefinement_tolerance,
39345
39346 // Tolerance for refinement
39347 double refinement_tolerance =
39348 open_curve_pt->polyline_pt(cs)->refinement_tolerance();
39349
39350 // Apply refinement
39351 bool refinement_applied = refine_boundary_constrained_by_target_area(
39354 refinement_tolerance,
39356
39357 // Clear the local containter to recover the nodes ordered using
39358 // the zeta value
39359 local_vertex_nodes.clear();
39360
39361 // At the end of each unrefinement/refinement step store the new
39362 // nodes on the set that will give rise to the vertices of the
39363 // new polyline representation
39364 const unsigned nnew_nodes = local_tmp_vector_vertex_node.size();
39365 for (unsigned i = 0; i < nnew_nodes; i++)
39366 {
39370 vertex_nodes.insert(vertex_coord); // Global container
39372 }
39373
39374 // Update the flag to indicate whether an unrefinement or
39375 // refinement was applied
39377
39378#ifdef OOMPH_HAS_MPI
39379 if (this->is_mesh_distributed())
39380 {
39381 // Add the set of vertices for the boundary, this will help to
39382 // detect if we need to deal with sub_boundaries and
39383 // sub_polylines representations
39385 // Increase the counter for sub_boundaries
39387
39388 // Mark if the polyline created by these vertices will be used
39389 // as a shared boundary or as an internal boundary
39391 {
39392 internal_to_shared_boundary.push_back(false);
39393 }
39394 else
39395 {
39396 internal_to_shared_boundary.push_back(true);
39397 }
39398 }
39399#endif
39400
39401 } // while(nsorted_face_elements < nnon_halo_doubled_face_ele)
39402 // This while is in charge of sorting all the face elements to
39403 // create the new representation of the polyline (also deals
39404 // with the sub-boundary cases)
39405
39406 // Now turn into vector for ease of handling...
39407 const unsigned npoly_vertex = vertex_nodes.size();
39409 unsigned count = 0;
39410 for (std::set<Vector<double>>::iterator it = vertex_nodes.begin();
39411 it != vertex_nodes.end();
39412 ++it)
39413 {
39414 tmp_vector_vertex_node[count].resize(3);
39415 tmp_vector_vertex_node[count][0] = (*it)[0];
39416 tmp_vector_vertex_node[count][1] = (*it)[1];
39417 tmp_vector_vertex_node[count][2] = (*it)[2];
39418 ++count;
39419 }
39420
39421#ifdef OOMPH_HAS_MPI
39422 // Check that the number of set of vertices marked to be part of a
39423 // shared boundary or of an internal boundaries be the same as the
39424 // total number of sub-boundaries
39425#ifdef PARANOID
39426 const unsigned nsub_boundaries_set = sub_vertex_nodes.size();
39427 const unsigned ninternal_to_shared_boundaries =
39430 {
39431 std::ostringstream error_message;
39432 error_message
39433 << "The number of found sub-boundaries and the number of marked "
39434 << "internal\nboundaries are different\n"
39435 << "Number of found sub-boundaries: (" << nsub_boundaries_set << ")\n"
39436 << "Number of marked internal boundaries: ("
39437 << ninternal_to_shared_boundaries << ")\n\n";
39438 throw OomphLibError(error_message.str(),
39441 }
39442#endif
39443
39444 // --------- Stuff for the sub_boundaries ----- Begin section -------
39445#ifdef PARANOID
39447 {
39448 std::ostringstream error_message;
39449 error_message
39450 << "The number of found sub-boundaries and the number of counted\n"
39451 << "sub-boundaries are different:\n"
39452 << "Number of found sub-boundaries: (" << nsub_boundaries_set << ")\n"
39453 << "Number of counted sub-boundaries: (" << nsub_boundaries
39454 << ")\n\n";
39455 throw OomphLibError(error_message.str(),
39458 }
39459#endif
39460
39461 // Verify if need to deal with sub_boundaries
39462 if (this->is_mesh_distributed() && nsub_boundaries > 1)
39463 {
39464 // Mark the boundary as been splitted in the partition process
39465 this->Boundary_was_splitted[bound] = true;
39466
39467 // Resize the vector to store the info. of sub-boundaries
39469 // Loop over the sub-boundaries
39470 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
39471 {
39472 // Turn info. into vector for ease of handling...
39473 const unsigned nsubpoly_vertex = sub_vertex_nodes[isub].size();
39475 unsigned subcount = 0;
39476 std::set<Vector<double>>::iterator subit;
39477 for (subit = sub_vertex_nodes[isub].begin();
39478 subit != sub_vertex_nodes[isub].end();
39479 ++subit)
39480 {
39482 sub_tmp_vector_vertex_node[isub][subcount][0] = (*subit)[0];
39483 sub_tmp_vector_vertex_node[isub][subcount][1] = (*subit)[1];
39484 sub_tmp_vector_vertex_node[isub][subcount][2] = (*subit)[2];
39485 ++subcount;
39486 }
39487 }
39488 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
39489 // --------- Stuff for the sub_boundaries ----- End section ----------
39490#endif // OOMPH_HAS_MPI
39491
39492 // For further processing the three-dimensional vector has to be
39493 // reduced to a two-dimensional vector
39495
39496 // Resize the vector for vectices
39498 for (unsigned i = 0; i < n_vertex; i++)
39499 {
39500 vector_vertex_node[i].resize(2);
39503 }
39504
39505#ifdef OOMPH_HAS_MPI
39506 // --------- Stuff for the sub_boundaries ----- Begin section -------
39507 // Verify if need to deal with sub_boundaries
39508 if (this->is_mesh_distributed() && nsub_boundaries > 1)
39509 {
39510 // For further processing the three-dimensional vector has to be
39511 // reduced to a two-dimensional vector
39512 // Resize the vector to store the info. of sub-boundaries
39514 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
39515 {
39516 const unsigned subn_vertex = sub_tmp_vector_vertex_node[isub].size();
39517 // Resize the vector for vectices
39519 for (unsigned i = 0; i < subn_vertex; i++)
39520 {
39521 sub_vector_vertex_node[isub][i].resize(2);
39526 }
39527 }
39528 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
39529
39530 // We already have the info. for the sub-boundaries (if necessary)
39531 // and then we can create the sub-boundaries representations to
39532 // ease the generation of the mesh by Triangle
39533
39534 // --------- Stuff for the sub_boundaries ----- End section ---------
39535#endif // OOMPH_HAS_MPI
39536
39537 // ------------------------------------------------------------------
39538 // Check for contiguousness
39539 // ------------------------------------------------------------------
39540#ifdef OOMPH_HAS_MPI
39541 // Only perform this checking if the mesh is not distributed When
39542 // the mesh is distributed the polylines continuity is addressed by
39543 // the sort_polylines_helper() method
39544 if (!this->is_mesh_distributed())
39545#endif
39546 {
39547 if (cs > 0)
39548 {
39549 // Final end point of previous line
39551 unsigned n_prev_vertex =
39552 open_curve_pt->curve_section_pt(cs - 1)->nvertex();
39554 open_curve_pt->polyline_pt(cs - 1)->vertex_coordinate(
39555 n_prev_vertex - 1);
39556
39557 unsigned prev_seg_boundary_id =
39558 open_curve_pt->curve_section_pt(cs - 1)->boundary_id();
39559
39560 // Find the error between the final vertex of the previous
39561 // line and the first vertex of the current line
39562 double error = 0.0;
39563 for (unsigned i = 0; i < 2; i++)
39564 {
39565 const double dist = final_vertex_of_previous_segment[i] -
39566 (*vector_vertex_node.begin())[i];
39567 error += dist * dist;
39568 }
39569 error = sqrt(error);
39570
39571 // If the error is bigger than the tolerance then
39572 // we probably need to reverse, but better check
39574 {
39575 // Find the error between the final vertex of the previous
39576 // line and the last vertex of the current line
39577 double rev_error = 0.0;
39578 for (unsigned i = 0; i < 2; i++)
39579 {
39580 const double dist = final_vertex_of_previous_segment[i] -
39581 (*--vector_vertex_node.end())[i];
39582 rev_error += dist * dist;
39583 }
39585
39586 if (rev_error >
39588 {
39589 // It could be possible that the first segment be reversed and we
39590 // did not notice it because this check does not apply for the
39591 // first segment. We can verify if the first segment is reversed
39592 // by using the vertex number 1
39593 if (cs == 1)
39594 {
39595 // Initial end point of previous line
39597
39599 open_curve_pt->polyline_pt(cs - 1)->vertex_coordinate(0);
39600
39601 unsigned prev_seg_boundary_id =
39602 open_curve_pt->curve_section_pt(cs - 1)->boundary_id();
39603
39604 // Find the error between the initial vertex of the previous
39605 // line and the first vertex of the current line
39606 double error = 0.0;
39607 for (unsigned i = 0; i < 2; i++)
39608 {
39609 const double dist = initial_vertex_of_previous_segment[i] -
39610 (*vector_vertex_node.begin())[i];
39611 error += dist * dist;
39612 }
39613 error = sqrt(error); // Reversed only the previous one
39614
39615 // If the error is bigger than the tolerance then
39616 // we probably need to reverse, but better check
39617 if (error >
39619 {
39620 // Find the error between the final vertex of the previous
39621 // line and the last vertex of the current line
39622 double rev_error = 0.0;
39623 for (unsigned i = 0; i < 2; i++)
39624 {
39625 const double dist = initial_vertex_of_previous_segment[i] -
39626 (*--vector_vertex_node.end())[i];
39627 rev_error += dist * dist;
39628 }
39629 rev_error = sqrt(rev_error); // Reversed both the current
39630 // one and the previous one
39631
39632 if (rev_error >
39634 {
39635 std::ostringstream error_stream;
39637 << "The distance between the first node of the current\n"
39638 << "line segment (boundary " << bound
39639 << ") and either end of "
39640 << "the previous line segment\n"
39641 << "(boundary " << prev_seg_boundary_id
39642 << ") is bigger than"
39643 << " the desired tolerance "
39645 << ".\n"
39646 << "This suggests that the polylines defining the "
39647 "polygonal\n"
39648 << "representation are not properly ordered.\n"
39649 << "Fail on last vertex of polyline: ("
39651 << ") and\nfirst vertex of polyline (" << bound
39652 << ").\nThis should have failed when first trying to "
39653 << "construct the\npolygon.\n";
39654 throw OomphLibError(error_stream.str(),
39657 }
39658 else
39659 {
39660 // Reverse both
39661 // Reverse the current vector to line up with the previous
39662 // one
39663 std::reverse(vector_vertex_node.begin(),
39664 vector_vertex_node.end());
39665 open_curve_pt->polyline_pt(cs - 1)->reverse();
39666 }
39667 }
39668 else
39669 {
39670 // Reverse the previous one
39671 open_curve_pt->polyline_pt(cs - 1)->reverse();
39672 }
39673
39674 } // if (cs == 1)
39675 else
39676 {
39677 std::ostringstream error_stream;
39679 << "The distance between the first node of the current\n"
39680 << "line segment (boundary " << bound
39681 << ") and either end of "
39682 << "the previous line segment\n"
39683 << "(boundary " << prev_seg_boundary_id
39684 << ") is bigger than the "
39685 << "desired tolerance "
39687 << ".\n"
39688 << "This suggests that the polylines defining the polygonal\n"
39689 << "representation are not properly ordered.\n"
39690 << "Fail on last vertex of polyline: ("
39691 << prev_seg_boundary_id << ") and\nfirst vertex of polyline ("
39692 << bound << ").\n"
39693 << "This should have failed when first trying to construct\n"
39694 << "the polygon.\n";
39695 throw OomphLibError(error_stream.str(),
39698 }
39699 }
39700 else
39701 {
39702 // Reverse the current vector to line up with the previous one
39703 std::reverse(vector_vertex_node.begin(),
39704 vector_vertex_node.end());
39705 }
39706 }
39707
39708 } // if (cs > 0)
39709
39710 } // if (!this->is_mesh_distributed())
39711
39712 // ---------------------------------------------------------------
39713 // Update the polylines representation
39714 // ---------------------------------------------------------------
39715 // Always update the polylines representation, in a distributed
39716 // mesh it is necessary to update the polyline representation since
39717 // it may no longer have vertices (the boundary may not be part of
39718 // the domain in the current processor)
39719
39720 // The new number of vertices
39722
39723 // Update the polyline according to the new vertices
39726
39727 // Create a temporal "curve section" version of the recently
39728 // created polyline
39730
39731 // Tolerance below which the middle point can be deleted (ratio of
39732 // deflection to element length)
39733 double unrefinement_tolerance =
39734 open_curve_pt->polyline_pt(cs)->unrefinement_tolerance();
39735
39736 // Tolerance to add points
39737 double refinement_tolerance =
39738 open_curve_pt->polyline_pt(cs)->refinement_tolerance();
39739
39740 // Establish refinement and unrefinement tolerance
39741 tmp_polyline_pt->set_unrefinement_tolerance(unrefinement_tolerance);
39742 tmp_polyline_pt->set_refinement_tolerance(refinement_tolerance);
39743
39744 // Establish the maximum length constraint
39745 double maximum_length = open_curve_pt->polyline_pt(cs)->maximum_length();
39746 tmp_polyline_pt->set_maximum_length(maximum_length);
39747
39748#ifdef OOMPH_HAS_MPI
39749 // If the mesh is distributed check that the polyline still has
39750 // vertices
39751 if (this->is_mesh_distributed())
39752 {
39753 if (n_vertex >= 2)
39754 {
39755 // Pass the connection information from the old polyline to
39756 // the new one
39757 this->copy_connection_information(open_curve_pt->polyline_pt(cs),
39759 } // if (n_vertex >= 2)
39760 } // if (this->is_mesh_distributed())
39761 else
39762#endif
39763 {
39764 // Pass the connection information from the old polyline to the
39765 // new one
39766 this->copy_connection_information(open_curve_pt->polyline_pt(cs),
39768 }
39769
39770 // Now update the polyline according to the new vertices but first
39771 // check if the object is allowed to delete the representation or
39772 // if it should be done by other object
39773 bool delete_it_on_destructor = false;
39774
39775 std::set<TriangleMeshCurveSection*>::iterator it =
39776 this->Free_curve_section_pt.find(open_curve_pt->curve_section_pt(cs));
39777
39778 if (it != this->Free_curve_section_pt.end())
39779 {
39780 this->Free_curve_section_pt.erase(it);
39781 delete open_curve_pt->curve_section_pt(cs);
39783 }
39784
39785 // -------------------------------------------------------------
39786 // Copying the new representation
39787 open_curve_pt->curve_section_pt(cs) = tmp_polyline_pt;
39788
39789 // Update the Boundary - Polyline map
39790 this->Boundary_curve_section_pt[bound] =
39791 open_curve_pt->curve_section_pt(cs);
39792
39794 {
39795 this->Free_curve_section_pt.insert(open_curve_pt->curve_section_pt(cs));
39796 }
39797
39798#ifdef OOMPH_HAS_MPI
39799 // If there are not sub-boundaries mark the boundary if need to be
39800 // trated as shared or as internal boundary
39801 if (this->is_mesh_distributed() && nsub_boundaries == 1)
39802 {
39803 // Clear all previous stored data
39804 this->Boundary_marked_as_shared_boundary[bound].clear();
39805
39806 // .. and store the flag for the boundary
39807 this->Boundary_marked_as_shared_boundary[bound].push_back(
39809 }
39810 // --------- Stuff for the sub_boundaries ----- Begin section --------
39811 // Verify if need to deal with sub_boundaries
39812 else if (this->is_mesh_distributed() && nsub_boundaries > 1)
39813 {
39814 // Create temporary representations for the boundaries, only to
39815 // create the mesh when calling Triangle
39816
39817 // Clear all previous stored data
39818 this->Boundary_subpolylines[bound].clear();
39819 // Now create storage for the sub-boundaries
39820 this->Boundary_subpolylines[bound].resize(nsub_boundaries);
39821
39822 // Clear all previous stored data
39823 this->Boundary_marked_as_shared_boundary[bound].clear();
39824 // Create storage to mark the internal boundaries as shared
39825 // boundaries
39826 this->Boundary_marked_as_shared_boundary[bound].resize(nsub_boundaries);
39827 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
39828 {
39829 // Now update the polyline according to the sub set of
39830 // vertices, set the chunk number of the polyline
39833
39834 // Add the sub-polyline to the container to represent the
39835 // boundary in parts
39836 this->Boundary_subpolylines[bound][isub] = sub_tmp_polyline_pt;
39837
39838 // Copy the flag that mark the boundary as internal or as
39839 // shared bound
39840 this->Boundary_marked_as_shared_boundary[bound][isub] =
39842
39843 // No need to send the unrefinement/refinement and maximum
39844 // length constraints since these are only temporary
39845 // representations
39846
39847 // But we certanly we need to pass the connection information
39848 // to the sub-polylines
39849 // Get a curve section representation of the sub-polyline
39852 this->copy_connection_information_to_sub_polylines(
39854
39855 } // for (isub < nsub_boundaries)
39856
39857 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
39858 // --------- Stuff for the sub_boundaries ----- End section ---------
39859#endif // OOMPH_HAS_MPI
39860
39861 // Delete the allocated memory for the geometric object
39862 // that represents the curvilinear boundary
39863 delete mesh_geom_obj_pt;
39864
39865 } // for (cs < ncurve_section)
39866
39867 // Cleanup the face mesh
39868 for (unsigned p = 0; p < ncurve_section; p++)
39869 {
39870 face_mesh_pt[p]->flush_node_storage();
39871 delete face_mesh_pt[p];
39872 }
39873
39874 return update_was_performed;
39875 }
39876
39877#ifdef OOMPH_HAS_MPI
39878 //======================================================================
39879 /// Updates the polylines using the elements area as
39880 /// constraint for the number of points along the boundaries
39881 //======================================================================
39882 template<class ELEMENT>
39886 {
39887 // Flag to check if there were a change on the shared boundary
39888 // representation
39889 unsigned update_was_performed = false;
39890
39891 // Go through all the shared boundaries/polylines
39892 const unsigned n_polylines = vector_polyline_pt.size();
39893 for (unsigned pp = 0; pp < n_polylines; pp++)
39894 {
39895 // Get the boundary id of the current polyline
39896 const unsigned shd_bnd_id = vector_polyline_pt[pp]->boundary_id();
39897
39898 // Get the chunk number
39899 const unsigned chunk = vector_polyline_pt[pp]->boundary_chunk();
39900
39901 // Get the face elements that created the shared boundary from the
39902 // bulk shared boundary elements
39903
39904 // Compute the face elements from the shared boundary elements,
39905 // create an association from the face element with the "bulk"
39906 // elements
39907 std::map<FiniteElement*, FiniteElement*> face_ele_pt_to_bulk_element_pt;
39908
39909 // The temporary storage for the halo face elements
39911 // The temporary storage for the nonhalo face elements
39913
39914 // Get the number of shared boundary elements associated with the
39915 // current shared boundary
39916 const unsigned nshared_bound_ele =
39917 this->nshared_boundary_element(shd_bnd_id);
39918
39919 // Loop over the elements in the shared boundary to create the face
39920 // elements
39921 for (unsigned e = 0; e < nshared_bound_ele; e++)
39922 {
39923 // Get the shared boundary element
39925 this->shared_boundary_element_pt(shd_bnd_id, e);
39926
39927 // Get the face index
39928 int face_index = this->face_index_at_shared_boundary(shd_bnd_id, e);
39929
39930 // Before adding the new element we need to ensure that the edge
39931 // that this element represents has not been already added
39933 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
39934
39935 // Establish the association between the bulk element and the
39936 // face element
39938
39939 // Nonhalo element
39940 if (!bulk_ele_pt->is_halo())
39941 {
39942 // Add nonhalo shared face element to the container
39944 }
39945 else // halo element
39946 {
39947 // Add halo shared face element to the container
39949 }
39950
39951 } // for (e < nshared_bound_ele)
39952
39953 // Now we have the face elements, we need to ensure that the halo
39954 // and nonhalo bulk element are sorted one after the other
39956
39957 // Mark the face elements already used
39958 std::map<FiniteElement*, bool> shared_face_done;
39959
39960 // Get the number of nonhalo face elements
39961 const unsigned nnonhalo_face_shared_ele =
39963
39964 // Get the number of halo face elements
39966
39967#ifdef PARANOID
39968 // The number of nonhalo shared face boundary elements must be the
39969 // half of the total number of shared boundary elements
39971 {
39972 std::ostringstream error_message;
39973 error_message
39974 << "The number of shared boundary elements (" << nshared_bound_ele
39975 << ") is not the double\nof the number of unsorted NONHALO shared "
39976 << "face boundary elements (" << nnonhalo_face_shared_ele << ")\n"
39977 << "for the current boundary (" << shd_bnd_id << ")\n\n";
39978 throw OomphLibError(error_message.str(),
39981 }
39982
39983 // The number of halo shared face boundary elements must be the
39984 // half of the total number of shared boundary elements
39986 {
39987 std::ostringstream error_message;
39988 error_message
39989 << "The number of shared boundary elements (" << nshared_bound_ele
39990 << ") is not the double\nof the number of unsorted HALO shared "
39991 << "face boundary elements (" << nhalo_face_shared_ele << ")\n"
39992 << "for the current boundary (" << shd_bnd_id << ")\n\n";
39993 throw OomphLibError(error_message.str(),
39996 }
39997#endif
39998
39999 // ------------------------------------------------------------------
40000 // Loop over the nonhalo face elements and look for the halo face
40001 // element at the other side of the shared boundary
40002 for (unsigned inh = 0; inh < nnonhalo_face_shared_ele; inh++)
40003 {
40004 // Get the inh-th face element
40006
40007 // Get the number of nodes on the face element
40008 const unsigned nnodes_nh = nonhalo_face_ele_pt->nnode();
40009 // Get the first and last node on the element
40012
40013 // Now find the (halo) face element at the other side of the
40014 // shared boundary
40015 for (unsigned ih = 0; ih < nhalo_face_shared_ele; ih++)
40016 {
40017 // Get the ih-th face element
40019
40020 // Check that the face element has not been done
40022 {
40023 // Get the number of nodes on the face element
40024 const unsigned nnodes_h = halo_face_ele_pt->nnode();
40025 // Get the first and last node on the element
40028
40029 // If the nodes are the same then we have found the (halo)
40030 // face element at the other side of the shared boundary
40033 {
40034 // Get the BULK elements associated with the face elements
40036 // Get the BULK elements associated to the face elements
40037 // (the nonhalo and the halo)
40042
40043 // Add the BULK elements to the temporal storage
40046
40047 // Store the pair of elements associated to the "edge"
40049
40050 // Mark the face elements as done
40053
40054 // Break the loop for (ih < nhalo_face_shared_ele)
40055 break;
40056 } // if (nh_first_node_pt == h_first_node_pt &&
40057 // nh_last_node_pt == h_last_node_pt)
40058 else if (nh_first_node_pt == h_last_node_pt &&
40060 {
40061 // Get the BULK elements associated with the face elements
40063 // Get the BULK elements associated to the face elements
40064 // (the nonhalo and the halo)
40069
40070 // Add the BULK elements to the temporal storage
40073
40074 // Store the pair of elements associated to the "edge"
40076
40077 // Mark the face elements as done
40080
40081 // Break the loop for (ih < nhalo_face_shared_ele)
40082 break;
40083 } // else if (nh_first_node_pt == h_last_node_pt &&
40084 // nh_last_node_pt == h_first_node_pt)
40085
40086 } // if (face_done[halo_face_ele_pt])
40087
40088 } // for (ih < nhalo_face_shared_ele)
40089
40090 } // for (inh < nnonhalo_face_shared_ele)
40091
40092 // -------------------------------------------------------------
40093 // Now sort the face elements
40094 // -------------------------------------------------------------
40095
40096 // We already have the shared face elements that make the shared
40097 // boundary (and the bulk elements), now sort them to create a
40098 // contiguous boundary
40099
40100#ifdef PARANOID
40101 const unsigned nunsorted_shared_bulk_ele =
40103
40104 // The number of unsorted shared BULK elements MUST be the same
40105 // as the number of shared_boundary elements divided by two
40107 {
40108 std::ostringstream error_message;
40109 error_message
40110 << "The number of shared boundary elements (" << nshared_bound_ele
40111 << ") is not the double\nof the number of unsorted shared bulk "
40112 << "boundary elements (" << nunsorted_shared_bulk_ele << ")\n"
40113 << "for the current boundary (" << shd_bnd_id << ")\n\n";
40114 throw OomphLibError(error_message.str(),
40117 }
40118
40119 // The number of done shared face elements MUST be the same as the
40120 // sum of the nonhalo and halo shared boundary face elements
40123 {
40124 std::ostringstream error_message;
40125 error_message << "The number of DONE shared boundary face elements ("
40127 << ") is not the same\n as the sum of"
40128 << "the nonhalo face shared boundary elements ("
40130 << ")\nand the halo face shared "
40131 << "boundary elements (" << nhalo_face_shared_ele
40132 << ") for the\n/"
40133 << "current boundary (" << shd_bnd_id << ")\n\n";
40134 throw OomphLibError(error_message.str(),
40137 }
40138#endif
40139
40140 // Clear the already done face elements
40141 shared_face_done.clear();
40142
40143 // The number of sorted face elements
40144 unsigned nsorted_face_ele = 0;
40145
40146 // Storing for the sorting nodes extracted from the face
40147 // elements. This are also used to update the polyline
40148 std::list<Node*> sorted_nodes;
40149
40150 // Storing for the sorted shared face elements
40151 std::list<FiniteElement*> sorted_shared_bound_elements_pt;
40152
40153 // Get the root face element
40156
40157 // Mark face as done
40159
40160 // The initial and final node on the list
40161 const unsigned nnodes_root = root_face_ele_pt->nnode();
40164
40165 // Push back on the list the new nodes
40166 sorted_nodes.push_back(first_node_pt);
40167 sorted_nodes.push_back(last_node_pt);
40168
40169 // Store the bulk elements of the current face
40174
40175 // Sort the face elements
40177 {
40178 // Flag to indicate when a node was added
40179 bool node_added = false;
40180
40181 // Start from the next edge since we have already added the
40182 // previous one as the initial face element
40183 for (unsigned iface = 1; iface < nnonhalo_face_shared_ele; iface++)
40184 {
40187
40188 // If face has not been sorted
40190 {
40191 // Get the number of nodes for the current face element
40192 const unsigned tmp_nnodes = tmp_shared_face_ele_pt->nnode();
40193
40194 // Get each individual node
40198
40200 {
40201 // Push front the new node
40202 sorted_nodes.push_front(right_node_pt);
40204 node_added = true;
40205
40206 // Store the elements of the current face element
40211 }
40212 else if (left_node_pt == last_node_pt)
40213 {
40214 // Push back the new node
40215 sorted_nodes.push_back(right_node_pt);
40217 node_added = true;
40218
40219 // Store the elements of the current face element
40224 }
40225 else if (right_node_pt == first_node_pt)
40226 {
40227 // Push front the new node
40228 sorted_nodes.push_front(left_node_pt);
40230 node_added = true;
40231
40232 // Store the elements of the current face element
40237 }
40238 else if (right_node_pt == last_node_pt)
40239 {
40240 // Push back the new node
40241 sorted_nodes.push_back(left_node_pt);
40243 node_added = true;
40244
40245 // Store the elements of the current face element
40250 }
40251
40252 if (node_added)
40253 {
40254 // Mark as done if one of its nodes has been added to the
40255 // list
40258
40259 // Break the for
40260 break;
40261 }
40262
40263 } // if (!shared_face_done[tmp_shared_face_ele_pt])
40264
40265 } // for (iface < nnonhalo_face_shared_ele)
40266
40267 } // while (nsorted_face_ele < nnonhalo_face_shared_ele))
40268
40269 // ----------------------------------------------------------------
40270 // Here we can safely delete the face elements, they are no longer
40271 // required
40272
40273 // First the nonhalo face elements
40274 for (unsigned inh = 0; inh < nnonhalo_face_shared_ele; inh++)
40275 {
40278 } // for (inh < nnonhalo_face_shared_ele)
40279
40280 // ... then the halo face elements
40281 for (unsigned ih = 0; ih < nhalo_face_shared_ele; ih++)
40282 {
40285 } // for (inh < nhalo_face_shared_ele)
40286
40287 // ------------------------------------------------------------------
40288 // At this point we already have a sorted list of nodes, get the
40289 // vertices from them and store them in a vector container
40290
40291 // Get the number of nodes on the list
40292 const unsigned n_nodes = sorted_nodes.size();
40293
40294 // The vector to store the vertices
40296
40297 // Copy the vertices from the nodes
40298 unsigned counter = 0;
40299 for (std::list<Node*>::iterator it_nodes = sorted_nodes.begin();
40300 it_nodes != sorted_nodes.end();
40301 it_nodes++)
40302 {
40303 polyline_vertices[counter].resize(2);
40304 polyline_vertices[counter][0] = (*it_nodes)->x(0);
40305 polyline_vertices[counter][1] = (*it_nodes)->x(1);
40306 counter++;
40307 }
40308
40309 // ------------------------------------------------------------------
40310 // Now get the target areas associated to the shared boundary
40311 // elements
40312
40313 // Copy the sorted elements in a vector
40315 for (std::list<FiniteElement*>::iterator it_ele =
40318 it_ele++)
40319 {
40320 sorted_shared_ele_pt.push_back((*it_ele));
40321 }
40322
40323 // Get the number of target areas
40326
40327 // Mark those shared elements already found
40328 std::map<std::pair<GeneralisedElement*, unsigned>, bool> shared_ele_done;
40329
40330 // Counter for the number of already done shared elements
40331 unsigned count_found_shared_element = 0;
40332
40333 // Get the target area associated to the shared boundary elements
40334 const unsigned nele = this->nelement();
40335
40336 // Loop over the elements to find the target areas associated to
40337 // the shared boundary elements
40338 for (unsigned e = 0; e < nele; e++)
40339 {
40340 GeneralisedElement* current_ele_pt = this->element_pt(e);
40341 // Now compare the current element with those in the sorted
40342 // shared element array
40343 for (unsigned s = 0; s < n_shared_target_areas; s++)
40344 {
40345 // Get the element
40347 // Create the pair element-index to check if done
40348 std::pair<GeneralisedElement*, unsigned> pair_gen_ele_idx =
40349 std::make_pair(current_shared_ele_pt, s);
40351 {
40352 // Compare with the global element
40354 {
40355 // Store the target area of the current shared element
40357 // Mark the shared element as done
40359 // Increase the number of found elements
40361 } // if (current_ele_pt == current_shared_ele_pt)
40362 } // if (!shared_ele_done[current_shared_ele_pt])
40363 } // for (s < nshared_taget_areas)
40364
40365 // Check if all shared elements have been found
40367 {
40368 break;
40369 }
40370
40371 } // for (e < nele)
40372
40373#ifdef PARANOID
40374 // Check if the number of found target areas is the same as the
40375 // number of shared target areas
40377 {
40378 std::ostringstream error_message;
40379 error_message << "The number of found target areas ("
40381 << ") is different from the "
40382 << "total number\nof target areas ("
40383 << n_shared_target_areas << ") in shared boundary ("
40384 << shd_bnd_id << ")\n\n";
40385 throw OomphLibError(error_message.str(),
40388 }
40389#endif
40390
40391 // The number of vertices
40392 const unsigned n_vertices = n_nodes;
40393
40394 // Get the number of segments from the input vector_polyline_pt
40395 const unsigned n_segments = vector_polyline_pt[pp]->nsegment();
40396 // Get the number of segments from the input vector_polyline_pt to
40397 // ensure that the shared boundary corresponds to the one
40398 // represented by the shared face elements (this has sence when the
40399 // mesh was re-created from re-starting)
40400
40401 // Check that the number of vertices correspond with the number of
40402 // segments
40403#ifdef PARANOID
40404 if (n_segments != n_vertices - 1)
40405 {
40406 std::ostringstream error_message;
40407 error_message
40408 << "The number of segments from the current shared polyline "
40409 << "(" << n_segments << ") does not\ncorrespond with the number of "
40410 << "sorted vertices (" << n_vertices - 1
40411 << ") of the current shared\n"
40412 << "boundary\n\n";
40413 throw OomphLibError(error_message.str(),
40416 }
40417
40418 // Check that the number of target areas correspond with the number
40419 // of vertices
40421 {
40422 std::ostringstream error_message;
40423 error_message
40424 << "The number of segments for the current sorting of edges "
40425 << "(" << n_segments << ") is different\nfrom the number of "
40426 << "target areas (" << n_shared_target_areas / 2 << ")\n\n";
40427 throw OomphLibError(error_message.str(),
40430 }
40431#endif
40432
40433 // ------------------------------------------------------------------
40434 // Get the target areas that are used to perform the unrefinement
40435 // and refinement operation. For each face element on a shared
40436 // polyline there are two bulk elements, a halo and a haloed
40437 // element, each with an associated target area. Review the
40438 // function
40439 // TriangleMesh::create_polylines_from_halo_elements_helper() to
40440 // check how the shared boundaries were created
40442 // Loop over the segments in the shared polyline
40443 for (unsigned s = 0; s < n_segments; s++)
40444 {
40445 // Get the minimum of the associated target areas
40447 std::min(sorted_shared_target_areas[s * 2],
40448 sorted_shared_target_areas[(s * 2) + 1]);
40449 }
40450
40451 // Before going to the unrefinement or refinement process check
40452 // that in all processors where the shared boundary lives start
40453 // from the same vertex.
40454 // Start from the bottom left vertex
40456 {
40457 std::reverse(polyline_vertices.begin(), polyline_vertices.end());
40458 std::reverse(polyline_target_area.begin(), polyline_target_area.end());
40459 }
40460 else if (polyline_vertices[n_vertices - 1][1] == polyline_vertices[0][1])
40461 {
40463 {
40464 std::reverse(polyline_vertices.begin(), polyline_vertices.end());
40465 std::reverse(polyline_target_area.begin(),
40466 polyline_target_area.end());
40467 }
40468 }
40469
40470 // ------------------------------------------------------------------
40471 // Apply unrefinement
40472 bool unrefinement_applied = false;
40473 // Apply unefinement if there are more than three nodes at the
40474 // shared boundary
40475 if (n_vertices > 3)
40476 {
40478 unrefine_shared_boundary_constrained_by_target_area(
40480 }
40481
40482 // Apply refinement
40483 bool refinement_applied =
40484 refine_shared_boundary_constrained_by_target_area(polyline_vertices,
40486
40487 // Was unrefinement/refinement applied
40489
40490 // ------------------------------------------------------------------
40491 // Update the polyline representation of the shared boundary
40492
40493 // The new shared polyline representation
40496
40497 // Get the curve section representation
40498 TriangleMeshCurveSection* curve_section_pt = vector_polyline_pt[pp];
40499
40500 // Copy the connection information from the old shared polyline to
40501 // the new one
40502 this->copy_connection_information(curve_section_pt, new_polyline_pt);
40503
40504 // Now update the polyline according to the new vertices but first
40505 // check if the object is allowed to delete the representation or
40506 // if it should be done by other object
40507 bool delete_it_on_destructor = false;
40508
40509 // Establish the element as being deleted by the destructor of the
40510 // class
40511 std::set<TriangleMeshCurveSection*>::iterator it =
40512 this->Free_curve_section_pt.find(curve_section_pt);
40513
40514 if (it != this->Free_curve_section_pt.end())
40515 {
40516 this->Free_curve_section_pt.erase(it);
40517 delete curve_section_pt;
40519 }
40520
40521 // Copy the new representation to the output vector_polyline_pt
40523
40524 // Get the new curve section representation
40526
40527 // Update the Boundary - Polyline map
40528 this->Boundary_curve_section_pt[shd_bnd_id] = new_curve_section_pt;
40529
40531 {
40532 this->Free_curve_section_pt.insert(new_curve_section_pt);
40533 }
40534
40535 } // for (pp < npoly)
40536
40537 return update_was_performed;
40538 }
40539#endif // #ifdef OOMPH_HAS_MPI
40540
40541 //=========================================================================
40542 /// Helper function that performs the unrefinement process
40543 /// on the specified boundary by using the provided vertices
40544 /// representation and the associated target area.
40545 //=========================================================================
40546 template<class ELEMENT>
40549 const unsigned& b,
40550 const unsigned& c,
40552 double& unrefinement_tolerance,
40554 {
40555 // Store the vertices not allowed for deletion
40556 std::set<Vector<double>> no_delete_vertex;
40557
40558 // Does the boundary receives connections?
40559 const bool boundary_receive_connections =
40560 this->boundary_connections(b, c, no_delete_vertex);
40561
40562 // Boolean that indicates whether an actual update of the vertex
40563 // coordinates was performed
40564 bool unrefinement_applied = false;
40565
40566 // Return inmedately
40567 if (!Do_boundary_unrefinement_constrained_by_target_areas)
40568 {
40569 return unrefinement_applied;
40570 }
40571
40572 // Strategy to delete nodes: Consider the target area of the
40573 // elements (e_i and e_(i+1)) sharing the i-th node (middle node),
40574 // if the number of segments to be added is equal to zero for both
40575 // elements then compute the average of both target areas and check
40576 // if the number of segments is still zero, if that holds mark the
40577 // node to be deleted. Before delete the node check whether it is in
40578 // the non_delete_vertex list. Skip the i+1-th node and go for the
40579 // (i+2)-th one, it means, increase the counter for current node by
40580 // two.
40581
40582 // Number of vertices on the boundary
40583 unsigned n_vertex = vector_bnd_vertices.size();
40584
40585 // Compute a constant value
40586 const double constant_value = 4.0 / sqrt(3.0);
40587
40588 if (n_vertex > 2)
40589 {
40590 // Go through all the vertices and delete points when the target area
40591 // indicates zero points along the boundary
40592 for (unsigned i = 1; i < n_vertex - 1; i += 2)
40593 {
40594 if (area_constraint[i - 1] > 0 && area_constraint[i] > 0)
40595 {
40596 const double local_zeta_first = vector_bnd_vertices[i - 1][0];
40597 const double local_zeta_last = vector_bnd_vertices[i + 1][0];
40598 const double local_length_zeta =
40599 std::fabs(local_zeta_last - local_zeta_first);
40600
40601 const double x1 = vector_bnd_vertices[i - 1][1];
40602 const double y1 = vector_bnd_vertices[i - 1][2];
40603 const double x2 = vector_bnd_vertices[i + 1][1];
40604 const double y2 = vector_bnd_vertices[i + 1][2];
40605 const double local_length =
40606 sqrt(((x1 - x2) * (x1 - x2)) + ((y1 - y2) * (y1 - y2)));
40607
40608 const double x_m = vector_bnd_vertices[i][1];
40609 const double y_m = vector_bnd_vertices[i][2];
40610
40611 const double average_area_constraint =
40612 (area_constraint[i - 1] + area_constraint[i]) / 2.0;
40613
40614 // Compute the length of the the side of an equilateral
40615 // triangle
40616 const double length_side =
40618
40619 const double length_side_zeta =
40621
40622 // Is the new length greater that the old one
40623 if ((length_side_zeta / local_length_zeta) > 1.0)
40624 {
40625 // If the number of segments is zero then verify the condition for
40626 // deletion of nodes but using the condition in the default
40627 // unrefine_boundary() method. If both conditions are true then
40628 // delete the node
40629 // Maths from
40630 // http://www.cgafaq.info/wiki/Circle_Through_Three_Points
40631 double a_x = vector_bnd_vertices[i - 1][1];
40632 double a_y = vector_bnd_vertices[i - 1][2];
40633 double b_x = vector_bnd_vertices[i][1];
40634 double b_y = vector_bnd_vertices[i][2];
40635 double c_x = vector_bnd_vertices[i + 1][1];
40636 double c_y = vector_bnd_vertices[i + 1][2];
40637
40638 double a = b_x - a_x;
40639 double b = b_y - a_y;
40640 double c = c_x - a_x;
40641 double d = c_y - a_y;
40642
40643 double e = a * (a_x + b_x) + b * (a_y + b_y);
40644 double f = c * (a_x + c_x) + d * (a_y + c_y);
40645
40646 double g = 2.0 * (a * (c_y - b_y) - b * (c_x - b_x));
40647
40648 bool do_it = false;
40649 if (std::fabs(g) < 1.0e-14)
40650 {
40651 do_it = true;
40652 }
40653 else
40654 {
40655 double p_x = (d * e - b * f) / g;
40656 double p_y = (a * f - c * e) / g;
40657
40658 double r = sqrt(pow((a_x - p_x), 2) + pow((a_y - p_y), 2));
40659
40660 double rhalfca_x = 0.5 * (a_x - c_x);
40661 double rhalfca_y = 0.5 * (a_y - c_y);
40662
40663 double halfca_squared = pow(rhalfca_x, 2) + pow(rhalfca_y, 2);
40664
40665 double sticky_out_bit =
40666 r - sqrt(std::fabs((r * r) - halfca_squared));
40667
40668 // If sticky out bit divided by distance between end nodes
40669 // is less than tolerance the boundary is so flat that we
40670 // can safely kill the node
40671 if ((sticky_out_bit / (2.0 * sqrt(halfca_squared))) <
40672 unrefinement_tolerance)
40673 {
40674 do_it = true;
40675 }
40676 }
40677
40678 // If the vertex was proposed for deletion check if it is
40679 // allowed for being deleted
40681 {
40682 // Is the vertex one of the non deletable vertices
40683 for (std::set<Vector<double>>::iterator it =
40684 no_delete_vertex.begin();
40685 it != no_delete_vertex.end();
40686 it++)
40687 {
40688 // Compute the distance between the proposed node to
40689 // delete and the ones that should not be deleted
40690 const double x = (*it)[0];
40691 const double y = (*it)[1];
40692 double error = (x_m - x) * (x_m - x) + (y_m - y) * (y_m - y);
40693 error = sqrt(error);
40694
40695 if (error <
40697 {
40698 // Do not delete the vertex
40699 do_it = false;
40700 break;
40701 }
40702 }
40703
40704 } // if (do_it && boundary_receive_connections)
40705
40706 // Remove node?
40707 if (do_it)
40708 {
40709 vector_bnd_vertices[i].resize(0);
40710 }
40711 } // if (n_seg == 0)
40712 } // if (area_constraint[i] >= 0)
40713 } // for (i < n_vertex-1)
40714
40715 // Create a new (temporary) vector for the nodes, so that deleted nodes
40716 // are not stored
40718
40719 // Compact vector for target areas too
40721
40722 // Copy only the non deleted nodes
40723 for (unsigned i = 0; i < n_vertex; i++)
40724 {
40725 // If the entry was not deleted include it in the new vector
40726 if (vector_bnd_vertices[i].size() != 0)
40727 {
40729 }
40730 }
40731
40732 // ------------------------------------------------------------------
40733 // Size of the target areas vector
40734 unsigned nsize_target = area_constraint.size();
40735 if (nsize_target == 1)
40736 {
40737 // No node was deleted, just copy the target area
40739 }
40740
40741 // Copy the target areas
40742 for (unsigned i = 1; i < n_vertex; i += 2)
40743 {
40744 // If the entry was not deleted include the target areas of both
40745 // elements sharing the node
40746 if (vector_bnd_vertices[i].size() != 0)
40747 {
40749 // To catch the case when working with even number of vertex
40750 if (i < nsize_target)
40751 {
40753 }
40754 }
40755 else
40756 {
40757 // If the node was deleted then compute the new target area as the
40758 // average of the target area of the elements sharing the node
40759 double new_area_constraint =
40760 (area_constraint[i - 1] + area_constraint[i]) / 2.0;
40762 }
40763 }
40764
40765 // If the size of the compact vector is different from the size of the
40766 // vector before applying the area length constraint then the polyline
40767 // was updated
40768 if (n_vertex != compact_vector.size())
40769 {
40770 unrefinement_applied = true;
40771 }
40772
40773 // Copy back to the original vector
40776 for (unsigned i = 0; i < n_vertex; i++)
40777 {
40778 vector_bnd_vertices[i].resize(3);
40782 }
40783
40784 // Copy back to the original vector of target areas
40787 for (unsigned i = 0; i < ntarget_areas; i++)
40788 {
40790 }
40791
40792 } // if (n_vertex > 2)
40793
40794 return unrefinement_applied;
40795 }
40796
40797 //=========================================================================
40798 /// Helper function that performs the refinement process
40799 /// on the specified boundary by using the provided vertices
40800 /// representation and the associated elements target area.
40801 //=========================================================================
40802 template<class ELEMENT>
40807 double& refinement_tolerance,
40809 {
40810 // Boolean that indicates whether an actual update of the vertex
40811 // coordinates was performed
40812 bool refinement_applied = false;
40813
40814 // Return inmedately
40815 if (!Do_boundary_refinement_constrained_by_target_areas)
40816 {
40817 return refinement_applied;
40818 }
40819
40820 // Get the total number of current vertices
40821 unsigned n_vertex = vector_bnd_vertices.size();
40822
40823 // Compute a constant value
40824 const double constant_value = 4.0 / sqrt(3.0);
40825
40826 if (n_vertex > 1)
40827 {
40828 // Create a new (temporary) vector for the nodes, so that new
40829 // nodes can be stored
40831
40832 // Go through all the vertices and create points according to the
40833 // specified element area
40834 for (unsigned i = 0; i < n_vertex - 1; i++)
40835 {
40836 // Include the first node
40837 new_vector.push_back(vector_bnd_vertices[i]);
40838
40839 if (area_constraint[i] > 0)
40840 {
40842 double local_zeta_last = vector_bnd_vertices[i + 1][0];
40843 const double local_length_zeta =
40844 std::fabs(local_zeta_last - local_zeta_first);
40845
40846 // Check if need to interchange the zeta first and the zeta
40847 // last (to ensure the same order in zeta values in any two
40848 // processors)
40850 {
40851 const double tmp_zeta = local_zeta_first;
40854 }
40855
40856 const double x1 = vector_bnd_vertices[i][1];
40857 const double y1 = vector_bnd_vertices[i][2];
40858 const double x2 = vector_bnd_vertices[i + 1][1];
40859 const double y2 = vector_bnd_vertices[i + 1][2];
40860 const double local_length =
40861 sqrt(((x1 - x2) * (x1 - x2)) + ((y1 - y2) * (y1 - y2)));
40862
40863 // Compute the length in zeta units
40864 const double length_side = sqrt(constant_value * area_constraint[i]);
40865 const double length_side_zeta =
40867
40868 // How many segments should be introduced
40870
40871 // One segment initialy (the original one)
40872 unsigned n_seg = 1;
40873
40874 // How many more segments to introduce?
40875 n_seg += static_cast<unsigned>(std::floor(1.0 / n_seg_double));
40876
40877 // Are there segments to introduce? There must be at least one
40878 // segment, the original one
40879 if (n_seg > 0)
40880 {
40881 // The zeta increment
40882 double zeta_increment = (local_length_zeta) / ((double)n_seg);
40883
40885 // Create the n_seg segmets between each pair of nodes
40886 for (unsigned s = 1; s < n_seg; s++)
40887 {
40888 // Get the coordinates
40892
40893 // Create the new node
40895 new_node[0] = zeta[0];
40896 new_node[1] = vertex[0];
40897 new_node[2] = vertex[1];
40898
40899 // Include the new node
40900 new_vector.push_back(new_node);
40901
40902 } // for (s<=n_seg)
40903
40904 } // if (n_seg > 0)
40905
40906 } // if (area_constraint[i] >= 0)
40907
40908 } // for (i < n_vertex-1)
40909
40910 // Once finished all the vertices add the last node to the vector
40911 new_vector.push_back(vector_bnd_vertices[n_vertex - 1]);
40912
40913 // If the new size of the vector (including the added nodes) is
40914 // different from the size of the vector before applying the
40915 // area length constraint then the polyline was updated
40918 {
40919 refinement_applied = true;
40920 }
40921
40922 // Copy the new representation
40924 for (unsigned i = 0; i < n_vertex; i++)
40925 {
40926 vector_bnd_vertices[i].resize(3);
40930 }
40931
40932 } // if (n_vertex > 1)
40933
40934 return refinement_applied;
40935 }
40936
40937 //======================================================================
40938 /// Helper function that performs the unrefinement process
40939 /// on the specified boundary by using the provided vertices
40940 /// representation and the associated target area.
40941 /// NOTE: This is the version that applies unrefinement to shared
40942 /// boundaries
40943 //======================================================================
40944 template<class ELEMENT>
40947 const unsigned& b,
40948 const unsigned& c,
40951 {
40952 // Store the vertices not allowed for deletion
40953 std::set<Vector<double>> no_delete_vertex;
40954
40955 // Does the boundary receives connections?
40956 const bool boundary_receive_connections =
40957 this->boundary_connections(b, c, no_delete_vertex);
40958
40959 // Boolean that indicates whether an actual update of the vertex
40960 // coordinates was performed
40961 bool unrefinement_applied = false;
40962
40963 // Return inmedately
40964 if (!Do_shared_boundary_unrefinement_constrained_by_target_areas)
40965 {
40966 return unrefinement_applied;
40967 }
40968
40969 // Strategy to delete nodes:
40970
40971 // Strategy to delete nodes: Consider the target area of the
40972 // elements (e_i and e_(i+1)) sharing the i-th node (middle node),
40973 // if the number of segments to be added is equal to zero for both
40974 // elements then compute the average of both target areas and check
40975 // if the number of segments is still zero, if that holds mark the
40976 // node to be deleted. Before delete the node check whether it is in
40977 // the non_delete_vertex list. Skip the i+1-th node and go for the
40978 // (i+2)-th one, it means, increase the counter for current node by
40979 // two.
40980
40981 // Number of vertices on the boundary
40982 unsigned n_vertex = vector_bnd_vertices.size();
40983
40984 // Compute a constant value
40985 const double constant_value = 4.0 / sqrt(3.0);
40986
40987 if (n_vertex > 2)
40988 {
40989 // Go through all the vertices and delete points when the target
40990 // area indicates zero points along the boundary
40991 for (unsigned i = 1; i < n_vertex - 1; i += 2)
40992 {
40993 // Is a target area assigned to the left and right element of
40994 // the i-th node
40995 if (area_constraint[i - 1] > 0 && area_constraint[i] > 0)
40996 {
40997 // Get the vertices to the left
40998 const double x1 = vector_bnd_vertices[i - 1][0];
40999 const double y1 = vector_bnd_vertices[i - 1][1];
41000 // ... and to the right of the i-th vertex
41001 const double x2 = vector_bnd_vertices[i + 1][0];
41002 const double y2 = vector_bnd_vertices[i + 1][1];
41003
41004 // The distance
41005 const double local_length =
41006 sqrt(((x1 - x2) * (x1 - x2)) + ((y1 - y2) * (y1 - y2)));
41007
41008 // Get the middle vertex
41009 const double x_m = vector_bnd_vertices[i][0];
41010 const double y_m = vector_bnd_vertices[i][1];
41011
41012 // The average area
41013 const double average_area_constraint =
41014 (area_constraint[i - 1] + area_constraint[i]) / 2.0;
41015
41016 // Compute the base length of the triangle with
41017 // area_constraint area
41018 const double length_side =
41020
41021 // Is the new length greater than the old one
41022 if ((length_side / local_length) > 1.0)
41023 {
41024 bool do_it = true;
41025
41026 // If the vertex was proposed for deletion check that it is
41027 // allowed for being deleted
41029 {
41030 // Is the vertex one of the non deletable vertices
41031 for (std::set<Vector<double>>::iterator it =
41032 no_delete_vertex.begin();
41033 it != no_delete_vertex.end();
41034 it++)
41035 {
41036 // Compute the distance between the proposed node to delete
41037 // and the ones that should not be deleted
41038 const double x = (*it)[0];
41039 const double y = (*it)[1];
41040 double error = (x_m - x) * (x_m - x) + (y_m - y) * (y_m - y);
41041 error = sqrt(error);
41042
41043 if (error <
41045 {
41046 // Do not delete the vertex
41047 do_it = false;
41048 break;
41049 }
41050 }
41051
41052 } // if (do_it && boundary_receive_connections)
41053
41054 // Remove node?
41055 if (do_it)
41056 {
41057 vector_bnd_vertices[i].resize(0);
41058 }
41059 } // if ((local_length / length_side) <= 1.3)
41060
41061 } // if (area_constraint[i] >= 0)
41062
41063 } // for (i < n_vertex-1)
41064
41065 // Create a new (temporary) vector for the nodes, so that deleted nodes
41066 // are not stored
41068
41069 // Compact vector for target areas too
41071
41072 // Copy only the non deleted nodes
41073 for (unsigned i = 0; i < n_vertex; i++)
41074 {
41075 // If the entry was not deleted include it in the new vector
41076 if (vector_bnd_vertices[i].size() != 0)
41077 {
41079 }
41080 }
41081
41082 // ------------------------------------------------------------------
41083 // The number of target areas
41085 if (n_area_constraint == 1)
41086 {
41087 // No node could be deleted then just copy the target area
41089 }
41090
41091 // Copy the target areas
41092 for (unsigned i = 1; i < n_vertex; i += 2)
41093 {
41094 // If the entry was not deleted include the target areas of both
41095 // elements sharing the node
41096 if (vector_bnd_vertices[i].size() != 0)
41097 {
41099 // To catch the case when working with even number of vertices
41100 if (i < n_area_constraint)
41101 {
41103 }
41104 }
41105 else
41106 {
41107 // If the node was deleted then compute the new target area as the
41108 // average of the target area of the elements sharing the node
41109 const double new_area_constraint =
41110 (area_constraint[i - 1] + area_constraint[i]) / 2.0;
41112 }
41113 } // for (i < n_vertex)
41114
41115 // If the size of the compact vector is different from the size of
41116 // the vector before applying the area length constraint then the
41117 // polyline was updated
41118 if (n_vertex != compact_vector.size())
41119 {
41120 unrefinement_applied = true;
41121 }
41122
41123 // Copy back to the original vector
41126 for (unsigned i = 0; i < n_vertex; i++)
41127 {
41128 vector_bnd_vertices[i].resize(2);
41131 }
41132
41133 // Copy back to the original vector of target areas
41136 for (unsigned i = 0; i < ntarget_areas; i++)
41137 {
41139 }
41140
41141 } // if (n_vertex > 2)
41142
41143 return unrefinement_applied;
41144 }
41145
41146 //======================================================================
41147 /// Helper function that performs the refinement process
41148 /// on the specified boundary by using the provided vertices
41149 /// representation and the associated elements target area.
41150 /// NOTE: This is the version that applies refinement to shared
41151 /// boundaries
41152 //======================================================================
41153 template<class ELEMENT>
41158 {
41159 // Boolean that indicates whether an actual update of the vertex
41160 // coordinates was performed
41161 bool refinement_applied = false;
41162
41163 // Return inmedately
41164 if (!Do_shared_boundary_refinement_constrained_by_target_areas)
41165 {
41166 return refinement_applied;
41167 }
41168
41169 // Get the number of segments
41170 unsigned nsegments = vector_bnd_vertices.size() - 1;
41171
41172 // Create a new (temporary) vector for the nodes, so that new nodes
41173 // can be stored
41175
41176 // Compute a constant value
41177 const double constant_value = 4.0 / sqrt(3.0);
41178
41179 for (unsigned s = 0; s < nsegments; s++)
41180 {
41183
41184 // Initial and final point of the segment
41185 const double x1 = left_vertex[0];
41186 const double y1 = left_vertex[1];
41187 const double x2 = right_vertex[0];
41188 const double y2 = right_vertex[1];
41189
41190 // Lenght of the segment
41191 const double segment_length =
41192 sqrt(((x1 - x2) * (x1 - x2)) + ((y1 - y2) * (y1 - y2)));
41193
41194 // Compute the distance for the new segments
41195 const double new_segment_length =
41197
41198 // How many segments should be introduced
41200
41201 // One segment initialy (the original one)
41202 unsigned nseg = 1;
41203 // How many more segments to introduce?
41204 nseg += static_cast<unsigned>(std::floor(1.0 / n_seg_double));
41205
41206 // The left vertex must be always included, even though no new vertex
41207 // be added
41208 tmp_bnd_vertices.push_back(left_vertex);
41209
41210 // Are there segments to introduce? There must be at least one
41211 // segment, the original one
41212 if (nseg > 0)
41213 {
41214 // Create intermediate vertices
41215 double incrementx = (right_vertex[0] - left_vertex[0]) / (double)(nseg);
41216 double incrementy = (right_vertex[1] - left_vertex[1]) / (double)(nseg);
41217 for (unsigned i = 1; i < nseg; i++)
41218 {
41220 tmp_vertex[0] = left_vertex[0] + incrementx * i;
41221 tmp_vertex[1] = left_vertex[1] + incrementy * i;
41222 tmp_bnd_vertices.push_back(tmp_vertex);
41223 } // for (i < nseg)
41224
41225 } // if (nseg > 0)
41226
41227 } // for (s < nsegments)
41228
41229 // Add the last vertex
41230 tmp_bnd_vertices.push_back(vector_bnd_vertices[nsegments]);
41231
41232 // If the new size of the vector (including the added nodes) is
41233 // different from the size of the vector before applying the
41234 // refinement then the polyline was updated
41235 nsegments = tmp_bnd_vertices.size() - 1;
41236 if (nsegments != vector_bnd_vertices.size() - 1)
41237 {
41238 refinement_applied = true;
41239
41240 // Copy across
41241 vector_bnd_vertices.resize(nsegments + 1);
41242 for (unsigned i = 0; i < nsegments + 1; i++)
41243 {
41244 vector_bnd_vertices[i].resize(2);
41247 }
41248 }
41249
41250 return refinement_applied;
41251 }
41252
41253 //======================================================================
41254 /// Updates the polylines representation after restart
41255 //======================================================================
41256 template<class ELEMENT>
41259 {
41260 // **********************************************************************
41261 // 1) Collect the elements adjacet to the polyline boundary id and
41262 // update the polyline
41263 // **********************************************************************
41264
41265 // (1.1) Get the face mesh representation
41267 get_face_mesh_representation(polygon_pt, face_mesh_pt);
41268
41269 // (1.2) Create vertices of the polylines by using the vertices of the
41270 // FaceElements
41271 Vector<double> vertex_coord(3); // zeta,x,y
41274
41275 const unsigned n_polyline = polygon_pt->npolyline();
41276
41277 // Go for each polyline
41278 for (unsigned p = 0; p < n_polyline; p++)
41279 {
41280 // Get the MeshAsGeomObject representation just once per polyline,
41281 // this object is only used by the
41282 // refine_boundary_constrained_by_target_area() method. We get it here
41283 // to ensure that all processors (in a distributed context) get this
41284 // representation just once, and because an AllToAll MPI communication
41285 // is used in this calling
41288
41289 // Set of coordinates that are on the boundary
41290 // Set entries are ordered on first entry in vector which stores
41291 // the boundary coordinate so the vertices come out in order!
41292 std::set<Vector<double>> vertex_nodes;
41293
41294 // Vector to store the vertices, transfer the sorted vertices from the
41295 // set to this vector, --- including the z-value ---
41297
41298 // Vector to store the coordinates of the polylines, same as the
41299 // tmp_vector_vertex_node vector (after adding more nodes) but
41300 // --- without the z-value ---, used to re-generate the polylines
41302
41303#ifdef OOMPH_HAS_MPI
41304 // --------- Stuff to deal with splitted boundaries ---------- Begin -----
41305 // Set of coordinates that are on the boundary (splitted boundary version)
41306 // The first vector is used to allocate the points for each sub-boundary
41307 // Set entries are ordered on first entry in vector which stores
41308 // the boundary coordinate so the vertices come out in order!
41310
41311 // Vector to store the vertices, transfer the sorted vertices from the
41312 // set (sub_vertex_nodes) to this vector, --- including the z-value ---
41314
41315 // Vector to store the coordinates of the polylines that will represent
41316 // the splitted boundary. Used to pass the info. from sub_vertex_nodes
41317 // but --- without the z-value ---, used to generate the sub-polylines
41319 // --------- Stuff to deal with splitted boundaries ----------- End
41320 // ------
41321#endif
41322
41323 // Get the boundary id
41324 unsigned bound = polygon_pt->curve_section_pt(p)->boundary_id();
41325
41326 /// Use a vector of vector for vertices and target areas to
41327 /// deal with the cases when the boundaries are split by the
41328 /// distribution process
41329
41330 // Loop over the face elements (ordered) and add their vertices
41331 const unsigned nface_element = face_mesh_pt[p]->nelement();
41332
41333 // Store the non halo face elements, the ones from which we will
41334 // get the vertices
41336 // Map to store the index of the face element on a boundary
41337 std::map<FiniteElement*, unsigned> face_element_index_on_boundary;
41338
41339 for (unsigned ef = 0; ef < nface_element; ++ef)
41340 {
41341 FiniteElement* ele_face_pt = face_mesh_pt[p]->finite_element_pt(ef);
41342 // Skip the halo elements
41343#ifdef OOMPH_HAS_MPI
41344 if (this->is_mesh_distributed())
41345 {
41346 // Only work with non-halo elements
41347 if (ele_face_pt->is_halo())
41348 {
41349 continue;
41350 }
41351 }
41352#endif
41353 // Add the face element to the vector
41356 }
41357
41358 // Get the number of non halo face element
41360
41361 // Map to know the already sorted face elements
41362 std::map<FiniteElement*, bool> face_element_done;
41363
41364 // Number of done face elements
41365 unsigned nsorted_face_elements = 0;
41366
41367#ifdef OOMPH_HAS_MPI
41368 // Counter for sub_boundaries
41369 unsigned nsub_boundaries = 0;
41370#endif // #ifdef OOMPH_HAS_MPI
41371
41372 // Continue until all the face elements have been sorted
41373 // This while is to deal with the cases of splitted boundaries
41375 {
41376 // Get and initial face element
41378#ifdef PARANOID
41379 bool found_initial_face_element = false;
41380#endif
41381
41382 unsigned iface = 0;
41383 for (iface = 0; iface < nnon_halo_face_element; iface++)
41384 {
41386 // If not done then take it as initial face element
41388 {
41389#ifdef PARANOID
41391#endif
41393 iface++;
41394 break;
41395 }
41396 }
41397
41398#ifdef PARANOID
41400 {
41401 std::ostringstream error_message;
41402 error_message << "Could not find an initial face element for the "
41403 "current segment\n";
41404 // << "----- Possible memory leak -----\n";
41405 throw OomphLibError(
41406 error_message.str(),
41407 "RefineableTriangleMesh::update_polygon_after_restart()",
41409 }
41410#endif
41411
41412 // Local set of coordinates that are on the boundary
41413 // Set entries are ordered on first entry in vector which stores
41414 // the boundary coordinate so the vertices come out in order!
41415 std::set<Vector<double>> local_vertex_nodes;
41416
41417 // Vector to store the vertices, transfer the sorted vertices from the
41418 // set (local) to this vector (local), --- including the z-value ---
41420
41421 // ------------------------------------------------------------------
41422 // ------------------------------------------------------------------
41423 // -----------------------------------------------------------------
41424 // Add the vertices of the initial face element to the set of local
41425 // sorted vertices
41426 // -----------------------------------------------------------------
41427 unsigned nnode = ele_face_pt->nnode();
41428 // Add the left-hand node to the set:
41429 // Boundary coordinate
41431 vertex_coord[0] = bound_left[0];
41432
41433 // Actual coordinates
41434 for (unsigned i = 0; i < 2; i++)
41435 {
41436 vertex_coord[i + 1] = ele_face_pt->node_pt(0)->x(i);
41437 }
41439
41440 // Add the right-hand nodes to the set:
41441 // Boundary coordinate
41444 vertex_coord[0] = bound_right[0];
41445
41446 // Actual coordinates
41447 for (unsigned i = 0; i < 2; i++)
41448 {
41449 vertex_coord[i + 1] = ele_face_pt->node_pt(nnode - 1)->x(i);
41450 }
41452
41453 // The initial and final node on the set
41456
41457 // Mark the current face element as done
41459
41460 // ------------------------------------------------------------------
41461 // ------------------------------------------------------------------
41462 // ------------------------------------------------------------------
41463
41464 // Continue iterating if a new face element has been added to the
41465 // list
41466 bool face_element_added = false;
41467
41468 // While a new face element has been added to the set of sorted
41469 // face elements then re-iterate
41470 do
41471 {
41472 // Start from the next face elements since we have already added
41473 // the previous one as the initial face element (any previous face
41474 // element had to be added on previous iterations)
41475 for (unsigned iiface = iface; iiface < nnon_halo_face_element;
41476 iiface++)
41477 {
41478 face_element_added = false;
41481 {
41482 // Get each individual node to check if they are contiguous
41483 nnode = ele_face_pt->nnode();
41486
41488 {
41490 face_element_added = true;
41491 }
41492 else if (left_node_pt == last_node_pt)
41493 {
41495 face_element_added = true;
41496 }
41497 else if (right_node_pt == first_node_pt)
41498 {
41500 face_element_added = true;
41501 }
41502 else if (right_node_pt == last_node_pt)
41503 {
41505 face_element_added = true;
41506 }
41507
41509 {
41510 // Add the left-hand node to the set:
41511 // Boundary coordinate
41512 left_node_pt->get_coordinates_on_boundary(bound, bound_left);
41513 vertex_coord[0] = bound_left[0];
41514
41515 // Actual coordinates
41516 for (unsigned i = 0; i < 2; i++)
41517 {
41518 vertex_coord[i + 1] = left_node_pt->x(i);
41519 }
41521
41522 // Add the right-hand nodes to the set:
41523 // Boundary coordinate
41524 right_node_pt->get_coordinates_on_boundary(bound, bound_right);
41525 vertex_coord[0] = bound_right[0];
41526
41527 // Actual coordinates
41528 for (unsigned i = 0; i < 2; i++)
41529 {
41530 vertex_coord[i + 1] = right_node_pt->x(i);
41531 }
41533
41534 // Mark as done only if one of its nodes has been
41535 // added to the list
41538
41539 break;
41540 }
41541
41542 } // if (!edge_done[edge])
41543 } // for (iiedge < nedges)
41544 } while (face_element_added &&
41546
41547 // -----------------------------------------------------------------
41548 // At this point we already have a sorted set of nodes and
41549 // can be used to peform the unrefinement and refinement procedures
41550 // -----------------------------------------------------------------
41551
41552 // Get the number of nodes on the list
41553 const unsigned nlocal_nodes = local_vertex_nodes.size();
41554 // Change representation to vector for easy of handling ...
41556
41557 // Copy the vertices of the nodes
41558 unsigned counter = 0;
41559 std::set<Vector<double>>::iterator it_vertex;
41560 for (it_vertex = local_vertex_nodes.begin();
41562 it_vertex++)
41563 {
41565 local_tmp_vector_vertex_node[counter][0] = (*it_vertex)[0];
41566 local_tmp_vector_vertex_node[counter][1] = (*it_vertex)[1];
41567 local_tmp_vector_vertex_node[counter][2] = (*it_vertex)[2];
41568 counter++;
41569 }
41570
41571 // *********************************************************************
41572 // 3) Create the vertices along the boundary using the target area to
41573 // define the distance among them
41574 // *********************************************************************
41575
41576 // Clear the local containter to recover the nodes ordered using the
41577 // zeta value
41578 local_vertex_nodes.clear();
41579
41580 // At the end of each unrefinement/refinement step store the new nodes
41581 // on the set that will give rise to the vertices of the new polyline
41582 // representation
41584 for (unsigned i = 0; i < nnew_nodes; i++)
41585 {
41589 vertex_nodes.insert(vertex_coord); // Global container
41591 }
41592
41593#ifdef OOMPH_HAS_MPI
41594 if (this->is_mesh_distributed())
41595 {
41596 // Add the set of vertices for the boundary, this will help to detect
41597 // if we need to deal with sub_boundaries and sub_polylines represen.
41599 // Increase the counter for sub_boundaries
41601 }
41602#endif
41603
41604 } // while(nsorted_face_elements < nnon_halo_face_element)
41605
41606 // Now turn into vector for ease of handling...
41607 unsigned npoly_vertex = vertex_nodes.size();
41609 unsigned count = 0;
41610 std::set<Vector<double>>::iterator it;
41611 for (it = vertex_nodes.begin(); it != vertex_nodes.end(); ++it)
41612 {
41613 tmp_vector_vertex_node[count].resize(3);
41614 tmp_vector_vertex_node[count][0] = (*it)[0];
41615 tmp_vector_vertex_node[count][1] = (*it)[1];
41616 tmp_vector_vertex_node[count][2] = (*it)[2];
41617 ++count;
41618 }
41619
41620#ifdef OOMPH_HAS_MPI
41621 // --------- Stuff for the sub_boundaries ----- Begin section
41622 // ---------
41623#ifdef PARANOID
41626 {
41627 std::ostringstream error_message;
41628 error_message
41629 << "The number of found sub-boundaries and the number of counted\n"
41630 << "sub-boundaries are different:\n"
41631 << "Number of found sub-boundaries: (" << nsub_boundaries_set << ")\n"
41632 << "Number of counted sub-boundaries: (" << nsub_boundaries << ")\n";
41633 throw OomphLibError(
41634 error_message.str(),
41635 "RefineableTriangleMesh::update_polygon_after_restart()",
41637 }
41638#endif
41639
41640 // Verify if need to deal with sub_boundaries
41641 if (this->is_mesh_distributed() && nsub_boundaries > 1)
41642 {
41643 // Mark the boundary as been splitted in the partition process
41644 this->Boundary_was_splitted[bound] = true;
41645 // Resize the vector to store the info. of sub-boundaries
41647 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
41648 {
41649 // Turn info. into vector for ease of handling...
41650 const unsigned nsubpoly_vertex = sub_vertex_nodes[isub].size();
41652 unsigned subcount = 0;
41653 std::set<Vector<double>>::iterator subit;
41654 for (subit = sub_vertex_nodes[isub].begin();
41655 subit != sub_vertex_nodes[isub].end();
41656 ++subit)
41657 {
41659 sub_tmp_vector_vertex_node[isub][subcount][0] = (*subit)[0];
41660 sub_tmp_vector_vertex_node[isub][subcount][1] = (*subit)[1];
41661 sub_tmp_vector_vertex_node[isub][subcount][2] = (*subit)[2];
41662 ++subcount;
41663 }
41664 }
41665 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
41666 // --------- Stuff for the sub_boundaries ----- End section
41667 // ------------
41668#endif // OOMPH_HAS_MPI
41669
41670 // For further processing the three-dimensional vector
41671 // has to be reduced to a two-dimensional vector
41673
41674 // Resize the vector for vectices
41676 for (unsigned i = 0; i < n_vertex; i++)
41677 {
41678 vector_vertex_node[i].resize(2);
41681 }
41682
41683#ifdef OOMPH_HAS_MPI
41684 // --------- Stuff for the sub_boundaries ----- Begin section ----------
41685 // Verify if need to deal with sub_boundaries
41686 if (this->is_mesh_distributed() && nsub_boundaries > 1)
41687 {
41688 // For further processing the three-dimensional vector
41689 // has to be reduced to a two-dimensional vector
41690 // Resize the vector to store the info. of sub-boundaries
41692 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
41693 {
41694 const unsigned subn_vertex = sub_tmp_vector_vertex_node[isub].size();
41695 // Resize the vector for vectices
41697 for (unsigned i = 0; i < subn_vertex; i++)
41698 {
41699 sub_vector_vertex_node[isub][i].resize(2);
41704 }
41705 }
41706 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
41707
41708 // We already have the info. for the sub-boundaries (if necessary)
41709 // and then we can create the sub-boundaries representations to
41710 // ease the generation of the mesh by Triangle
41711
41712 // --------- Stuff for the sub_boundaries ----- End section
41713 // ------------
41714#endif // OOMPH_HAS_MPI
41715
41716 // *********************************************************************
41717 // 4) Check for contiguousness
41718 // *********************************************************************
41719#ifdef OOMPH_HAS_MPI
41720 // Only perform this checking if the mesh is not distributed
41721 // When the mesh is distributed the polylines continuity is
41722 // addressed with the sort_polylines_helper() method
41723 if (!this->is_mesh_distributed())
41724#endif
41725 {
41726 if (p > 0)
41727 {
41728 // Final end point of previous line
41730 unsigned n_prev_vertex =
41731 polygon_pt->curve_section_pt(p - 1)->nvertex();
41733 polygon_pt->polyline_pt(p - 1)->vertex_coordinate(n_prev_vertex -
41734 1);
41735
41736 unsigned prev_seg_boundary_id =
41737 polygon_pt->curve_section_pt(p - 1)->boundary_id();
41738
41739 // Find the error between the final vertex of the previous
41740 // line and the first vertex of the current line
41741 double error = 0.0;
41742 for (unsigned i = 0; i < 2; i++)
41743 {
41744 const double dist = final_vertex_of_previous_segment[i] -
41745 (*vector_vertex_node.begin())[i];
41746 error += dist * dist;
41747 }
41748 error = sqrt(error);
41749
41750 // If the error is bigger than the tolerance then
41751 // we probably need to reverse, but better check
41753 {
41754 // Find the error between the final vertex of the previous
41755 // line and the last vertex of the current line
41756 double rev_error = 0.0;
41757 for (unsigned i = 0; i < 2; i++)
41758 {
41759 const double dist = final_vertex_of_previous_segment[i] -
41760 (*--vector_vertex_node.end())[i];
41761 rev_error += dist * dist;
41762 }
41764
41765 if (rev_error >
41767 {
41768 // It could be possible that the first segment be reversed and we
41769 // did not notice it because this check does not apply for the
41770 // first segment. We can verify if the first segment is reversed
41771 // by using the vertex number 1
41772 if (p == 1)
41773 {
41774 // Initial end point of previous line
41776
41778 polygon_pt->polyline_pt(p - 1)->vertex_coordinate(0);
41779
41780 unsigned prev_seg_boundary_id =
41781 polygon_pt->curve_section_pt(p - 1)->boundary_id();
41782
41783 // Find the error between the initial vertex of the previous
41784 // line and the first vertex of the current line
41785 double error = 0.0;
41786 for (unsigned i = 0; i < 2; i++)
41787 {
41788 const double dist = initial_vertex_of_previous_segment[i] -
41789 (*vector_vertex_node.begin())[i];
41790 error += dist * dist;
41791 }
41792 error = sqrt(error); // Reversed only the previous one
41793
41794 // If the error is bigger than the tolerance then
41795 // we probably need to reverse, but better check
41796 if (error >
41798 {
41799 // Find the error between the final vertex of the previous
41800 // line and the last vertex of the current line
41801 double rev_error = 0.0;
41802 for (unsigned i = 0; i < 2; i++)
41803 {
41804 const double dist = initial_vertex_of_previous_segment[i] -
41805 (*--vector_vertex_node.end())[i];
41806 rev_error += dist * dist;
41807 }
41808 rev_error =
41809 sqrt(rev_error); // Reversed both the current one and
41810 // the previous one
41811
41812 if (rev_error >
41814 {
41815 std::ostringstream error_stream;
41817 << "The distance between the first node of the current\n"
41818 << "line segment (boundary " << bound
41819 << ") and either end of "
41820 << "the previous line segment\n"
41821 << "(boundary " << prev_seg_boundary_id
41822 << ") is bigger than "
41823 << "the desired tolerance "
41825 << ".\n"
41826 << "This suggests that the polylines defining the "
41827 "polygonal\n"
41828 << "representation are not properly ordered.\n"
41829 << "Fail on last vertex of polyline: ("
41831 << ") and\nfirst vertex of polyline (" << bound
41832 << ").\nThis should have failed when first trying to"
41833 << " construct the\npolygon.\n";
41834 throw OomphLibError(
41835 error_stream.str(),
41836 "RefineableTriangleMesh::update_polygon_after_restart()",
41838 }
41839 else
41840 {
41841 // Reverse both
41842 // Reverse the current vector to line up with the previous
41843 // one
41844 std::reverse(vector_vertex_node.begin(),
41845 vector_vertex_node.end());
41846 polygon_pt->polyline_pt(p - 1)->reverse();
41847 }
41848 }
41849 else
41850 {
41851 // Reverse the previous one
41852 polygon_pt->polyline_pt(p - 1)->reverse();
41853 }
41854
41855 } // if p == 1
41856 else
41857 {
41858 std::ostringstream error_stream;
41860 << "The distance between the first node of the current\n"
41861 << "line segment (boundary " << bound
41862 << ") and either end of "
41863 << "the previous line segment\n"
41864 << "(boundary " << prev_seg_boundary_id
41865 << ") is bigger than the "
41866 << "desired tolerance "
41868 << ".\n"
41869 << "This suggests that the polylines defining the polygonal\n"
41870 << "representation are not properly ordered.\n"
41871 << "Fail on last vertex of polyline: ("
41872 << prev_seg_boundary_id << ") and\nfirst vertex of polyline ("
41873 << bound << ").\n"
41874 << "This should have failed when first trying to construct "
41875 "the\n"
41876 << "polygon.\n";
41877 throw OomphLibError(
41878 error_stream.str(),
41879 "RefineableTriangleMesh::update_polygon_after_restart()",
41881 }
41882 }
41883 else
41884 {
41885 // Reverse the current vector to line up with the previous one
41886 std::reverse(vector_vertex_node.begin(),
41887 vector_vertex_node.end());
41888 }
41889 } // error
41890 } // p > 0
41891 } // is mesh not distributed
41892
41893 // *********************************************************************
41894 // 5) Update the polylines representation
41895 // *********************************************************************
41896 // if (applied_area_length_constraint)
41897 // If only applied when there is a change then it keeps the
41898 // previous polyline representation, it means, it does not delete
41899 // the boundaries that are not part of the domain. We must update
41900 // the boundary representation
41901 {
41903
41904 // Now update the polyline according to the new vertices
41905 // The new one representation
41908
41909 // for (unsigned h = 0; h < vector_vertex_node.size(); h++)
41910 // {
41911 // DEBP(h);
41912 // DEBP(vector_vertex_node[h][0]);
41913 // DEBP(vector_vertex_node[h][1]);
41914 // }
41915
41916 // Create a temporal "curve section" version of the recently created
41917 // polyline
41919
41920 // Tolerance below which the middle point can be deleted
41921 // (ratio of deflection to element length)
41922 double unrefinement_tolerance =
41923 polygon_pt->polyline_pt(p)->unrefinement_tolerance();
41924
41925 // Tolerance to add points
41926 double refinement_tolerance =
41927 polygon_pt->polyline_pt(p)->refinement_tolerance();
41928
41929 // Establish refinement and unrefinement tolerance
41930 tmp_polyline_pt->set_unrefinement_tolerance(unrefinement_tolerance);
41931 tmp_polyline_pt->set_refinement_tolerance(refinement_tolerance);
41932
41933 // Establish the maximum length constraint
41934 double maximum_length = polygon_pt->polyline_pt(p)->maximum_length();
41935 tmp_polyline_pt->set_maximum_length(maximum_length);
41936
41937 if (n_vertex >= 2)
41938 {
41939 // Pass the connection information from the old polyline to the
41940 // new one
41941 this->copy_connection_information(polygon_pt->polyline_pt(p),
41943 }
41944
41945 // Now update the polyline according to the new vertices but
41946 // first check if the object is allowed to delete the representation
41947 // or if it should be done by other object
41948 bool delete_it_on_destructor = false;
41949
41950 std::set<TriangleMeshCurveSection*>::iterator it =
41951 this->Free_curve_section_pt.find(polygon_pt->curve_section_pt(p));
41952
41953 if (it != this->Free_curve_section_pt.end())
41954 {
41955 this->Free_curve_section_pt.erase(it);
41956 delete polygon_pt->curve_section_pt(p);
41958 }
41959
41960 // *****************************************************************
41961 // Copying the new representation
41962 polygon_pt->curve_section_pt(p) = tmp_polyline_pt;
41963
41964 // Update the Boundary - Polyline map
41965 this->Boundary_curve_section_pt[bound] =
41966 polygon_pt->curve_section_pt(p);
41967
41969 {
41970 this->Free_curve_section_pt.insert(polygon_pt->curve_section_pt(p));
41971 }
41972
41973#ifdef OOMPH_HAS_MPI
41974 // --------- Stuff for the sub_boundaries ----- Begin section --------
41975 // Verify if need to deal with sub_boundaries
41976 if (this->is_mesh_distributed() && nsub_boundaries > 1)
41977 {
41978 // Create temporary representations for the boundaries, only to
41979 // create the mesh when calling Triangle
41980 // Clear all previous stored data
41981 this->Boundary_subpolylines[bound].clear();
41982 // Now create storage for the sub-boundaries
41983 this->Boundary_subpolylines[bound].resize(nsub_boundaries);
41984 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
41985 {
41986 // Now update the polyline according to the sub set of
41987 // vertices, set the chunk number of the polyline
41991
41992 // Add the sub-polyline to the container to represent the boundary
41993 // in parts
41994 this->Boundary_subpolylines[bound][isub] = sub_tmp_polyline_pt;
41995
41996 // No need to send the unrefinement/refinement and maximum
41997 // length constraints since these are only temporary
41998 // representations. These polylines can be deleted once the
41999 // new polygons that represent the distributed domain have
42000 // been created
42001
42002 } // for (isub < nsub_boundaries)
42003
42004 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
42005 // --------- Stuff for the sub_boundaries ----- End section
42006 // ---------
42007#endif // OOMPH_HAS_MPI
42008
42009 } // update polyline representation
42010
42011 // Delete the allocated memory for the geometric object that
42012 // represents the curvilinear boundary
42013 delete mesh_geom_obj_pt;
42014
42015 } // npolyline
42016
42017 // Cleanup the face mesh
42018 for (unsigned p = 0; p < n_polyline; p++)
42019 {
42020 face_mesh_pt[p]->flush_node_storage();
42021 delete face_mesh_pt[p];
42022 }
42023 }
42024
42025 //======================================================================
42026 /// Updates the open curve representation after restart
42027 //======================================================================
42028 template<class ELEMENT>
42031 {
42032 // **********************************************************************
42033 // 1) Get the vertices along the boundaries ids of the polylines and
42034 // update them
42035 // **********************************************************************
42036
42037 // (1.1) Get the face mesh representation
42039 get_face_mesh_representation(open_curve_pt, face_mesh_pt);
42040
42041 // (1.2) Create vertices of the polylines by using the vertices of the
42042 // FaceElements
42043 Vector<double> vertex_coord(3); // zeta,x,y
42046
42047 const unsigned ncurve_section = open_curve_pt->ncurve_section();
42048 // Go for each curve section
42049 for (unsigned cs = 0; cs < ncurve_section; cs++)
42050 {
42051 // Get the MeshAsGeomObject representation just once per polyline,
42052 // this object is only used by the
42053 // refine_boundary_constrained_by_target_area() method. We get it here
42054 // to ensure that all processors (in a distributed context) get this
42055 // representation just once, and because an AllToAll MPI communication
42056 // is used in this calling
42059
42060 // Get the boundary id
42061 const unsigned bound = open_curve_pt->curve_section_pt(cs)->boundary_id();
42062
42063 /// Use a vector of vector for vertices and target areas to deal
42064 /// with the cases when the boundaries are split bn the
42065 /// distribution process. Internal boundaries may be completely or
42066 /// partially overlapped by shared boundaries
42067
42068 // Loop over the face elements and add their vertices (they are
42069 // automatically sorted because of the set)
42070 const unsigned nface_element = face_mesh_pt[cs]->nelement();
42071 // Store the non halo elements and the element at the other side of
42072 // the boundary (whatever it be halo or not), the first will be the
42073 // ones from which we will get the vertices (in even position)
42075
42076 // Map to store the index of the face element on a boundary
42077 std::map<FiniteElement*, unsigned> face_element_index_on_boundary;
42078
42079 // Map to know the already sorted face elements
42080 std::map<FiniteElement*, bool> face_element_done;
42081
42082 for (unsigned ef = 0; ef < nface_element; ++ef)
42083 {
42084 FiniteElement* ele_face_pt = face_mesh_pt[cs]->finite_element_pt(ef);
42085
42086 // Skip the halo elements (not used as base elements, only
42087 // include those elements which one of its counterparts -- at the
42088 // other side of the boundary -- is non halo)
42089#ifdef OOMPH_HAS_MPI
42090 if (this->is_mesh_distributed())
42091 {
42092 // Only work with non-halo elements
42093 if (ele_face_pt->is_halo())
42094 {
42095 continue;
42096 }
42097 }
42098#endif
42099
42100 // Check if not already done
42102 {
42103 // Add the element and look for the element at the other side
42104 // of the boundary to add it immediately after the new added
42105 // element
42107 // Create the map of the face element with the index
42109 // Mark the current element as done
42111 // Get the number of nodes
42112 const unsigned nnodes = ele_face_pt->nnode();
42113 // Get the left and right node to look for the elements at the
42114 // other side of the boundary
42117
42118#ifdef PARANOID
42119 // Flag to know if the element at the other side of the
42120 // boundary was found
42121 bool found_other_side_face_ele = false;
42122#endif
42123 for (unsigned iface = 0; iface < nface_element; iface++)
42124 {
42125 // Get the candidate face element
42127 face_mesh_pt[cs]->finite_element_pt(iface);
42128 // Check if not already done
42130 {
42133
42134 // Check if the nodes are the same
42135 if ((left_node_pt == cleft_node_pt &&
42139 {
42140 // Add the element to the storage
42142 // ... and mark the element as done
42144 // Create the map of the face element with the index
42146#ifdef PARANOID
42147 // Set the flag of found other side face element
42149#endif
42150 break;
42151 }
42152 }
42153 } // (iface < nface_element)
42154
42155#ifdef PARANOID
42157 {
42158 std::ostringstream error_message;
42159 error_message
42160 << "The face element at the other side of the boundary (" << bound
42161 << ") was not found!!\n"
42162 << "These are the nodes of the face element:\n"
42163 << "(" << left_node_pt->x(0) << ", " << left_node_pt->x(1) << ") "
42164 << "and (" << right_node_pt->x(0) << "," << right_node_pt->x(1)
42165 << ")\n\n";
42166 throw OomphLibError(
42167 error_message.str(),
42168 "RefineableTriangleMesh::update_open_curve_after_restart()",
42170 }
42171#endif
42172 } // if (!face_ele_done[ele_face_pt])
42173
42174 } // (ef < nface_element)
42175
42176 // Clear the map of the already done face elements
42177 // This will now be used to sort the face elements
42178 face_element_done.clear();
42179
42180 // Set of coordinates that are on the boundary
42181 // The entries are sorted on first entry in vector which stores
42182 // the boundary coordinate so the vertices come out in order!
42183 std::set<Vector<double>> vertex_nodes;
42184
42185 // Vector to store the vertices, transfer the sorted vertices from the
42186 // set to this vector, --- including the z-value ---
42188
42189 // Vector to store the coordinates of the polylines, same as the
42190 // tmp_vector_vertex_node vector (after adding more nodes) but
42191 // --- without the z-value ---, used to re-generate the polylines
42193
42194#ifdef OOMPH_HAS_MPI
42195 // Indicates if the set of vertices give rise to a internal
42196 // boundary that will be used as shared boundary or as normal
42197 // internal boundary -- Only used to deal with internal boundaries
42198 // in a distributed scheme
42199 std::vector<bool> internal_to_shared_boundary;
42200
42201 // --------- Stuff to deal with splitted boundaries ---------- Begin -----
42202 // Set of coordinates that are on the boundary (splitted boundary version)
42203 // The first vector is used to allocate the points for each sub-boundary
42204 // Set entries are ordered on first entry in vector which stores
42205 // the boundary coordinate so the vertices come out in order!
42207
42208 // Vector to store the vertices, transfer the sorted vertices from the
42209 // set (sub_vertex_nodes) to this vector, --- including the z-value ---
42211
42212 // Vector to store the coordinates of the polylines that will represent
42213 // the splitted boundary. Used to pass the info. from sub_vertex_nodes
42214 // but --- without the z-value ---, used to generate the sub-polylines
42216
42217 // --------- Stuff to deal with splitted boundaries ----------- End
42218 // ------
42219#endif
42220
42221 // Sort face elements, separate those with both nonhalo face
42222 // elements from those with one halo and one nonhalo face element
42223
42224 // Number of done face elements
42225 unsigned nsorted_face_elements = 0;
42226
42227#ifdef OOMPH_HAS_MPI
42228 // Counter for sub_boundaries
42229 unsigned nsub_boundaries = 0;
42230#endif // #ifdef OOMPH_HAS_MPI
42231
42232 // Total number of non halo double face element
42233 const unsigned nnon_halo_doubled_face_ele =
42235
42236 // Continue until all the face elements have been sorted
42237 // This while is to deal with the cases of splitted boundaries
42239 {
42240 // Get and initial face element
42243#ifdef PARANOID
42244 bool found_initial_face_element = false;
42245#endif
42246
42247 // Flag to know if we are working with a face element which the
42248 // face element at the other side of the boundary is also non
42249 // halo
42251
42252 unsigned iface = 0;
42253 for (iface = 0; iface < nnon_halo_doubled_face_ele; iface += 2)
42254 {
42256 // If not done then take it as initial face element
42258 {
42259 // Mark it as done
42261 // Get the other side boundary face element
42263 // ... also mark as done the repeated face element
42265
42266#ifdef OOMPH_HAS_MPI
42268 {
42270 }
42271#endif // #ifdef OOMPH_HAS_MPI
42272
42273 // Plus two because internal boundaries have
42274 // two face elements per each edge
42276 iface += 2;
42277#ifdef PARANOID
42278 // And set the flag to true
42280#endif
42281 break;
42282 }
42283 }
42284
42285#ifdef PARANOID
42287 {
42288 std::ostringstream error_message;
42289 error_message << "Could not find an initial face element for the "
42290 "current segment\n";
42291 // << "----- Possible memory leak -----\n";
42292 throw OomphLibError(error_message.str(),
42295 }
42296#endif
42297
42298 // Local set of coordinates that are on the boundary Set entries
42299 // are ordered on first entry in vector which stores the boundary
42300 // coordinate so the vertices come out in order
42301 std::set<Vector<double>> local_vertex_nodes;
42302
42303 // Vector to store the vertices, transfer the sorted vertices from the
42304 // set (local) to this vector (local), --- including the z-value ---
42306
42307 // ------------------------------------------------------------------
42308 // ------------------------------------------------------------------
42309 // Add the vertices of the initial face element to the set of local
42310 // sorted vertices
42311 // ------------------------------------------------------------------
42312 // ------------------------------------------------------------------
42313 const unsigned nnode = ele_face_pt->nnode();
42314 // Add the left-hand node to the set:
42315 // Boundary coordinate
42317 vertex_coord[0] = bound_left[0];
42318
42319 // Actual coordinates
42320 for (unsigned i = 0; i < 2; i++)
42321 {
42322 vertex_coord[i + 1] = ele_face_pt->node_pt(0)->x(i);
42323 }
42325
42326 // Add the right-hand node to the set:
42327 // Boundary coordinate
42330 vertex_coord[0] = bound_right[0];
42331
42332 // Actual coordinates
42333 for (unsigned i = 0; i < 2; i++)
42334 {
42335 vertex_coord[i + 1] = ele_face_pt->node_pt(nnode - 1)->x(i);
42336 }
42338
42339 // The initial and final node on the set
42342
42343 // Continue iterating if a new face element has been added to the
42344 // list
42345 bool face_element_added = false;
42346
42347 // While a new face element has been added to the set of sorted
42348 // face elements then re-iterate
42349 do
42350 {
42351 // Start from the next face elements since we have already
42352 // added the previous one as the initial face element (any
42353 // previous face element had to be added on previous
42354 // iterations)
42355 for (unsigned iiface = iface; iiface < nnon_halo_doubled_face_ele;
42356 iiface += 2)
42357 {
42358 face_element_added = false;
42360
42361 // Check that the face element with which we are working has
42362 // the same conditions as the root face element (both faces
42363 // are nonhalo or one face is halo and the other nonhalo)
42364
42365 // Get the face element at the other side of the boundary
42367 bool both_face_elements_are_nonhalo = false;
42368
42369#ifdef OOMPH_HAS_MPI
42371 {
42373 }
42374#endif // #ifdef OOMPH_HAS_MPI
42375
42379 {
42380 // Get each individual node to check if they are contiguous
42381 const unsigned nlnode = ele_face_pt->nnode();
42384
42386 {
42388 face_element_added = true;
42389 }
42390 else if (left_node_pt == last_node_pt)
42391 {
42393 face_element_added = true;
42394 }
42395 else if (right_node_pt == first_node_pt)
42396 {
42398 face_element_added = true;
42399 }
42400 else if (right_node_pt == last_node_pt)
42401 {
42403 face_element_added = true;
42404 }
42405
42407 {
42408 // Add the left-hand node to the set:
42409 // Boundary coordinate
42410 left_node_pt->get_coordinates_on_boundary(bound, bound_left);
42411 vertex_coord[0] = bound_left[0];
42412
42413 // Actual coordinates
42414 for (unsigned i = 0; i < 2; i++)
42415 {
42416 vertex_coord[i + 1] = left_node_pt->x(i);
42417 }
42419
42420 // Add the right-hand nodes to the set:
42421 // Boundary coordinate
42422 right_node_pt->get_coordinates_on_boundary(bound, bound_right);
42423 vertex_coord[0] = bound_right[0];
42424
42425 // Actual coordinates
42426 for (unsigned i = 0; i < 2; i++)
42427 {
42428 vertex_coord[i + 1] = right_node_pt->x(i);
42429 }
42431
42432 // Mark as done only if one of its nodes has been
42433 // added to the list
42435 // .. also mark as done the face element at the othe side of
42436 // the boundary
42440 // ... and increase the number of sorted face elements
42442
42443 break;
42444 }
42445
42446 } // if (!face_element_done[[ele_face_pt])
42447 } // for (iiface<nnon_halo_doubled_face_ele)
42448 } while (face_element_added &&
42450
42451 // -------------------------------------------------------------
42452 // At this point we already have a sorted set of nodes and can
42453 // be used to peform the unrefinement and refinement procedures
42454 // -------------------------------------------------------------
42455
42456 // Get the number of nodes on the list
42457 const unsigned nlocal_nodes = local_vertex_nodes.size();
42458 // Change representation to vector for easy of handling ...
42460
42461 // Copy the vertices of the nodes
42462 unsigned counter = 0;
42463 std::set<Vector<double>>::iterator it_vertex;
42464 for (it_vertex = local_vertex_nodes.begin();
42466 it_vertex++)
42467 {
42469 local_tmp_vector_vertex_node[counter][0] = (*it_vertex)[0];
42470 local_tmp_vector_vertex_node[counter][1] = (*it_vertex)[1];
42471 local_tmp_vector_vertex_node[counter][2] = (*it_vertex)[2];
42472 counter++;
42473 }
42474
42475 // The unrefinement and refinement process needs to be applied
42476 // from the bottom-left node since the internal open curve could
42477 // lie on the shared boundaries
42480 {
42481 std::reverse(local_tmp_vector_vertex_node.begin(),
42483 }
42484 else if (local_tmp_vector_vertex_node[nlocal_nodes - 1][2] ==
42486 {
42489 {
42490 std::reverse(local_tmp_vector_vertex_node.begin(),
42492 }
42493 }
42494
42495 // ****************************************************************
42496 // 3) Create the vertices along the boundary using the target
42497 // area to define the distance among them
42498 // ****************************************************************
42499
42500 // Clear the local containter to recover the nodes ordered using
42501 // the zeta value
42502 local_vertex_nodes.clear();
42503
42504 // At the end of each unrefinement/refinement step store the new
42505 // nodes on the set that will give rise to the vertices of the
42506 // new polyline representation
42507 const unsigned nnew_nodes = local_tmp_vector_vertex_node.size();
42508 for (unsigned i = 0; i < nnew_nodes; i++)
42509 {
42513 vertex_nodes.insert(vertex_coord); // Global container
42515 }
42516
42517#ifdef OOMPH_HAS_MPI
42518 if (this->is_mesh_distributed())
42519 {
42520 // Add the set of vertices for the boundary, this will help to
42521 // detect if we need to deal with sub_boundaries and
42522 // sub_polylines representations
42524 // Increase the counter for sub_boundaries
42526
42527 // Mark if the polyline created by these vertices will be used
42528 // as a shared boundary or as an internal boundary
42530 {
42531 internal_to_shared_boundary.push_back(false);
42532 }
42533 else
42534 {
42535 internal_to_shared_boundary.push_back(true);
42536 }
42537 }
42538#endif
42539
42540 } // while(nsorted_face_elements < nnon_halo_doubled_face_ele)
42541 // This while is in charge of sorting all the face elements to
42542 // create the new representation of the polyline (also deals
42543 // with the sub-boundary cases)
42544
42545 // Now turn into vector for ease of handling...
42546 const unsigned npoly_vertex = vertex_nodes.size();
42548 unsigned count = 0;
42549 std::set<Vector<double>>::iterator it;
42550 for (it = vertex_nodes.begin(); it != vertex_nodes.end(); ++it)
42551 {
42552 tmp_vector_vertex_node[count].resize(3);
42553 tmp_vector_vertex_node[count][0] = (*it)[0];
42554 tmp_vector_vertex_node[count][1] = (*it)[1];
42555 tmp_vector_vertex_node[count][2] = (*it)[2];
42556 ++count;
42557 }
42558
42559#ifdef OOMPH_HAS_MPI
42560 // Check that the number of set of vertices marked to be shared or
42561 // internal boundaries be the same as the total number of
42562 // sub-boundaries
42563#ifdef PARANOID
42564 const unsigned nsub_boundaries_set = sub_vertex_nodes.size();
42565 const unsigned ninternal_to_shared_boundaries =
42568 {
42569 std::ostringstream error_message;
42570 error_message
42571 << "The number of found sub-boundaries and the number of marked "
42572 << "internal\nboundaries are different\n"
42573 << "Number of found sub-boundaries: (" << nsub_boundaries_set << ")\n"
42574 << "Number of marked internal boundaries: ("
42575 << ninternal_to_shared_boundaries << ")\n\n";
42576 throw OomphLibError(
42577 error_message.str(),
42578 "RefineableTriangleMesh::update_open_curve_after_restart()",
42580 }
42581#endif
42582
42583 // --------- Stuff for the sub_boundaries ----- Begin section -------
42584#ifdef PARANOID
42586 {
42587 std::ostringstream error_message;
42588 error_message
42589 << "The number of found sub-boundaries and the number of counted\n"
42590 << "sub-boundaries are different:\n"
42591 << "Number of found sub-boundaries: (" << nsub_boundaries_set << ")\n"
42592 << "Number of counted sub-boundaries: (" << nsub_boundaries
42593 << ")\n\n";
42594 throw OomphLibError(
42595 error_message.str(),
42596 "RefineableTriangleMesh::update_open_curve_after_restart()",
42598 }
42599#endif
42600
42601 // Verify if need to deal with sub_boundaries
42602 if (this->is_mesh_distributed() && nsub_boundaries > 1)
42603 {
42604 // Mark the boundary as been splitted in the partition process
42605 this->Boundary_was_splitted[bound] = true;
42606 // Resize the vector to store the info. of sub-boundaries
42608 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
42609 {
42610 // Turn info. into vector for ease of handling...
42611 const unsigned nsubpoly_vertex = sub_vertex_nodes[isub].size();
42613 unsigned subcount = 0;
42614 std::set<Vector<double>>::iterator subit;
42615 for (subit = sub_vertex_nodes[isub].begin();
42616 subit != sub_vertex_nodes[isub].end();
42617 ++subit)
42618 {
42620 sub_tmp_vector_vertex_node[isub][subcount][0] = (*subit)[0];
42621 sub_tmp_vector_vertex_node[isub][subcount][1] = (*subit)[1];
42622 sub_tmp_vector_vertex_node[isub][subcount][2] = (*subit)[2];
42623 ++subcount;
42624 }
42625 }
42626 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
42627 // --------- Stuff for the sub_boundaries ----- End section ----------
42628#endif // OOMPH_HAS_MPI
42629
42630 // For further processing the three-dimensional vector has to be
42631 // reduced to a two-dimensional vector
42633
42634 // Resize the vector for vectices
42636 for (unsigned i = 0; i < n_vertex; i++)
42637 {
42638 vector_vertex_node[i].resize(2);
42641 }
42642
42643#ifdef OOMPH_HAS_MPI
42644 // --------- Stuff for the sub_boundaries ----- Begin section ----------
42645 // Verify if need to deal with sub_boundaries
42646 if (this->is_mesh_distributed() && nsub_boundaries > 1)
42647 {
42648 // For further processing the three-dimensional vector
42649 // has to be reduced to a two-dimensional vector
42650 // Resize the vector to store the info. of sub-boundaries
42652 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
42653 {
42654 const unsigned subn_vertex = sub_tmp_vector_vertex_node[isub].size();
42655 // Resize the vector for vectices
42657 for (unsigned i = 0; i < subn_vertex; i++)
42658 {
42659 sub_vector_vertex_node[isub][i].resize(2);
42664 }
42665 }
42666 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
42667
42668 // We already have the info. for the sub-boundaries (if necessary) and
42669 // then we can create the sub-boundaries representations to ease the
42670 // generation of the mesh by Triangle
42671
42672 // --------- Stuff for the sub_boundaries ----- End section
42673 // ------------
42674#endif // OOMPH_HAS_MPI
42675
42676 // *********************************************************************
42677 // 4) Check for contiguousness
42678 // *********************************************************************
42679#ifdef OOMPH_HAS_MPI
42680 // Only perform this checking if the mesh is not distributed
42681 // When the mesh is distributed the polylines continuity is
42682 // addressed with the sort_polylines_helper() method
42683 if (!this->is_mesh_distributed())
42684#endif
42685 {
42686 if (cs > 0)
42687 {
42688 // Final end point of previous line
42690 unsigned n_prev_vertex =
42691 open_curve_pt->curve_section_pt(cs - 1)->nvertex();
42693 open_curve_pt->polyline_pt(cs - 1)->vertex_coordinate(
42694 n_prev_vertex - 1);
42695
42696 unsigned prev_seg_boundary_id =
42697 open_curve_pt->curve_section_pt(cs - 1)->boundary_id();
42698
42699 // Find the error between the final vertex of the previous
42700 // line and the first vertex of the current line
42701 double error = 0.0;
42702 for (unsigned i = 0; i < 2; i++)
42703 {
42704 const double dist = final_vertex_of_previous_segment[i] -
42705 (*vector_vertex_node.begin())[i];
42706 error += dist * dist;
42707 }
42708 error = sqrt(error);
42709
42710 // If the error is bigger than the tolerance then
42711 // we probably need to reverse, but better check
42713 {
42714 // Find the error between the final vertex of the previous
42715 // line and the last vertex of the current line
42716 double rev_error = 0.0;
42717 for (unsigned i = 0; i < 2; i++)
42718 {
42719 const double dist = final_vertex_of_previous_segment[i] -
42720 (*--vector_vertex_node.end())[i];
42721 rev_error += dist * dist;
42722 }
42724
42725 if (rev_error >
42727 {
42728 // It could be possible that the first segment be reversed and we
42729 // did not notice it because this check does not apply for the
42730 // first segment. We can verify if the first segment is reversed
42731 // by using the vertex number 1
42732 if (cs == 1)
42733 {
42734 // Initial end point of previous line
42736
42738 open_curve_pt->polyline_pt(cs - 1)->vertex_coordinate(0);
42739
42740 unsigned prev_seg_boundary_id =
42741 open_curve_pt->curve_section_pt(cs - 1)->boundary_id();
42742
42743 // Find the error between the initial vertex of the previous
42744 // line and the first vertex of the current line
42745 double error = 0.0;
42746 for (unsigned i = 0; i < 2; i++)
42747 {
42748 const double dist = initial_vertex_of_previous_segment[i] -
42749 (*vector_vertex_node.begin())[i];
42750 error += dist * dist;
42751 }
42752 error = sqrt(error); // Reversed only the previous one
42753
42754 // If the error is bigger than the tolerance then
42755 // we probably need to reverse, but better check
42756 if (error >
42758 {
42759 // Find the error between the final vertex of the previous
42760 // line and the last vertex of the current line
42761 double rev_error = 0.0;
42762 for (unsigned i = 0; i < 2; i++)
42763 {
42764 const double dist = initial_vertex_of_previous_segment[i] -
42765 (*--vector_vertex_node.end())[i];
42766 rev_error += dist * dist;
42767 }
42768 rev_error = sqrt(rev_error); // Reversed both the current
42769 // one and the previous one
42770
42771 if (rev_error >
42773 {
42774 std::ostringstream error_stream;
42776 << "The distance between the first node of the current\n"
42777 << "line segment (boundary " << bound
42778 << ") and either end of "
42779 << "the previous line segment\n"
42780 << "(boundary " << prev_seg_boundary_id
42781 << ") is bigger than"
42782 << " the desired tolerance "
42784 << ".\n"
42785 << "This suggests that the polylines defining the "
42786 "polygonal\n"
42787 << "representation are not properly ordered.\n"
42788 << "Fail on last vertex of polyline: ("
42790 << ") and\nfirst vertex of polyline (" << bound
42791 << ").\nThis should have failed when first trying to "
42792 << "construct the\npolygon.\n";
42793 throw OomphLibError(error_stream.str(),
42794 "RefineableTriangleMesh::update_open_"
42795 "curve_after_restart()",
42797 }
42798 else
42799 {
42800 // Reverse both
42801 // Reverse the current vector to line up with the previous
42802 // one
42803 std::reverse(vector_vertex_node.begin(),
42804 vector_vertex_node.end());
42805 open_curve_pt->polyline_pt(cs - 1)->reverse();
42806 }
42807 }
42808 else
42809 {
42810 // Reverse the previous one
42811 open_curve_pt->polyline_pt(cs - 1)->reverse();
42812 }
42813
42814 } // if (cs == 1)
42815 else
42816 {
42817 std::ostringstream error_stream;
42819 << "The distance between the first node of the current\n"
42820 << "line segment (boundary " << bound
42821 << ") and either end of "
42822 << "the previous line segment\n"
42823 << "(boundary " << prev_seg_boundary_id
42824 << ") is bigger than the "
42825 << "desired tolerance "
42827 << ".\n"
42828 << "This suggests that the polylines defining the polygonal\n"
42829 << "representation are not properly ordered.\n"
42830 << "Fail on last vertex of polyline: ("
42831 << prev_seg_boundary_id << ") and\nfirst vertex of polyline ("
42832 << bound << ").\n"
42833 << "This should have failed when first trying to construct "
42834 "the\n"
42835 << "polygon.\n";
42836 throw OomphLibError(
42837 error_stream.str(),
42838 "RefineableTriangleMesh::update_open_curve_after_restart()",
42840 }
42841 }
42842 else
42843 {
42844 // Reverse the current vector to line up with the previous one
42845 std::reverse(vector_vertex_node.begin(),
42846 vector_vertex_node.end());
42847 }
42848 } // error
42849 } // (cs > 0)
42850 } // is mesh not distributed
42851
42852 // DEBP(applied_area_length_constraint);
42853 // DEBP(p);
42854 // getchar();
42855 // *********************************************************************
42856 // 5) Update the polylines representation
42857 // *********************************************************************
42858 // if (applied_area_length_constraint)
42859 // If only applied when there is a change then it keeps the
42860 // previous polyline representation, it means, it does not delete
42861 // the boundaries that are not part of the domain. We must update
42862 // the boundary representation
42863 {
42865
42866 // Now update the polyline according to the new vertices
42867 // The new one representation
42870
42871 // Create a temporal "curve section" version of the recently created
42872 // polyline
42874
42875 // Tolerance below which the middle point can be deleted
42876 // (ratio of deflection to element length)
42877 double unrefinement_tolerance =
42878 open_curve_pt->polyline_pt(cs)->unrefinement_tolerance();
42879
42880 // Tolerance to add points
42881 double refinement_tolerance =
42882 open_curve_pt->polyline_pt(cs)->refinement_tolerance();
42883
42884 // Establish refinement and unrefinement tolerance
42885 tmp_polyline_pt->set_unrefinement_tolerance(unrefinement_tolerance);
42886 tmp_polyline_pt->set_refinement_tolerance(refinement_tolerance);
42887
42888 // Establish the maximum length constraint
42889 double maximum_length =
42890 open_curve_pt->polyline_pt(cs)->maximum_length();
42891 tmp_polyline_pt->set_maximum_length(maximum_length);
42892
42893 if (n_vertex >= 2)
42894 {
42895 // Pass the connection information from the old polyline to the
42896 // new one
42897 this->copy_connection_information(open_curve_pt->polyline_pt(cs),
42899 }
42900
42901 // Now update the polyline according to the new vertices but first
42902 // check if the object is allowed to delete the representation or
42903 // if it should be done by other object
42904 bool delete_it_on_destructor = false;
42905
42906 std::set<TriangleMeshCurveSection*>::iterator it =
42907 this->Free_curve_section_pt.find(open_curve_pt->curve_section_pt(cs));
42908
42909 if (it != this->Free_curve_section_pt.end())
42910 {
42911 this->Free_curve_section_pt.erase(it);
42912 delete open_curve_pt->curve_section_pt(cs);
42914 }
42915
42916 // *****************************************************************
42917 // Copying the new representation
42918 open_curve_pt->curve_section_pt(cs) = tmp_polyline_pt;
42919
42920 // Update the Boundary - Polyline map
42921 this->Boundary_curve_section_pt[bound] =
42922 open_curve_pt->curve_section_pt(cs);
42923
42925 {
42926 this->Free_curve_section_pt.insert(
42927 open_curve_pt->curve_section_pt(cs));
42928 }
42929
42930#ifdef OOMPH_HAS_MPI
42931
42932 // If there are not sub-boundaries mark the boundary if need to be
42933 // trated as shared or as internal boundary
42934 if (this->is_mesh_distributed() && nsub_boundaries == 1)
42935 {
42936 // Clear all previous stored data
42937 this->Boundary_marked_as_shared_boundary[bound].clear();
42938
42939 // .. and store the flag for the boundary
42940 this->Boundary_marked_as_shared_boundary[bound].push_back(
42942 }
42943 // --------- Stuff for the sub_boundaries ----- Begin section --------
42944 // Verify if need to deal with sub_boundaries
42945 else if (this->is_mesh_distributed() && nsub_boundaries > 1)
42946 {
42947 // Create temporary representations for the boundaries, only to
42948 // create the mesh when calling Triangle
42949 // Clear all previous stored data
42950 this->Boundary_subpolylines[bound].clear();
42951 // Now create storage for the sub-boundaries
42952 this->Boundary_subpolylines[bound].resize(nsub_boundaries);
42953
42954 // Clear all previous stored data
42955 this->Boundary_marked_as_shared_boundary[bound].clear();
42956 // Create storage to mark the internal boundaries as shared
42957 // boundaries
42958 this->Boundary_marked_as_shared_boundary[bound].resize(
42960 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
42961 {
42962 // Now update the polyline according to the sub set of
42963 // vertices, set the chunk number of the polyline
42967
42968 // Add the sub-polyline to the container to represent the
42969 // boundary in parts
42970 this->Boundary_subpolylines[bound][isub] = sub_tmp_polyline_pt;
42971
42972 // Copy the flag that mark the boundary as internal or as
42973 // shared bound
42974 this->Boundary_marked_as_shared_boundary[bound][isub] =
42976
42977 // No need to send the unrefinement/refinement and maximum
42978 // length constraints since these are only temporary
42979 // representations
42980 }
42981
42982 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
42983 // --------- Stuff for the sub_boundaries ----- End section
42984 // ---------
42985#endif // OOMPH_HAS_MPI
42986
42987 } // update polyline representation
42988
42989 // Delete the allocated memory for the geometric object
42990 // that represents the curvilinear boundary
42991 delete mesh_geom_obj_pt;
42992
42993 } // npolyline
42994
42995 // Cleanup the face mesh
42996 for (unsigned p = 0; p < ncurve_section; p++)
42997 {
42998 face_mesh_pt[p]->flush_node_storage();
42999 delete face_mesh_pt[p];
43000 }
43001 }
43002
43003#ifdef OOMPH_HAS_MPI
43004 //======================================================================
43005 /// Updates the shared polylines representation after restart
43006 //======================================================================
43007 template<class ELEMENT>
43010 {
43011 // Go through all the shared boundaries/polylines
43012 const unsigned npolylines = vector_polyline_pt.size();
43013 for (unsigned pp = 0; pp < npolylines; pp++)
43014 {
43015 // Get the boundary of the current polyline
43016 const unsigned b = vector_polyline_pt[pp]->boundary_id();
43017
43018 // Get the edges of the shared boundary elements that create the
43019 // shared boundary and store the shared boundary elements from where
43020 // were created
43021 std::map<std::pair<Node*, Node*>, FiniteElement*> halo_edge_element_pt;
43022 std::map<std::pair<Node*, Node*>, FiniteElement*> nonhalo_edge_element_pt;
43023
43024 // Store the nodes that define the edges
43027
43028 // Go through the shared boundary elements and store their edges
43029 const unsigned nshared_bound_ele = this->nshared_boundary_element(b);
43030 for (unsigned e = 0; e < nshared_bound_ele; e++)
43031 {
43032 // Get the shared boundary element
43033 FiniteElement* current_ele_pt = this->shared_boundary_element_pt(b, e);
43034
43035 // Get the corner nodes, the first three nodes
43039
43040 // Check if the elements is halo
43041 if (!current_ele_pt->is_halo())
43042 {
43043 // Store the edges
43046
43049
43052
43053 // Store the info. of the element used to create these edges
43054 std::pair<Node*, Node*> edge1 =
43055 std::make_pair(first_node_pt, second_node_pt);
43057
43058 std::pair<Node*, Node*> edge2 =
43059 std::make_pair(second_node_pt, third_node_pt);
43061
43062 std::pair<Node*, Node*> edge3 =
43063 std::make_pair(third_node_pt, first_node_pt);
43065 }
43066 else
43067 {
43068 // Store the edges
43071
43074
43077
43078 // Store the info. of the element used to create these edges
43079 std::pair<Node*, Node*> edge1 =
43080 std::make_pair(first_node_pt, second_node_pt);
43082
43083 std::pair<Node*, Node*> edge2 =
43084 std::make_pair(second_node_pt, third_node_pt);
43086
43087 std::pair<Node*, Node*> edge3 =
43088 std::make_pair(third_node_pt, first_node_pt);
43090 }
43091
43092 } // for (e < nshared_bound_ele)
43093
43094 // Filter the edges that give rise to a shared boundary
43095
43096 // Mark the done edges
43097 std::map<std::pair<Node*, Node*>, bool> edge_done;
43098
43099 // Storage for the edges shared by the elements
43101
43102 // Storage for the elements that created the unsorted edges (two
43103 // elements, one at each side of the shared boundary)
43105
43107 for (unsigned i = 0; i < nnonhalo_edge_nodes; i += 2)
43108 {
43112
43113 // Create the edge (both nodes that make the edge)
43114 std::pair<Node*, Node*> new_edge =
43115 std::make_pair(currenti_edge[0], currenti_edge[1]);
43116
43117 if (!edge_done[new_edge])
43118 {
43119 const unsigned nhalo_edge_nodes = halo_edge_nodes_pt.size();
43120 for (unsigned j = 0; j < nhalo_edge_nodes; j += 2)
43121 {
43125
43126 // Comparing pointer of nodes
43127 if (currenti_edge[0] == currentj_edge[0] &&
43129 {
43130 // Store the edge in the proper container
43131 unsorted_edges.push_back(new_edge);
43132
43133 // Get the elements associated with the edges
43135
43138
43141
43142 // Store the elements associated with the edge
43144
43145 // Mark the edge as done
43146 edge_done[new_edge] = true;
43147
43148 // Break the loop for (j < nedge_node)
43149 break;
43150
43151 } // equal edge
43152
43153 // Comparing pointer of nodes (reversed)
43154 else if (currenti_edge[0] == currentj_edge[1] &&
43156 {
43157 // Create the edge (both nodes that make the edge)
43158 std::pair<Node*, Node*> new_edge =
43159 std::make_pair(currenti_edge[0], currenti_edge[1]);
43160
43161 // Store the edge in the proper container
43162 unsorted_edges.push_back(new_edge);
43163
43164 // Create the (reversed) edge (both nodes that make the edge)
43165 std::pair<Node*, Node*> rev_new_edge =
43166 std::make_pair(currentj_edge[0], currentj_edge[1]);
43167
43168 // Get the elements associated with the edge
43170
43173
43176
43177 // Store the elements associated with the edge
43179
43180 // Mark the edge as done
43181 edge_done[new_edge] = true;
43182
43183 // Break the loop for (j < nedge_node)
43184 break;
43185
43186 } // if (equal edge)
43187
43188 } // for (j < nhalo_edge_nodes)
43189
43190 } // if (!edge_done[new_edge])
43191
43192 } // for (i < nnonhalo_edge_nodes)
43193
43194 // We already have the edges that make the shared boundary (and the
43195 // elements)
43196 // Sort them to create a contiguous boundary
43197
43198 // Mark the already sorted edges
43199 std::map<std::pair<Node*, Node*>, bool> edge_sorted;
43200
43201 const unsigned nunsorted_edges = unsorted_edges.size();
43202
43203#ifdef PARANOID
43204 // The number of unsorted edges must be the same as the number of
43205 // shared_boundary element / 2
43207 {
43208 std::ostringstream error_message;
43209 error_message
43210 << "The number of shared boundary elements (" << nshared_bound_ele
43211 << ") is not the double\nof the number of unsorted edges ("
43212 << nunsorted_edges << ") for the current boundary (" << b << ")\n\n";
43213 throw OomphLibError(
43214 error_message.str(),
43215 "RefineableTriangleMesh::update_shared_curve_after_restart()",
43217 }
43218#endif
43219
43220 unsigned nsorted_edges = 0;
43221
43222 // Storing for the sorting nodes extracted from the edges, and
43223 // then used to update the polyline
43224 std::list<Node*> sorted_nodes;
43225
43226 // Storing for the edges elements
43227 std::list<FiniteElement*> sorted_edges_elements_pt;
43228
43229 // Get the root edge
43230 std::pair<Node*, Node*> edge = unsorted_edges[0];
43231 nsorted_edges++;
43232
43233 // Mark edge as done
43234 edge_sorted[edge] = true;
43235
43236 // The initial and final node on the list
43237 Node* first_node_pt = edge.first;
43238 Node* last_node_pt = edge.second;
43239
43240 // Push back on the list the new edge (nodes)
43241 sorted_nodes.push_back(first_node_pt);
43242 sorted_nodes.push_back(last_node_pt);
43243
43244 // Store the elements for the current edge
43247
43248 // Iterate while the number of sorted edges be less than the number of
43249 // unsorted edges
43251 {
43252 // Flag to indicate when a node was added
43253 bool node_added = false;
43254
43255 // Start from the next edge since we have already added the
43256 // previous one as the initial edge
43257 for (unsigned iedge = 1; iedge < nunsorted_edges; iedge++)
43258 {
43260
43261 // If edge not done
43262 if (!edge_sorted[edge])
43263 {
43264 // Get each individual node
43265 Node* left_node_pt = edge.first;
43266 Node* right_node_pt = edge.second;
43267
43269 {
43270 // Push front the new node
43271 sorted_nodes.push_front(right_node_pt);
43273 node_added = true;
43274
43275 // Store the elements for the current edge
43276 sorted_edges_elements_pt.push_front(
43278 sorted_edges_elements_pt.push_front(
43280 }
43281 else if (left_node_pt == last_node_pt)
43282 {
43283 // Push back the new node
43284 sorted_nodes.push_back(right_node_pt);
43286 node_added = true;
43287
43288 // Store the elements for the current edge
43289 sorted_edges_elements_pt.push_back(
43291 sorted_edges_elements_pt.push_back(
43293 }
43294 else if (right_node_pt == first_node_pt)
43295 {
43296 // Push front the new node
43297 sorted_nodes.push_front(left_node_pt);
43299 node_added = true;
43300
43301 // Store the elements for the current edge
43302 sorted_edges_elements_pt.push_front(
43304 sorted_edges_elements_pt.push_front(
43306 }
43307 else if (right_node_pt == last_node_pt)
43308 {
43309 // Push back the new node
43310 sorted_nodes.push_back(left_node_pt);
43312 node_added = true;
43313
43314 // Store the elements for the current edge
43315 sorted_edges_elements_pt.push_back(
43317 sorted_edges_elements_pt.push_back(
43319 }
43320
43321 if (node_added)
43322 {
43323 // Mark as done only if one of its nodes has been
43324 // added to the list
43325 edge_sorted[edge] = true;
43326 nsorted_edges++;
43327
43328 // Break the for
43329 break;
43330 }
43331
43332 } // if (!edge_done[edge])
43333 } // for (iedge < nunsorted_edges)
43334 } // while (nsorted_edges < nunsorted_edges)
43335
43336 // At this point we already have a sorted list of nodes, get the
43337 // vertices from them and store them in a vector container
43338
43339 // Get the number of nodes on the list
43340 unsigned nvertex = sorted_nodes.size();
43341 // The vector to store the vertices (assign space)
43343
43344 // Copy the vertices of the nodes
43345 unsigned counter = 0;
43346 for (std::list<Node*>::iterator it_nodes = sorted_nodes.begin();
43347 it_nodes != sorted_nodes.end();
43348 it_nodes++)
43349 {
43350 polyline_vertices[counter].resize(2);
43351 polyline_vertices[counter][0] = (*it_nodes)->x(0);
43352 polyline_vertices[counter][1] = (*it_nodes)->x(1);
43353 counter++;
43354 }
43355
43356 // Before going to the unrefinement or refinement process check that
43357 // all processors start from the same vertex. Start from the bottom
43358 // left vertex
43359 if (polyline_vertices[nvertex - 1][1] < polyline_vertices[0][1])
43360 {
43361 std::reverse(polyline_vertices.begin(), polyline_vertices.end());
43362 }
43363 else if (polyline_vertices[nvertex - 1][1] == polyline_vertices[0][1])
43364 {
43365 if (polyline_vertices[nvertex - 1][0] < polyline_vertices[0][0])
43366 {
43367 std::reverse(polyline_vertices.begin(), polyline_vertices.end());
43368 }
43369 }
43370
43371 // Create the polyline associated with this edge
43374
43375 // Get the curve section representation
43376 TriangleMeshCurveSection* curve_section_pt = vector_polyline_pt[pp];
43377
43378 // Copy the connection information from the old shared polyline
43379 // to the new one
43380 this->copy_connection_information(curve_section_pt, new_polyline_pt);
43381
43382 // Now update the polyline according to the new vertices but first
43383 // check if the object is allowed to delete the representation
43384 // or if it should be done by other object
43385 bool delete_it_on_destructor = false;
43386
43387 // Establish the element as being deleted by the destructor of
43388 // the class
43389 std::set<TriangleMeshCurveSection*>::iterator it =
43390 this->Free_curve_section_pt.find(curve_section_pt);
43391
43392 if (it != this->Free_curve_section_pt.end())
43393 {
43394 this->Free_curve_section_pt.erase(it);
43395 delete curve_section_pt;
43397 }
43398
43399 // Copy the new representation
43401
43402 // Get the new curve section representation
43404
43405 // Update the Boundary - Polyline map
43406 this->Boundary_curve_section_pt[b] = new_curve_section_pt;
43407
43409 {
43410 this->Free_curve_section_pt.insert(new_curve_section_pt);
43411 }
43412
43413 } // for (pp < npoly)
43414 }
43415
43416 //===================================================================
43417 // Fill the boundary elements structures when dealing with
43418 // shared boundaries that overlap internal boundaries. Document the
43419 // number of elements on the shared boundaries that go to internal
43420 // boundaries
43421 //===================================================================
43422 template<class ELEMENT>
43424 ELEMENT>::fill_boundary_elements_and_nodes_for_internal_boundaries()
43425 {
43426 // Dummy file
43427 std::ofstream some_file;
43428 fill_boundary_elements_and_nodes_for_internal_boundaries(some_file);
43429 }
43430
43431 //===================================================================
43432 // Fill the boundary elements structures when dealing with
43433 // shared boundaries that overlap internal boundaries
43434 //===================================================================
43435 template<class ELEMENT>
43438 std::ofstream& outfile)
43439 {
43440 // Get the number of processors
43441 const unsigned nproc = this->communicator_pt()->nproc();
43442 // Get the rank of the current processor
43443 unsigned my_rank = this->communicator_pt()->my_rank();
43444
43445 // Temporal name for the shared boundary overlaps structure
43446 std::map<unsigned, unsigned> shd_bnd_over_int_bnd =
43447 this->Shared_boundary_overlaps_internal_boundary;
43448
43449 // Register the internal boundary elements that where found to be
43450 // overlapped by shared boundaries
43451 std::set<unsigned> internal_boundary_overlaped;
43452
43453 // Document the number of elements and nodes associated to the
43454 // boundaries before filling elements and nodes
43455 if (outfile.is_open())
43456 {
43457 const unsigned nbound = this->nboundary();
43458 outfile << "Number of boundaries: " << nbound << "\n\n";
43459 outfile << "Number of elements and nodes associated to each "
43460 << "boundary before\nfilling elements and nodes\n\n";
43461 for (unsigned i = 0; i < nbound; i++)
43462 {
43463 outfile << "Boundary (" << i << ") Elements ("
43464 << this->nboundary_element(i) << ") "
43465 << "Nodes (" << this->nboundary_node(i) << ")\n";
43466 }
43467 }
43468
43469 // Storage for the shared boundaries in this processor
43470 std::set<unsigned> shared_boundaries_in_this_processor;
43471
43472 // Get the shared boundaries that this processor has with other
43473 // processors
43474 for (unsigned iproc = 0; iproc < nproc; iproc++)
43475 {
43476 // Work with other processors only
43477 if (iproc != my_rank)
43478 {
43479 // Get the number of boundaries shared with the "iproc"-th processor
43481 this->nshared_boundaries(my_rank, iproc);
43482
43484 {
43485 // Get the boundaries ids shared with "iproc"-th processor
43487 bound_shared_with_iproc = this->shared_boundaries_ids(my_rank, iproc);
43488
43489 // Loop over shared boundaries with "iproc"-th processor
43490 for (unsigned bs = 0; bs < nshared_boundaries_with_iproc; bs++)
43491 {
43492 unsigned bnd_id = bound_shared_with_iproc[bs];
43493 shared_boundaries_in_this_processor.insert(bnd_id);
43494 }
43495 }
43496 }
43497 }
43498
43499 // ------------------------------------------------------------------
43500 // Copy the boundary elements and nodes from the shared boundary to
43501 // the internal boundary it overlaps
43502 // ------------------------------------------------------------------
43503 // Go through the shared boundaries that overlap internal boundaries
43504 for (std::map<unsigned, unsigned>::iterator it =
43505 shd_bnd_over_int_bnd.begin();
43506 it != shd_bnd_over_int_bnd.end();
43507 it++)
43508 {
43509 // The shared boundary id that overlaps with an internal boundary
43510 const unsigned shd_bnd_id = (*it).first;
43511 // The internal boundary overlapped by the shared boundary
43512 const unsigned int_bnd_id = (*it).second;
43513
43514 // Check if the shared boundary exist in this processor
43515 std::set<unsigned>::iterator it_set =
43516 shared_boundaries_in_this_processor.find(shd_bnd_id);
43517 if (it_set != shared_boundaries_in_this_processor.end())
43518 {
43520
43521 // -----------------------------------------------------------------
43522 // First work the nodes of the shared boundaries that should be
43523 // added to the internal boundaries
43524 const unsigned nbnd_node_shd_bnd = this->nboundary_node(shd_bnd_id);
43525
43526 // Document the number of nodes that will be passed to the internal
43527 // boundary from the current shared boundary
43528 if (outfile.is_open())
43529 {
43530 outfile << "\nPass info. from shared (" << shd_bnd_id
43531 << ") to internal (" << int_bnd_id << ")\n";
43532 outfile << "Number of shared boundary nodes: " << nbnd_node_shd_bnd
43533 << "\n";
43534 }
43535
43536 for (unsigned in = 0; in < nbnd_node_shd_bnd; in++)
43537 {
43538 // Get the boundary node
43539 Node* bnd_node_pt = this->boundary_node_pt(shd_bnd_id, in);
43540 // Add the node to the internal boundary
43541 this->add_boundary_node(int_bnd_id, bnd_node_pt);
43542 }
43543
43544 // -----------------------------------------------------------------
43545 // Second work the boundary elements
43546 // Get the number of boundary elements that should be copied to the
43547 // internal boundary
43548 const unsigned nbnd_ele_shd_bnd = this->nboundary_element(shd_bnd_id);
43549
43550 // Document the number of elements that will be passed to the
43551 // internal boundary from the current shared boundary
43552 if (outfile.is_open())
43553 {
43554 outfile << "Number of shared boundary elements: " << nbnd_ele_shd_bnd
43555 << "\n\n";
43556 }
43557
43558 // Go through the boundary elements in the shrared boundary and add
43559 // them to the boundary elements of the internal boundary
43560 for (unsigned ie = 0; ie < nbnd_ele_shd_bnd; ie++)
43561 {
43562 // Get the boundary element
43563 FiniteElement* bnd_ele_pt = this->boundary_element_pt(shd_bnd_id, ie);
43564 // Add the element to the boundary elements storage of the
43565 // internal boundary
43566 Boundary_element_pt[int_bnd_id].push_back(bnd_ele_pt);
43567 // Get the face index of the boundary
43568 int face_index = this->face_index_at_boundary(shd_bnd_id, ie);
43569 // Add the face index to the storage of the boundary
43570 Face_index_at_boundary[int_bnd_id].push_back(face_index);
43571
43572 } // for (ie < nbnd_ele_shd_bnd)
43573
43574 // If there are regions we need to fill the storage for regions too
43575 const unsigned nregions = this->nregion();
43576 if (nregions > 1)
43577 {
43578 for (unsigned ir = 0; ir < nregions; ir++)
43579 {
43580 // Get the region attribute
43581 const unsigned region_id =
43582 static_cast<unsigned>(this->Region_attribute[ir]);
43583
43584 // Loop over all elements on boundaries in region ir
43585 const unsigned nele_ir =
43586 this->nboundary_element_in_region(shd_bnd_id, region_id);
43587 for (unsigned ier = 0; ier < nele_ir; ier++)
43588 {
43589 // Get the boundary element in current region
43591 this->boundary_element_in_region_pt(shd_bnd_id, region_id, ier);
43592 // Add the boundary element to the internal boundary in the
43593 // region
43594 this->Boundary_region_element_pt[int_bnd_id][region_id].push_back(
43595 bnd_ele_pt);
43596
43597 // Get the face index of the boundary
43598 int face_index = this->face_index_at_boundary_in_region(
43600 // Add the face index to the storage of the boundary region
43601 this->Face_index_region_at_boundary[int_bnd_id][region_id]
43602 .push_back(face_index);
43603
43604 } // for (ier < nele_ir)
43605
43606 } // for (ir < nregions)
43607
43608 } // if (nregions > 1)
43609
43610 } // if (the shared boundary appears in the current processor)
43611
43612 } // for (loop over the shared bound that overlap an internal bound)
43613
43614 // Document the number of elements and nodes associated to the
43615 // boundaries after filling elements and nodes
43616 if (outfile.is_open())
43617 {
43618 const unsigned nbound = this->nboundary();
43619 outfile << "Number of boundaries: " << nbound << "\n\n";
43620 outfile << "Number of elements and nodes associated to each "
43621 << "boundary after\nfilling elements and nodes\n\n";
43622 for (unsigned i = 0; i < nbound; i++)
43623 {
43624 outfile << "Boundary (" << i << ") Elements ("
43625 << this->nboundary_element(i) << ")"
43626 << " Nodes (" << this->nboundary_node(i) << ")\n";
43627 }
43628 }
43629
43630 // ------------------------------------------------------------------
43631 // Finally, re-setup the boundary coordinates for the new nodes on
43632 // the overlaped internal boundaries
43633 // ------------------------------------------------------------------
43634 for (std::set<unsigned>::iterator it = internal_boundary_overlaped.begin();
43636 it++)
43637 {
43638 const unsigned overlaped_internal_bnd_id = (*it);
43639
43640 // Re-setup boundary coordinates
43642 overlaped_internal_bnd_id);
43643 }
43644 }
43645
43646#endif // #ifdef OOMPH_HAS_MPI
43647
43648 //======================================================================
43649 /// Move the boundary nodes onto the boundary defined by the old mesh
43650 //======================================================================
43651 template<class ELEMENT>
43653 RefineableTriangleMesh<ELEMENT>*& new_mesh_pt, const unsigned& b)
43654 {
43655 // Quick return
43656 if (!boundary_coordinate_exists(b))
43657 {
43658 return;
43659 }
43660
43661 // Firstly we set the boundary coordinates of the new nodes
43662 // In case the mapping between the geometric object's intrinsic coordinate
43663 // and the arc-length coordinate is nonlinear. This is only an
43664 // approximation, but it will ensure that the nodes that were input to
43665 // triangle will retain exactly the same boundary coordinates and then
43666 // linear interpolation is used between those values for any newly created
43667 // nodes.
43668
43669 // We need to get the boundary nodes from the boundary face
43670 // elements since the "multi_domain" methods add nodes to the
43671 // "Boundary_node_pt" structure which have no boundary coordinates
43672 // assigned
43673 std::set<Node*> tmp_boundary_node_pt;
43674 const unsigned nboundary_ele = this->nboundary_element(b);
43675 for (unsigned e = 0; e < nboundary_ele; e++)
43676 {
43677 // Get the boundary bulk element
43678 FiniteElement* bulk_ele_pt = this->boundary_element_pt(b, e);
43679#ifdef OOMPH_HAS_MPI
43680 // Only work with nonhalo elements if the mesh is distributed
43681 if (!bulk_ele_pt->is_halo())
43682 {
43683#endif
43684 // Get the face index
43685 int face_index = this->face_index_at_boundary(b, e);
43686 // Create the face element
43688 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
43689
43690 // Get the number of nodes on the face element
43691 const unsigned nnodes = face_ele_pt->nnode();
43692 for (unsigned i = 0; i < nnodes; i++)
43693 {
43694 // Get the nodes in the face elements
43696 // Add the nodes to the set of boundary nodes
43698 } // for (i < nnodes)
43699
43700 // Free the memory allocated for the face element
43701 delete face_ele_pt;
43702 face_ele_pt = 0;
43703#ifdef OOMPH_HAS_MPI
43704 } // if (!bulk_ele_pt->is_halo())
43705#endif
43706
43707 } // for (e < nboundary_ele)
43708
43709 // Get the number of boundary nodes
43710 const unsigned long n_boundary_node = tmp_boundary_node_pt.size();
43711
43712 // Quick return if there are no nodes
43713 if (n_boundary_node == 0)
43714 {
43715#ifdef OOMPH_HAS_MPI
43716 // Check if we are working with a distributed mesh
43717 if (!this->is_mesh_distributed())
43718 {
43719#endif
43720 return;
43721#ifdef OOMPH_HAS_MPI
43722 }
43723 else // The mesh is distributed !!!
43724 {
43725 // Do not forget to participate in the communication
43726 Mesh* face_mesh_pt = new Mesh();
43727 create_unsorted_face_mesh_representation(b, face_mesh_pt);
43729
43730 // Delete the allocated memory for the geometric object and face mesh
43731 delete mesh_geom_obj_pt;
43732
43733 // Flush the nodes from the face mesh to make sure we
43734 // don't delete them (the bulk mesh still needs them!)
43735 face_mesh_pt->flush_node_storage();
43736 delete face_mesh_pt;
43737 return;
43738 }
43739#endif
43740 } // if (n_boundary_node==0)
43741
43742 // Create a vector of existing boundary nodes with their boundary
43743 // coordinate as the first entry so that we can use standard sort algorithms
43746
43748 unsigned tmp_counter = 0;
43749 for (std::set<Node*>::iterator it_node = tmp_boundary_node_pt.begin();
43751 it_node++, tmp_counter++)
43752 {
43753 Node* nod_pt = (*it_node);
43754 nod_pt->get_coordinates_on_boundary(b, b_coord);
43755 node_coord[0] = b_coord[0];
43756 node_coord[1] = nod_pt->x(0);
43757 node_coord[2] = nod_pt->x(1);
43759 } // for (it_node != tmp_boundary_node_pt.end())
43760
43761 // Sort the vector
43762 std::sort(old_boundary_node.begin(), old_boundary_node.end());
43763
43764 // Set up an equivalent ordered vector for the new nodes, based on the
43765 // current coordinate which is the scaled arc-length.
43766 // Also provide storage for the original node index,
43767 // the mapped coordinate and a flag to indicate whether the mapped
43768 // coordinate has been assigned.
43769 // Get the nodes on the boundary but consider to which segment (which
43770 // may appear in a distributed mesh) they belong
43772
43773#ifdef OOMPH_HAS_MPI
43774 // Get the number of segments
43775 const unsigned nsegments = new_mesh_pt->nboundary_segment(b);
43776#else
43777 // The number of segments is one since the boundary is not split
43778 // over multiple processors
43779 const unsigned nsegments = 1;
43780#endif // #ifdef OOMPH_HAS_MPI
43781
43782#ifdef OOMPH_HAS_MPI
43783 // Get the total number of nodes on the boundary
43784 const unsigned n_new_boundary_node = new_mesh_pt->nboundary_segment_node(b);
43785
43786 // Check if we are working with a distributed mesh
43787 if (this->is_mesh_distributed())
43788 {
43789 // If that is the case we need to ensure that the new mesh has
43790 // nodes too, if that is not the case then return
43791 // Quick return if there are no nodes
43792 if (n_new_boundary_node == 0)
43793 {
43794 // Do not forget to participate in the communication
43795 Mesh* face_mesh_pt = new Mesh();
43796 create_unsorted_face_mesh_representation(b, face_mesh_pt);
43798
43799 // Delete the allocated memory for the geometric object and face mesh
43800 delete mesh_geom_obj_pt;
43801 // Flush the nodes from the face mesh to make sure we
43802 // don't delete them (the bulk mesh still needs them!)
43803 face_mesh_pt->flush_node_storage();
43804 delete face_mesh_pt;
43805 return;
43806 }
43807 }
43808#endif // #ifdef OOMPH_HAS_MPI
43809
43810 // Create a vector of boundary nodes that must be moved
43812
43813 // Go through all the segments to assign the snapped zeta coordinates
43814 // for the new nodes
43815 for (unsigned is = 0; is < nsegments; is++)
43816 {
43817#ifdef OOMPH_HAS_MPI
43818 const unsigned n_new_boundary_segment_node =
43819 new_mesh_pt->nboundary_segment_node(b, is);
43820#else
43821 const unsigned n_new_boundary_segment_node =
43822 new_mesh_pt->nboundary_node(b);
43823#endif // #ifdef OOMPH_HAS_MPI
43824
43826 // There will be six data associated with each node
43827 node_coord.resize(6, 0.0);
43828 for (unsigned n = 0; n < n_new_boundary_segment_node; n++)
43829 {
43830#ifdef OOMPH_HAS_MPI
43831 Node* nod_pt = new_mesh_pt->boundary_segment_node_pt(b, is, n);
43832#else
43833 Node* nod_pt = new_mesh_pt->boundary_node_pt(b, n);
43834#endif // #ifdef OOMPH_HAS_MPI
43835 nod_pt->get_coordinates_on_boundary(b, b_coord);
43836 node_coord[0] = b_coord[0];
43837 node_coord[1] = nod_pt->x(0);
43838 node_coord[2] = nod_pt->x(1);
43839 node_coord[3] = n;
43841 } // for (n < n_new_boundary_segment_node)
43842
43843 // Sort the new boundary nodes based on their arc-length coordinate
43844 std::sort(new_boundary_node.begin(), new_boundary_node.end());
43845
43846 // We now have two sets of nodes ordered by a coordinate that acts in the
43847 // same direction and has the same limits.
43848
43849 // Loop over the vector of new nodes and allocate exactly the same
43850 // coordinate as the old nodes at points of coincidence
43851 unsigned old_index = 0;
43852 for (unsigned n = 0; n < n_new_boundary_segment_node; ++n)
43853 {
43854 // Loop over the set of old nodes and if the x and y coordinates
43855 // coincide with the new node copy accross the new boundary coordinate
43856 for (unsigned m = old_index; m < n_boundary_node; ++m)
43857 {
43858 if ((std::fabs(old_boundary_node[m][1] - new_boundary_node[n][1]) <
43859 1.0e-14) &&
43860 (std::fabs(old_boundary_node[m][2] - new_boundary_node[n][2]) <
43861 1.0e-14))
43862 {
43863 // Store the boundary coordinate from the old mesh
43865 // Say that it has been stored
43866 new_boundary_node[n][5] = 1.0;
43867 // For efficiency, we can start the iteration from here next
43868 // time round because both vectors are ordered
43869 old_index = m;
43870 break;
43871 }
43872 }
43873 }
43874
43875 // Check that the end-points have new boundary coordinates allocated
43876#ifdef PARANOID
43877 if ((new_boundary_node[0][5] == 0.0) ||
43879 {
43880 std::ostringstream error_stream;
43882 << "New boundary coordinates not found for the first and/or last "
43883 << "nodes\n"
43884 << "on the boundary " << b << ". This should not happen because "
43885 << "these\nlimits should have been setup in the constructor\n";
43887 << "The distance between the new and old nodes is probably outside\n"
43888 << "our tolerance.\n";
43889 error_stream.precision(20);
43890 error_stream << "Old boundaries: \n";
43891 error_stream << old_boundary_node[0][1] << " "
43892 << old_boundary_node[0][2] << " : "
43893 << old_boundary_node[n_boundary_node - 1][1] << " "
43894 << old_boundary_node[n_boundary_node - 1][2] << "\n";
43895 error_stream << "New boundaries: \n"
43896 << new_boundary_node[0][1] << " "
43897 << new_boundary_node[0][2] << " : "
43899 << " "
43901 << "\n";
43903 "RefineableTriangleMesh::snap_nodes_onto_boundary()",
43905 }
43906#endif
43907
43908 // This is only true if the boundary is not splitted among the
43909 // processors
43910 if (!this->is_mesh_distributed())
43911 {
43912 // The end points should always be present, so we
43913 // can (and must) always add them in exactly
43915
43916 /// Correct!? Because assigned again below
43918 new_boundary_node[0][5] = 1.0;
43919
43923 }
43924
43925 // Now loop over the interior nodes again and
43926 // use linear interpolation to fill in any unassigned coordiantes
43927 for (unsigned n = 1; n < n_new_boundary_segment_node - 1; ++n)
43928 {
43929 // If the new boundary coordinate has NOT been allocated
43930 if (new_boundary_node[n][5] == 0.0)
43931 {
43932 // Add its (unsorted) node number to the list
43933 nodes_to_be_snapped[is].push_back(
43934 static_cast<unsigned>(new_boundary_node[n][3]));
43935
43936 // We assume that the previous nodal value has been assigned
43937 // and read out the old and new boundary coordinates
43938 double zeta_old_low = new_boundary_node[n - 1][0];
43939 double zeta_new_low = new_boundary_node[n - 1][4];
43940
43941 // Loop over the nodes above the current node until
43942 // we find the next one that has been allocated
43943 for (unsigned m = n + 1; m < n_new_boundary_segment_node; ++m)
43944 {
43945 if (new_boundary_node[m][5] == 1.0)
43946 {
43947 // Read out the old boundary coordinate
43948 double zeta_old_high = new_boundary_node[m][0];
43949 double zeta_new_high = new_boundary_node[m][4];
43950 // Use linear interpolation to assign the new boundary coordinate
43951 double frac = (new_boundary_node[n][0] - zeta_old_low) /
43953 new_boundary_node[n][4] =
43955 new_boundary_node[n][5] = 1.0;
43956 break;
43957 }
43958 }
43959 }
43960 }
43961
43962 // Loop over all the nodes and set the new boundary coordinate
43963 for (unsigned n = 0; n < n_new_boundary_segment_node; ++n)
43964 {
43965 if (new_boundary_node[n][5] == 0)
43966 {
43967 throw OomphLibError(
43968 "New boundary coordinate not assigned\n",
43969 "RefineableTriangleMesh::snap_nodes_onto_boundary()",
43971 }
43972
43973#ifdef OOMPH_HAS_MPI
43974 // get the old coordinate
43976 ->boundary_segment_node_pt(
43977 b, is, static_cast<unsigned>(new_boundary_node[n][3]))
43978 ->get_coordinates_on_boundary(b, b_coord);
43979 // Set the new coordinate
43980 b_coord[0] = new_boundary_node[n][4];
43982 ->boundary_segment_node_pt(
43983 b, is, static_cast<unsigned>(new_boundary_node[n][3]))
43984 ->set_coordinates_on_boundary(b, b_coord);
43985#else
43986 // get the old coordinate
43988 ->boundary_node_pt(b, static_cast<unsigned>(new_boundary_node[n][3]))
43989 ->get_coordinates_on_boundary(b, b_coord);
43990 // Set the new coordinate
43991 b_coord[0] = new_boundary_node[n][4];
43993 ->boundary_node_pt(b, static_cast<unsigned>(new_boundary_node[n][3]))
43994 ->set_coordinates_on_boundary(b, b_coord);
43995#endif // #ifdef OOMPH_HAS_MPI
43996 }
43997
43998 } // for (is < nsegments)
43999
44000 Mesh* face_mesh_pt = new Mesh();
44001 create_unsorted_face_mesh_representation(b, face_mesh_pt);
44002
44003 // Now that the coordinates have been set up we can do the snapping
44005
44006 // Now assign the new nodes positions based on the old meshes
44007 // potentially curvilinear boundary (its geom object incarnation)
44009
44010 // Loop over the nodes that need to be snapped
44011 for (unsigned is = 0; is < nsegments; is++)
44012 {
44013 const unsigned nnodes_to_snap = nodes_to_be_snapped[is].size();
44014
44015 for (unsigned in = 0; in < nnodes_to_snap; in++)
44016 {
44017 // Read out the boundary node number
44018 unsigned n = nodes_to_be_snapped[is][in];
44019#ifdef OOMPH_HAS_MPI
44020 // Get the boundary coordinate of all new nodes
44021 Node* const nod_pt = new_mesh_pt->boundary_segment_node_pt(b, is, n);
44022#else
44023 // Get the boundary coordinate of all new nodes
44024 Node* const nod_pt = new_mesh_pt->boundary_node_pt(b, n);
44025#endif // #ifdef OOMPH_HAS_MPI
44026
44027 nod_pt->get_coordinates_on_boundary(b, b_coord);
44028 // Let's find boundary coordinates of the new node
44030
44031 // Now snap to the boundary
44032 for (unsigned i = 0; i < 2; i++)
44033 {
44034 nod_pt->x(i) = new_x[i];
44035 }
44036 }
44037 }
44038
44039 // Delete the allocated memory for the geometric object and face mesh
44040 delete mesh_geom_obj_pt;
44041 // Flush the nodes from the face mesh to make sure we
44042 // don't delete them (the bulk mesh still needs them!)
44043 face_mesh_pt->flush_node_storage();
44044 delete face_mesh_pt;
44045
44046 // Fix up the elements adjacent to the boundary
44047
44048 // Dummy six node element for sorting out bubble node for
44049 // seven node enriched quadratic triangles
44051 for (unsigned j = 0; j < 6; j++)
44052 {
44054 }
44055
44056 // This should definitely become a triangular element member function
44057 // Loop over elements
44058 unsigned n_bound_el = new_mesh_pt->nboundary_element(b);
44059 for (unsigned e = 0; e < n_bound_el; e++)
44060 {
44061 FiniteElement* el_pt = new_mesh_pt->boundary_element_pt(b, e);
44062
44063 // Deal with different numbers of nodes separately
44064 unsigned nnod = el_pt->nnode();
44065
44066 // #ifdef PARANOID
44067 // // Flag to indicate if we successully classified/dealt with the
44068 // element bool success=false;
44069 // #endif
44070
44071 // Simplex element: Nothing to be done other than error checking
44072 if (nnod == 3)
44073 {
44074#ifdef PARANOID
44075 // Try to cast to a simplex element
44076 TElement<2, 2>* t_el_pt = dynamic_cast<TElement<2, 2>*>(el_pt);
44077 if (t_el_pt == 0)
44078 {
44079 throw OomphLibError(
44080 "Have a three-noded element that's not a TElement<2,2>",
44083 }
44084 // If I get there I must not have thrown :)
44085 // success=true;
44086#endif
44087 }
44088 // Quadratic element (or enriched quadratic)
44089
44090 else if ((nnod == 6) || (nnod == 7))
44091 {
44092#ifdef PARANOID
44093 // Try to cast to a quadratic element
44094 TElement<2, 3>* t_el_pt = dynamic_cast<TElement<2, 3>*>(el_pt);
44095 if (t_el_pt == 0)
44096 {
44097 if (nnod == 6)
44098 {
44099 throw OomphLibError(
44100 "Have a six-noded element that's not a TElement<2,3>",
44103 }
44104 else
44105 {
44106 throw OomphLibError(
44107 "Have a seven-noded element that's not a TElement<2,3>",
44110 }
44111 }
44112 // If I get there I must not have thrown :)
44113 // success=true;
44114#endif
44115 // Deal with six noded stuff for all (normal and enriched) elements
44116
44117 /// ----------------------------------------------------------------
44118 /// Repositioning of mid-side nodes
44119 /// ----------------------------------------------------------------
44120
44121 // Side between 0 and 1
44122 if (el_pt->node_pt(3)->is_on_boundary(b))
44123 {
44124 // Make sure that the node I'm about to move is NOT on
44125 // a boundary
44126 if (!el_pt->node_pt(5)->is_on_boundary())
44127 {
44128 // Reset the internal nodes
44129 for (unsigned i = 0; i < 2; i++)
44130 {
44131 el_pt->node_pt(5)->x(i) =
44132 0.5 * (el_pt->node_pt(0)->x(i) + el_pt->node_pt(2)->x(i));
44133 }
44134 }
44135 // Make sure that the node I'm about to move is NOT on
44136 // a boundary
44137 if (!el_pt->node_pt(4)->is_on_boundary())
44138 {
44139 // Reset the internal nodes
44140 for (unsigned i = 0; i < 2; i++)
44141 {
44142 el_pt->node_pt(4)->x(i) =
44143 0.5 * (el_pt->node_pt(1)->x(i) + el_pt->node_pt(2)->x(i));
44144 }
44145 }
44146 }
44147
44148 // Side between 1 and 2
44149 if (el_pt->node_pt(4)->is_on_boundary(b))
44150 {
44151 // Make sure that the node I'm about to move is NOT on
44152 // a boundary
44153 if (!el_pt->node_pt(5)->is_on_boundary())
44154 {
44155 // Reset the internal nodes
44156 for (unsigned i = 0; i < 2; i++)
44157 {
44158 el_pt->node_pt(5)->x(i) =
44159 0.5 * (el_pt->node_pt(0)->x(i) + el_pt->node_pt(2)->x(i));
44160 }
44161 }
44162 // Make sure that the node I'm about to move is NOT on
44163 // a boundary
44164 if (!el_pt->node_pt(3)->is_on_boundary())
44165 {
44166 // Reset the internal nodes
44167 for (unsigned i = 0; i < 2; i++)
44168 {
44169 el_pt->node_pt(3)->x(i) =
44170 0.5 * (el_pt->node_pt(0)->x(i) + el_pt->node_pt(1)->x(i));
44171 }
44172 }
44173 }
44174
44175 // Side between 0 and 2
44176 if (el_pt->node_pt(5)->is_on_boundary(b))
44177 {
44178 // Make sure that the node I'm about to move is NOT on
44179 // a boundary
44180 if (!el_pt->node_pt(4)->is_on_boundary())
44181 {
44182 // Reset the internal nodes
44183 for (unsigned i = 0; i < 2; i++)
44184 {
44185 el_pt->node_pt(4)->x(i) =
44186 0.5 * (el_pt->node_pt(1)->x(i) + el_pt->node_pt(2)->x(i));
44187 }
44188 }
44189 // Make sure that the node I'm about to move is NOT on
44190 // a boundary
44191 if (!el_pt->node_pt(3)->is_on_boundary())
44192 {
44193 // Reset the internal nodes
44194 for (unsigned i = 0; i < 2; i++)
44195 {
44196 el_pt->node_pt(3)->x(i) =
44197 0.5 * (el_pt->node_pt(0)->x(i) + el_pt->node_pt(1)->x(i));
44198 }
44199 }
44200 }
44201
44202 // If it's seven noded it's likely to be an enriched one: Deal with
44203 // the central (bubble) node
44204 if (nnod == 7)
44205 {
44206 // Try to cast to an enriched quadratic element
44208 dynamic_cast<TBubbleEnrichedElement<2, 3>*>(el_pt);
44209 if (t_el_pt == 0)
44210 {
44211 throw OomphLibError("Have seven-noded element that's not a "
44212 "TBubbleEnrichedElement<2,3>",
44215 }
44216
44217 // Assign the new non-bubble coordinates to the six noded dummy
44218 // element
44219 for (unsigned j = 0; j < 6; j++)
44220 {
44221 for (unsigned i = 0; i < 2; i++)
44222 {
44224 }
44225 }
44226
44227 // Local coordinate of enriched node
44228 unsigned j_enriched = 6;
44229 Vector<double> s(2);
44231
44232 // Get its position from non-enriched element
44233 Vector<double> x(2);
44235 el_pt->node_pt(j_enriched)->x(0) = x[0];
44236 el_pt->node_pt(j_enriched)->x(1) = x[1];
44237 }
44238 }
44239 // Any other case cannot be dealt with at the moment
44240
44241 else
44242 {
44243 std::ostringstream error_stream;
44244 error_stream << "Cannot deal with this particular " << nnod
44245 << "-noded element yet.\n"
44246 << "Please implement this yourself.\n";
44247 throw OomphLibError(
44249 }
44250 }
44251
44252 // Cleanup
44253 for (unsigned j = 0; j < 6; j++)
44254 {
44256 }
44257 }
44258
44259#endif // #ifdef OOMPH_HAS_TRIANGLE_LIB
44260
44261} // namespace oomph
44262
44263#endif
e
Definition cfortran.h:571
static char t char * s
Definition cfortran.h:568
cstr elem_len * i
Definition cfortran.h:603
char t
Definition cfortran.h:568
CGAL-based SamplePointContainer.
NonRefineableBinArray class.
const Vector< double > & diff() const
Vector of diffusion coefficients.
Algebraic meshes contain AlgebraicElements and AlgebraicNodes. They implement the node update functio...
Algebraic nodes are nodes with an algebraic positional update function.
A class that contains the information required by Nodes that are located on Mesh boundaries....
Definition nodes.h:1996
Helper object for dealing with the parameters used for the CGALSamplePointContainer objects.
A class that represents a collection of data; each Data object may contain many different individual ...
Definition nodes.h:86
Information for documentation of results: Directory and file number to enable output in the form RESL...
A general Finite Element class.
Definition elements.h:1317
Integral *const & integral_pt() const
Return the pointer to the integration scheme (const version)
Definition elements.h:1967
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
virtual Node * construct_node(const unsigned &n)
Construct the local node n and return a pointer to the newly created node object.
Definition elements.h:2513
virtual double interpolated_x(const Vector< double > &s, const unsigned &i) const
Return FE interpolated coordinate x[i] at local coordinate s.
Definition elements.cc:3992
unsigned 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
Node ** Node_pt
Storage for pointers to the nodes in the element.
Definition elements.h:1323
virtual Node * construct_boundary_node(const unsigned &n)
Construct the local node n as a boundary node; that is a node that MAY be placed on a mesh boundary a...
Definition elements.h:2542
Node *& node_pt(const unsigned &n)
Return a pointer to the local node n.
Definition elements.h:2179
virtual unsigned nnode_1d() const
Return the number of nodes along one edge of the element Default is to return zero — must be overload...
Definition elements.h:2222
A Generalised Element class.
Definition elements.h:73
bool is_halo() const
Is this element a halo?
Definition elements.h:1150
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
bool must_be_kept_as_halo() const
Test whether the element must be kept as a halo element.
Definition elements.h:1176
Generalised timestepper that can serve a variety of purposes in continuation, bifurcation detection a...
A geometric object is an object that provides a parametrised description of its shape via the functio...
unsigned ndim() const
Access function to # of Eulerian coordinates.
TimeStepper *& time_stepper_pt()
Access function for pointer to time stepper: Null if object is not time-dependent.
virtual double knot(const unsigned &i, const unsigned &j) const =0
Return local coordinate s[j] of i-th integration point.
virtual unsigned nweight() const =0
Return the number of integration points of the scheme.
MacroElementNodeUpdateMeshes contain MacroElementNodeUpdateNodes which have their own node update fun...
MacroElementNodeUpdate nodes are nodes with a positional update function, based on their element's Ma...
This class provides a GeomObject representation of a given finite element mesh. The Lagrangian coordi...
A general mesh class.
Definition mesh.h:67
Nodes are derived from Data, but, in addition, have a definite (Eulerian) position in a space of a gi...
Definition nodes.h:906
virtual bool is_on_boundary() const
Test whether the Node lies on a boundary. The "bulk" Node cannot lie on a boundary,...
Definition nodes.h:1373
void set_obsolete()
Mark node as obsolete.
Definition nodes.h:1436
void set_non_obsolete()
Mark node as non-obsolete.
Definition nodes.h:1442
virtual void set_coordinates_on_boundary(const unsigned &b, const unsigned &k, const Vector< double > &boundary_zeta)
Set the vector of the k-th generalised boundary coordinates on mesh boundary b. Broken virtual interf...
Definition nodes.cc:2394
double & x(const unsigned &i)
Return the i-th nodal coordinate.
Definition nodes.h:1060
virtual void get_coordinates_on_boundary(const unsigned &b, const unsigned &k, Vector< double > &boundary_zeta)
Return the vector of the k-th generalised boundary coordinates on mesh boundary b....
Definition nodes.cc:2379
void resize(const unsigned &n_value)
Resize the number of equations.
Definition nodes.cc:2167
Helper object for dealing with the parameters used for the NonRefineableBinArray objects.
An oomph-lib wrapper to the MPI_Comm communicator object. Just contains an MPI_Comm object (which is ...
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....
Unstructured refineable Triangle Mesh.
void construct_new_halo_node_helper(Node *&new_nod_pt, Vector< Node * > &new_nodes_on_domain, Vector< Vector< Vector< std::map< unsigned, Node * > > > > &other_proc_shd_bnd_node_pt, unsigned &iproc, unsigned &node_index, FiniteElement *const &new_el_pt, Vector< Vector< Vector< unsigned > > > &global_node_names, std::map< Vector< unsigned >, unsigned > &node_name_to_global_index, Vector< Node * > &global_shared_node_pt)
Helper function which constructs a new halo node (on an element) with the information sent from the h...
void add_received_node_load_balance_helper(Node *&new_nod_pt, Vector< Vector< FiniteElement * > > &f_haloed_ele_pt, Vector< Vector< std::map< unsigned, FiniteElement * > > > &received_old_haloed_element_pt, Vector< Node * > &new_nodes_on_domain, Vector< Vector< Vector< std::map< unsigned, Node * > > > > &other_proc_shd_bnd_node_pt, unsigned &iproc, unsigned &node_index, FiniteElement *const &new_el_pt, Vector< Vector< Vector< unsigned > > > &global_node_names, std::map< Vector< unsigned >, unsigned > &node_name_to_global_index, Vector< Node * > &global_shared_node_pt)
Helper function to add a new node from load balance.
bool update_shared_curve_using_elements_area(Vector< TriangleMeshPolyLine * > &vector_polyline_pt, const Vector< double > &target_areas)
Updates the polylines using the elements area as constraint for the number of points along the bounda...
bool get_connected_vertex_number_on_dst_boundary(Vector< double > &vertex_coordinates, const unsigned &dst_b_id, unsigned &vertex_number)
Computes the associated vertex number on the destination boundary.
void send_boundary_node_info_of_shared_nodes(Vector< Vector< Vector< unsigned > > > &global_node_names, std::map< Vector< unsigned >, unsigned > &node_name_to_global_index, Vector< Node * > &global_shared_node_pt)
Get the original boundaries to which is associated each shared node, and send the info....
void update_shared_curve_after_restart(Vector< TriangleMeshPolyLine * > &vector_polyline_pt)
Updates the shared polylines representation after restart.
void send_and_receive_elements_nodes_info(int &send_proc, int &recv_proc)
Helper function to send back halo and haloed information.
void add_halo_node_helper(Node *&new_nod_pt, Vector< Node * > &new_nodes_on_domain, Vector< Vector< Vector< std::map< unsigned, Node * > > > > &other_proc_shd_bnd_node_pt, unsigned &iproc, unsigned &node_index, FiniteElement *const &new_el_pt, Vector< Vector< Vector< unsigned > > > &global_node_names, std::map< Vector< unsigned >, unsigned > &node_name_to_global_index, Vector< Node * > &global_shared_node_pt)
Helper function to add halo node.
void snap_nodes_onto_boundary(RefineableTriangleMesh< ELEMENT > *&new_mesh_pt, const unsigned &b)
Snap the boundary nodes onto any curvilinear boundaries.
void reset_shared_boundary_elements_and_nodes(const bool flush_elements=true, const bool update_elements=true, const bool flush_nodes=true, const bool update_nodes=true)
Re-establish the shared boundary elements after the adaptation process (the updating of shared nodes ...
bool unrefine_shared_boundary_constrained_by_target_area(const unsigned &b, const unsigned &c, Vector< Vector< double > > &vector_bnd_vertices, Vector< double > &area_constraint)
Helper function that performs the unrefinement process on the specified boundary by using the provide...
void get_boundary_segment_nodes_helper(const unsigned &b, Vector< Vector< Node * > > &tmp_segment_nodes)
Get the nodes on the boundary (b), these are stored in the segment they belong (also used by the load...
bool unrefine_boundary(const unsigned &b, const unsigned &c, Vector< Vector< double > > &vector_bnd_vertices, double &unrefinement_tolerance, const bool &check_only=false)
Helper function that performs the unrefinement process.
void get_face_mesh_representation(TriangleMeshPolygon *polygon_pt, Vector< Mesh * > &face_mesh_pt)
Helper function to construct face mesh representation of all polylines, possibly with segments re-dis...
void add_non_delete_vertices_from_boundary_helper(Vector< Vector< Node * > > src_bound_segment_node_pt, Vector< Vector< Node * > > dst_bound_segment_node_pt, const unsigned &dst_bnd_id, const unsigned &dst_bnd_chunk)
Adds the vertices from the sources boundary that are repeated in the destination boundary to the list...
void add_node_load_balance_helper(unsigned &iproc, Vector< Vector< FiniteElement * > > &f_halo_ele_pt, Vector< Node * > &new_nodes_on_domain, Node *nod_pt)
Helper function to add haloed node.
void compute_shared_node_degree_helper(Vector< Vector< FiniteElement * > > &unsorted_face_ele_pt, std::map< Node *, unsigned > &global_node_degree)
Computes the degree of the nodes on the shared boundaries, the degree of the node is computed from th...
void resume_boundary_connections(Vector< TriangleMeshPolyLine * > &resume_initial_connection_polyline_pt, Vector< TriangleMeshPolyLine * > &resume_final_connection_polyline_pt)
Resume the boundary connections that may have been suspended because the destination boundary is no p...
void get_required_elemental_information_load_balance_helper(unsigned &iproc, Vector< Vector< FiniteElement * > > &f_haloed_ele_pt, FiniteElement *ele_pt)
Helper function to get the required elemental information from the element to be sent....
void sort_nodes_on_shared_boundaries()
Sort the nodes on shared boundaries so that the processors that share a boundary agree with the order...
bool update_open_curve_using_elements_area(TriangleMeshOpenCurve *&open_curve_pt, const Vector< double > &target_area)
Updates the open curve but using the elements area instead of the default refinement and unrefinement...
void construct_new_node_load_balance_helper(Node *&new_nod_pt, Vector< Vector< FiniteElement * > > &f_haloed_ele_pt, Vector< Vector< std::map< unsigned, FiniteElement * > > > &received_old_haloed_element_pt, Vector< Node * > &new_nodes_on_domain, Vector< Vector< Vector< std::map< unsigned, Node * > > > > &other_proc_shd_bnd_node_pt, unsigned &iproc, unsigned &node_index, FiniteElement *const &new_el_pt, Vector< Vector< Vector< unsigned > > > &global_node_names, std::map< Vector< unsigned >, unsigned > &node_name_to_global_index, Vector< Node * > &global_shared_node_pt)
Helper function which constructs a new node (on an element) with the information sent from the load b...
void create_halo_element(unsigned &iproc, Vector< Node * > &new_nodes_on_domain, Vector< Vector< Vector< std::map< unsigned, Node * > > > > &other_proc_shd_bnd_node_pt, Vector< Vector< Vector< unsigned > > > &global_node_names, std::map< Vector< unsigned >, unsigned > &node_name_to_global_index, Vector< Node * > &global_shared_node_pt)
Helper function to create (halo) elements on the loop process based on the info received in send_and_...
void get_shared_boundary_elements_and_face_indexes(const Vector< FiniteElement * > &first_element_pt, const Vector< FiniteElement * > &second_element_pt, Vector< FiniteElement * > &first_shared_boundary_element_pt, Vector< unsigned > &first_shared_boundary_element_face_index, Vector< FiniteElement * > &second_shared_boundary_element_pt, Vector< unsigned > &second_shared_boundary_element_face_index)
Use the first and second group of elements to find the intersection between them to get the shared bo...
void create_sorted_face_mesh_representation(const unsigned &boundary_id, Mesh *face_mesh_pt, std::map< FiniteElement *, bool > &is_inverted, bool &inverted_face_mesh)
Helper function Creates a sorted face mesh representation of the specified PolyLine It means that the...
void update_open_curve_after_restart(TriangleMeshOpenCurve *&open_curve_pt)
Updates the open curve representation after restart.
void reset_halo_haloed_scheme()
In charge of. re-establish the halo(ed) scheme on all processors. Sends info. to create halo elements...
bool refine_boundary(Mesh *face_mesh_pt, Vector< Vector< double > > &vector_bnd_vertices, double &refinement_tolerance, const bool &check_only=false)
Helper function that performs the refinement process on the specified boundary by using the provided ...
void create_element_load_balance_helper(unsigned &iproc, Vector< Vector< FiniteElement * > > &f_haloed_ele_pt, Vector< Vector< std::map< unsigned, FiniteElement * > > > &received_old_haloed_element_pt, Vector< FiniteElement * > &new_elements_on_domain, Vector< Node * > &new_nodes_on_domain, Vector< Vector< Vector< std::map< unsigned, Node * > > > > &other_proc_shd_bnd_node_pt, Vector< Vector< Vector< unsigned > > > &global_node_names, std::map< Vector< unsigned >, unsigned > &node_name_to_global_index, Vector< Node * > &global_shared_node_pt)
Helper function to create elements on the loop process based on the info received in send_and_receive...
void create_new_shared_boundaries(std::set< FiniteElement * > &element_in_processor_pt, Vector< Vector< FiniteElement * > > &new_shared_boundary_element_pt, Vector< Vector< unsigned > > &new_shared_boundary_element_face_index)
Creates the new shared boundaries, this method is also in charge of computing the shared boundaries i...
void get_shared_boundary_segment_nodes_helper(const unsigned &shd_bnd_id, Vector< Vector< Node * > > &tmp_segment_nodes)
Get the nodes on the shared boundary (b), these are stored in the segment they belong.
void refine_triangulateio(TriangulateIO &triangulate_io, const Vector< double > &target_area, TriangulateIO &triangle_refine)
Build a new TriangulateIO object from previous TriangulateIO based on target area for each element.
void load_balance(const Vector< unsigned > &input_target_domain_for_local_non_halo_element)
Performs the load balancing for unstructured meshes, the load balancing strategy is based on mesh mig...
void create_unsorted_face_mesh_representation(const unsigned &boundary_id, Mesh *face_mesh_pt)
Helper function Creates an unsorted face mesh representation from the specified boundary id....
void add_element_load_balance_helper(const unsigned &iproc, Vector< Vector< std::map< unsigned, FiniteElement * > > > &received_old_haloed_element_pt, FiniteElement *ele_pt)
Helper function to create elements on the loop process based on the info received in send_and_receive...
void restore_boundary_connections(Vector< TriangleMeshPolyLine * > &resume_initial_connection_polyline_pt, Vector< TriangleMeshPolyLine * > &resume_final_connection_polyline_pt)
After unrefinement and refinement has taken place compute the new vertices numbers of the boundaries ...
bool update_open_curve_using_face_mesh(TriangleMeshOpenCurve *open_polyline_pt, const bool &check_only=false)
Helper function that updates the input open curve by using end-points of elements from FaceMesh(es) t...
bool apply_max_length_constraint(Mesh *face_mesh_pt, Vector< Vector< double > > &vector_bnd_vertices, double &max_length_constraint)
bool refine_boundary_constrained_by_target_area(MeshAsGeomObject *mesh_geom_obj_pt, Vector< Vector< double > > &vector_bnd_vertices, double &refinement_tolerance, Vector< double > &area_constraint)
Helper function that performs the refinement process on the specified boundary by using the provided ...
void create_temporary_boundary_connections(Vector< TriangleMeshPolygon * > &tmp_outer_polygons_pt, Vector< TriangleMeshOpenCurve * > &tmp_open_curves_pt)
After unrefinement and refinement has taken place compute the new vertices numbers of the temporary r...
void add_haloed_node_helper(unsigned &iproc, Node *nod_pt)
Helper function to add haloed node.
void get_required_nodal_information_helper(unsigned &iproc, Node *nod_pt)
Helper function to get the required nodal information from a haloed node so that a fully-functional h...
void compute_global_node_names_and_shared_nodes(Vector< Vector< Vector< std::map< unsigned, Node * > > > > &other_proc_shd_bnd_node_pt, Vector< Vector< Vector< unsigned > > > &global_node_names, std::map< Vector< unsigned >, unsigned > &node_name_to_global_index, Vector< Node * > &global_shared_node_pt)
Compute the names of the nodes on shared boundaries in this (my_rank) processor with other processors...
void reset_halo_haloed_scheme_helper(Vector< Vector< Vector< std::map< unsigned, Node * > > > > &other_proc_shd_bnd_node_pt, Vector< Vector< Node * > > &iproc_currently_created_nodes_pt, Vector< Vector< Vector< unsigned > > > &global_node_names, std::map< Vector< unsigned >, unsigned > &node_name_to_global_index, Vector< Node * > &global_shared_node_pt)
In charge of creating additional halo(ed) elements on those processors that have no shared boundaries...
void get_required_nodal_information_load_balance_helper(Vector< Vector< FiniteElement * > > &f_halo_ele_pt, unsigned &iproc, Node *nod_pt)
Helper function to get the required nodal information from an haloed node so that a fully-functional ...
bool unrefine_boundary_constrained_by_target_area(const unsigned &b, const unsigned &c, Vector< Vector< double > > &vector_bnd_vertices, double &unrefinement_tolerance, Vector< double > &area_constraint)
Helper function that performs the unrefinement process on the specified boundary by using the provide...
void add_halo_element_helper(unsigned &iproc, FiniteElement *ele_pt)
Helper function to create (halo) elements on the loop process based on the info received in send_and_...
void update_other_proc_shd_bnd_node_helper(Node *&new_nod_pt, Vector< Vector< Vector< std::map< unsigned, Node * > > > > &other_proc_shd_bnd_node_pt, Vector< unsigned > &other_processor_1, Vector< unsigned > &other_processor_2, Vector< unsigned > &other_shared_boundaries, Vector< unsigned > &other_indexes, Vector< Vector< Vector< unsigned > > > &global_node_names, std::map< Vector< unsigned >, unsigned > &node_name_to_global_index, Vector< Node * > &global_shared_node_pt)
Helper function that assigns/updates the references to the node so that it can be found with any othe...
void create_adjacency_matrix_new_shared_edges_helper(Vector< Vector< FiniteElement * > > &unsorted_face_ele_pt, Vector< Vector< Node * > > &tmp_sorted_shared_node_pt, std::map< Node *, Vector< Vector< unsigned > > > &node_alias, Vector< Vector< Vector< unsigned > > > &adjacency_matrix)
Sort the nodes on the new shared boundaries (after load balancing), computes the alias of the nodes a...
bool refine_shared_boundary_constrained_by_target_area(Vector< Vector< double > > &vector_bnd_vertices, Vector< double > &area_constraint)
Helper function that performs the refinement process on the specified boundary by using the provided ...
bool update_polygon_using_face_mesh(TriangleMeshPolygon *polygon_pt, const bool &check_only=false)
Helper function that updates the input polygon's PSLG by using the end-points of elements from FaceMe...
void update_polygon_after_restart(TriangleMeshPolygon *&polygon_pt)
Updates the polylines representation after restart.
void add_vertices_for_non_deletion()
Mark the vertices that are not allowed for deletion by the unrefienment/refinement polyline methods....
void create_polylines_from_polyfiles(const std::string &node_file_name, const std::string &poly_file_name)
Helper function to create polylines and fill associate data.
bool update_polygon_using_elements_area(TriangleMeshPolygon *&polygon_pt, const Vector< double > &target_area)
Updates the polylines using the elements area as constraint for the number of points along the bounda...
void restore_polyline_connections_helper(TriangleMeshPolyLine *polyline_pt, Vector< TriangleMeshPolyLine * > &resume_initial_connection_polyline_pt, Vector< TriangleMeshPolyLine * > &resume_final_connection_polyline_pt)
Restore the connections of the specific polyline The vertices numbering on the destination boundaries...
void adapt(const Vector< double > &elem_error)
Adapt mesh, based on elemental error provided.
General SolidMesh class.
Definition mesh.h:2570
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...
TAdvectionDiffusionReactionElement()
Constructor: Call constructors for TElement and AdvectionDiffusionReaction equations.
Base class for time-stepping schemes. Timestepper provides an approximation of the temporal derivativ...
unsigned ntstorage() const
Return the number of doubles required to represent history (one for steady)
Class to keep track of discrete/continous time. It is essential to have a single Time object when usi...
Base class for defining a triangle mesh boundary, this class has the methods that allow to connect th...
void suspend_final_vertex_connected()
Set the final vertex connection as suspended, it will be resumed when the method to resume the connec...
unsigned initial_vertex_connected_n_vertex() const
Gets the vertex number to which the initial end is connected.
unsigned final_vertex_connected_n_chunk() const
Gets the boundary chunk to which the final end is connected.
unsigned final_vertex_connected_n_vertex() const
Sets the vertex number to which the final end is connected.
bool is_final_vertex_connected() const
Test whether final vertex is connected or not.
unsigned initial_vertex_connected_bnd_id() const
Gets the id to which the initial end is connected.
bool is_initial_vertex_connected() const
Test whether initial vertex is connected or not.
void set_initial_vertex_connected()
Sets the initial vertex as connected.
void set_final_vertex_connected()
Sets the final vertex as connected.
void suspend_initial_vertex_connected()
Set the initial vertex connection as suspended, it will be resumed when the method to resume the conn...
unsigned initial_vertex_connected_n_chunk() const
Gets the boundary chunk to which the initial end is connected.
unsigned final_vertex_connected_bnd_id() const
Gets the id to which the final end is connected.
Base class defining an open curve for the Triangle mesh generation Basically used to define internal ...
Helper object for dealing with the parameters used for the TriangleMesh objects.
Class defining a polyline for use in Triangle Mesh generation.
Vector< double > vertex_coordinate(const unsigned &i) const
Coordinate vector of i-th vertex (const version)
unsigned nvertex() const
Number of vertices.
Class defining a closed polygon for the Triangle mesh generation.
const int check_connections_of_polyline_nodes(std::set< FiniteElement * > &element_in_processor_pt, const int &root_edge_bnd_id, std::map< std::pair< Node *, Node * >, bool > &overlapped_face, std::map< unsigned, std::map< Node *, bool > > &node_on_bnd_not_overlapped_by_shd_bnd, std::list< Node * > &current_polyline_nodes, std::map< unsigned, std::list< Node * > > &shared_bnd_id_to_sorted_list_node_pt, const unsigned &node_degree, Node *&new_node_pt, const bool called_from_load_balance=false)
Check for any possible connections that the array of sorted nodes have with any previous boundaries o...
void compute_boundary_segments_connectivity_and_initial_zeta_values(const unsigned &b)
Compute the boundary segments connectivity for those boundaries that were splited during the distribu...
void break_loops_on_shared_polyline_helper(const unsigned &initial_shd_bnd_id, std::list< Node * > &input_nodes, Vector< FiniteElement * > &input_boundary_element_pt, Vector< int > &input_face_index_element, const int &input_connect_to_the_left, const int &input_connect_to_the_right, Vector< std::list< Node * > > &output_sorted_nodes_pt, Vector< Vector< FiniteElement * > > &output_boundary_element_pt, Vector< Vector< int > > &output_face_index_element, Vector< int > &output_connect_to_the_left, Vector< int > &output_connect_to_the_right)
Break any possible loop created by the sorted list of nodes that is used to create a new shared polyl...
void build_triangulateio(const std::string &poly_file_name, TriangulateIO &triangulate_io, bool &use_attributes)
Helper function to create TriangulateIO object (return in triangulate_io) from the ....
void create_shared_boundaries(OomphCommunicator *comm_pt, const Vector< unsigned > &element_domain, const Vector< GeneralisedElement * > &backed_up_el_pt, const Vector< FiniteElement * > &backed_up_f_el_pt, std::map< Data *, std::set< unsigned > > &processors_associated_with_data, const bool &overrule_keep_as_halo_element_status)
Creates the shared boundaries.
void create_shared_polylines_connections()
Establish the connections of the polylines previously marked as having connections....
void break_loops_on_shared_polyline_load_balance_helper(const unsigned &initial_shd_bnd_id, std::list< Node * > &input_nodes, Vector< FiniteElement * > &input_boundary_element_pt, Vector< FiniteElement * > &input_boundary_face_element_pt, Vector< int > &input_face_index_element, const int &input_connect_to_the_left, const int &input_connect_to_the_right, Vector< std::list< Node * > > &output_sorted_nodes_pt, Vector< Vector< FiniteElement * > > &output_boundary_element_pt, Vector< Vector< FiniteElement * > > &output_boundary_face_element_pt, Vector< Vector< int > > &output_face_index_element, Vector< int > &output_connect_to_the_left, Vector< int > &output_connect_to_the_right)
Break any possible loop created by the sorted list of nodes that is used to create a new shared polyl...
void synchronize_boundary_coordinates(const unsigned &b)
In charge of sinchronize the boundary coordinates for internal boundaries that were split as part of ...
void compute_holes_left_by_halo_elements_helper(Vector< Vector< double > > &output_holes_coordinates)
Compute the holes left by the halo elements, those adjacent to the shared boundaries.
void identify_boundary_segments_and_assign_initial_zeta_values(const unsigned &b, Vector< FiniteElement * > &input_face_ele_pt, const bool &is_internal_boundary, std::map< FiniteElement *, FiniteElement * > &face_to_bulk_element_pt)
Identify the segments from the old mesh (original mesh) in the new mesh (this) and assign initial and...
virtual void reset_boundary_element_info(Vector< unsigned > &ntmp_boundary_elements, Vector< Vector< unsigned > > &ntmp_boundary_elements_in_region, Vector< FiniteElement * > &deleted_elements)
Virtual function to perform the reset boundary elements info routines. Generally used after load bala...
void re_scale_re_assigned_initial_zeta_values_for_internal_boundary(const unsigned &b)
Re-scale the re-assigned zeta values for the boundary nodes, apply only for internal boundaries.
void create_shared_polyline(const unsigned &my_rank, const unsigned &shd_bnd_id, const unsigned &iproc, const unsigned &jproc, std::list< Node * > &sorted_nodes, const int &root_edge_bnd_id, Vector< FiniteElement * > &bulk_bnd_ele_pt, Vector< int > &face_index_ele, Vector< Vector< TriangleMeshPolyLine * > > &unsorted_polylines_pt, const int &connect_to_the_left_flag, const int &connect_to_the_right_flag)
Create the shared polyline and fill the data structured that keep all the information associated with...
void update_holes_information_helper(Vector< TriangleMeshPolygon * > &polygons_pt, Vector< Vector< double > > &output_holes_coordinates)
Keeps those vertices that define a hole, those that are inside closed internal boundaries in the new ...
void create_distributed_domain_representation(Vector< TriangleMeshPolygon * > &polygons_pt, Vector< TriangleMeshOpenCurve * > &open_curves_pt)
Creates the distributed domain representation. Joins the original boundaires, shared boundaries and c...
void build_from_scaffold(TimeStepper *time_stepper_pt, const bool &use_attributes)
Build mesh from scaffold.
void sort_polylines_helper(Vector< TriangleMeshPolyLine * > &unsorted_polylines_pt, Vector< Vector< TriangleMeshPolyLine * > > &sorted_polylines_pt)
Sorts the polylines so they be continuous and then we can create a closed or open curve from them.
void create_tmp_open_curves_helper(Vector< Vector< TriangleMeshPolyLine * > > &sorted_open_curves_pt, Vector< TriangleMeshPolyLine * > &unsorted_shared_to_internal_poly_pt, Vector< TriangleMeshOpenCurve * > &open_curves_pt)
Take the polylines from the original open curves and created new temporaly representations of open cu...
void get_halo_elements_on_all_procs(const unsigned &nproc, const Vector< unsigned > &element_domain, const Vector< GeneralisedElement * > &backed_up_el_pt, std::map< Data *, std::set< unsigned > > &processors_associated_with_data, const bool &overrule_keep_as_halo_element_status, std::map< GeneralisedElement *, unsigned > &element_to_global_index, Vector< Vector< Vector< GeneralisedElement * > > > &output_halo_elements_pt)
Creates the halo elements on all processors Gets the halo elements on all processors,...
void dump_distributed_info_for_restart(std::ostream &dump_file)
Used to dump info. related with distributed triangle meshes.
void re_assign_initial_zeta_values_for_internal_boundary(const unsigned &b, Vector< std::list< FiniteElement * > > &old_segment_sorted_ele_pt, std::map< FiniteElement *, bool > &old_is_inverted)
Re-assign the boundary segments initial zeta (arclength) value for those internal boundaries that wer...
void read_distributed_info_for_restart(std::istream &restart_file)
Used to read info. related with distributed triangle meshes.
void select_boundary_face_elements(Vector< FiniteElement * > &face_el_pt, const unsigned &b, bool &is_internal_boundary, std::map< FiniteElement *, FiniteElement * > &face_to_bulk_element_pt)
Select face element from boundary using the criteria to decide which of the two face elements should ...
void create_polylines_from_halo_elements_helper(const Vector< unsigned > &element_domain, std::map< GeneralisedElement *, unsigned > &element_to_global_index, std::set< FiniteElement * > &element_in_processor_pt, Vector< Vector< Vector< GeneralisedElement * > > > &input_halo_elements, std::map< std::pair< Node *, Node * >, unsigned > &elements_edges_on_boundary, Vector< Vector< Vector< TriangleMeshPolyLine * > > > &output_polylines_pt)
Creates polylines from the intersection of halo elements on all processors. The new polylines define ...
void output_boundary_coordinates(const unsigned &b, std::ostream &outfile)
Output the nodes on the boundary and their respective boundary coordinates(into separate tecplot zone...
void create_tmp_polygons_helper(Vector< Vector< TriangleMeshPolyLine * > > &polylines_pt, Vector< TriangleMeshPolygon * > &polygons_pt)
Take the polylines from the shared boundaries and create temporary polygon representations of the dom...
void get_element_edges_on_boundary(std::map< std::pair< Node *, Node * >, unsigned > &element_edges_on_boundary)
Get the element edges (pair of nodes, edges) that lie on a boundary (used to mark shared boundaries t...
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...
bool Doc_timings
Boolean to indicate whether to doc timings or not.
bool Doc_stats
Boolean to indicate whether to output basic info during setup_multi_domain_interaction() routines.
bool Doc_full_stats
Boolean to indicate whether to output further info during setup_multi_domain_interaction() routines.
double timer()
returns the time in seconds after some point in past
double Tolerable_error
Acceptable discrepancy for mismatch in vertex coordinates. In paranoid mode, the code will die if the...
void initialise_triangulateio(TriangulateIO &triangle_io)
Initialise TriangulateIO structure.
void clear_triangulateio(TriangulateIO &triangulate_io, const bool &clear_hole_data)
Clear TriangulateIO structure.
TriangulateIO deep_copy_of_triangulateio_representation(TriangulateIO &triangle_io, const bool &quiet)
Make (partial) deep copy of TriangulateIO object. We only copy those items we need within oomph-lib's...
DRAIG: Change all instances of (SPATIAL_DIM) to (DIM-1).
struct oomph::classcomp Bottom_left_sorter
OomphInfo oomph_info
Single (global) instantiation of the OomphInfo object – this is used throughout the library as a "rep...
The Triangle data structure, modified from the triangle.h header supplied with triangle 1....
bool operator()(const std::pair< double, double > &lhs, const std::pair< double, double > &rhs) const