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"
36#include "generic/multi_domain.h"
37#include "generic/projection.h"
38#include "generic/face_element_as_geometric_object.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
75 Node_pt.resize(nnode_scaffold, 0);
76
77 // Set number of boundaries
78 unsigned nbound = Tmp_mesh_pt->nboundary();
79
80 // Resize the boundary information
84
85 // If we have different regions, then resize the region
86 // information
88 {
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 {
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(
200 }
201 }
202
203 // Now let's construct lists
204 // Find the number of attributes
205 if (use_attributes)
206 {
207 unsigned n_attribute = element_attribute_map.size();
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
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)
262 Node* new_node_pt = elem_pt->construct_node(n, time_stepper_pt);
263
264 // What are the node's local coordinates?
265 elem_pt->local_coordinate_of_node(n, s);
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 {
271 new_node_pt->x(i) = tmp_elem_pt->interpolated_x(s, i);
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
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 {
304 elem_pt->construct_boundary_node(n, time_stepper_pt);
305 // Add it to the boundary
307 }
308 // Otherwise construct a normal node
309 else
310 {
311 new_node_pt = elem_pt->construct_node(n, time_stepper_pt);
312 }
313
314 // What are the node's local coordinates?
315 elem_pt->local_coordinate_of_node(n, s);
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 {
321 new_node_pt->x(i) = tmp_elem_pt->interpolated_x(s, i);
322 }
323
324 // Add to the global node list
325 Node_pt.push_back(new_node_pt);
326
327 // Add to the edge index
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) =
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 {
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
366 elem_pt);
367 // Need to put a shift in here because of an inconsistent naming
368 // convention between triangle and face elements
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
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 =
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
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 =
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(
470 tmp_ele_pt->node_pt(0), tmp_ele_pt->node_pt(n_nodes - 1));
471
472 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
473 tmp_ele_pt->node_pt(n_nodes - 1), tmp_ele_pt->node_pt(0));
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
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(
559 tmp_ele_pt->node_pt(0), tmp_ele_pt->node_pt(n_nodes - 1));
560
561 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
562 tmp_ele_pt->node_pt(n_nodes - 1), tmp_ele_pt->node_pt(0));
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;
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;
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;
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)
746 Node* left_node_pt = ele_face_pt->node_pt(0);
747 Node* right_node_pt = ele_face_pt->node_pt(nnod - 1);
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
773 Node* local_left_node_pt = ele_face_pt->node_pt(0);
774 Node* local_right_node_pt = ele_face_pt->node_pt(nnod - 1);
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;
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
893 Node* first_node_pt = first_ele_pt->node_pt(0);
895 {
896 first_node_pt = first_ele_pt->node_pt(nnod - 1);
897 }
898
899 // Get access to the last element on the segment
901
902 // Get the last node of the current segment
903 Node* last_node_pt = last_ele_pt->node_pt(nnod - 1);
905 {
906 last_node_pt = last_ele_pt->node_pt(0);
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 {
953 Node* nod_pt = el_pt->node_pt(k_nod);
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(
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;
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
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
1114
1117
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
1146 current_seg_initial_ele_pt->node_pt(0);
1148 {
1150 current_seg_initial_ele_pt->node_pt(nnod - 1);
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
1159 current_seg_last_ele_pt->node_pt(nnod - 1);
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
1196 if (dist < ToleranceForVertexMismatchInPolygons::Tolerable_error)
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
1208 if (dist < ToleranceForVertexMismatchInPolygons::Tolerable_error)
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
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
1229 Boundary_segment_final_zeta[b].push_back(
1231 }
1232 else
1233 {
1234 // Copy the initial and final arclength for each
1235 // segment
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 <
1265 ToleranceForVertexMismatchInPolygons::Tolerable_error)
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 <
1279 ToleranceForVertexMismatchInPolygons::Tolerable_error)
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
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
1300 Boundary_segment_final_zeta[b].push_back(
1302 }
1303 else
1304 {
1305 // Copy the initial and final arclength for each segment
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;
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
1403 Node* first_node_pt = first_ele_pt->node_pt(0);
1405 {
1406 first_node_pt = first_ele_pt->node_pt(nnod - 1);
1407 }
1408
1409 // Get access to the last element on the segment
1411
1412 // Get the last node of the current segment
1413 Node* last_node_pt = last_ele_pt->node_pt(nnod - 1);
1415 {
1416 last_node_pt = last_ele_pt->node_pt(0);
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;
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;
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;
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;
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
1805 original_mesh_pt->boundary_initial_coordinate(b);
1806
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)
1813 original_mesh_pt->boundary_initial_zeta_coordinate(b);
1814
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
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;
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)
1985 Node* left_node_pt = ele_face_pt->node_pt(0);
1986 Node* right_node_pt = ele_face_pt->node_pt(nnod - 1);
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
2012 Node* local_left_node_pt = ele_face_pt->node_pt(0);
2013 Node* local_right_node_pt = ele_face_pt->node_pt(nnod - 1);
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;
2086 << "The number of segments is zero, but the number of nonhalo\n"
2087 << "elements is: (" << nnon_halo_face_elements << ")\n";
2088 throw OomphLibError(
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
2128 Node* first_node_pt = first_ele_pt->node_pt(0);
2130 {
2131 first_node_pt = first_ele_pt->node_pt(nnod - 1);
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
2156 Node* last_node_pt = last_ele_pt->node_pt(nnod - 1);
2158 {
2159 last_node_pt = last_ele_pt->node_pt(0);
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 {
2196 Node* nod_pt = el_pt->node_pt(k_nod);
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
2299 Node* first_node_pt = first_ele_pt->node_pt(0);
2301 {
2302 first_node_pt = first_ele_pt->node_pt(nnod - 1);
2303 }
2304
2305 // Get access to the last element on the segment
2307
2308 // Get the last node of the current segment
2309 Node* last_node_pt = last_ele_pt->node_pt(nnod - 1);
2311 {
2312 last_node_pt = last_ele_pt->node_pt(0);
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
2342 Node* left_node_pt = halo_face_ele_pt->node_pt(0);
2343 Node* right_node_pt = halo_face_ele_pt->node_pt(nnod - 1);
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
2430 if (tmp_left_bulk_ele_pt->is_halo())
2431 {
2432 // Then store the corresponding info.
2433 int left_proc = tmp_left_bulk_ele_pt->non_halo_proc_ID();
2434#ifdef PARANOID
2435 if (left_proc < 0)
2436 {
2437 std::ostringstream 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
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;
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
2499 if (tmp_left_bulk_ele_pt->is_halo())
2500 {
2501 std::ostringstream 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
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;
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
2573 if (tmp_right_bulk_ele_pt->is_halo())
2574 {
2575 // Then store the corresponding info.
2576 int right_proc = tmp_right_bulk_ele_pt->non_halo_proc_ID();
2577#ifdef PARANOID
2578 if (right_proc < 0)
2579 {
2580 std::ostringstream 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
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;
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
2642 if (tmp_right_bulk_ele_pt->is_halo())
2643 {
2644 std::ostringstream 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
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;
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
2762 const unsigned nudata_to_send = flat_packed_unsigned_send_data.size();
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
2790 if (flat_packed_unsigned_send_data.size() == 0)
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
2801 if (flat_packed_unsigned_receive_data.size() == 0)
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
2843 const unsigned nddata_to_send = flat_packed_double_send_data.size();
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
2867 if (flat_packed_double_send_data.size() == 0)
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
2877 if (flat_packed_double_receive_data.size() == 0)
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;
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;
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
3511 if (flat_packed_unsigned_send_data.size() == 0)
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
3521 if (flat_packed_unsigned_receive_data.size() == 0)
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
3578 if (flat_packed_double_send_data.size() == 0)
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
3588 if (flat_packed_double_receive_data.size() == 0)
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
3666
3669
3670 // The info. for the segments
3674
3677
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
3701 if (first_seg_ele_pt->is_halo())
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 {
3720 first_seg_node_pt = first_seg_ele_pt->node_pt(nnod - 1);
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
3742 Node* last_seg_node_pt = last_seg_ele_pt->node_pt(nnod - 1);
3744 {
3745 last_seg_node_pt = last_seg_ele_pt->node_pt(0);
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
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
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
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 {
3790 }
3791 else
3792 {
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
3803
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
3870 first_node_pt = first_ele_pt->node_pt(0);
3872 {
3873 first_node_pt = first_ele_pt->node_pt(nnod - 1);
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
3886 first_node_pt = first_ele_pt->node_pt(nnod - 1);
3888 {
3889 first_node_pt = first_ele_pt->node_pt(0);
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
3966 last_node_pt = last_ele_pt->node_pt(nnod - 1);
3968 {
3969 last_node_pt = last_ele_pt->node_pt(0);
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
3982 last_node_pt = last_ele_pt->node_pt(0);
3984 {
3985 last_node_pt = last_ele_pt->node_pt(nnod - 1);
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
4041
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 =
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
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 =
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
4161
4162 const unsigned n_nodes = tmp_ele_pt->nnode();
4163
4164 std::pair<Node*, Node*> tmp_pair = std::make_pair(
4165 tmp_ele_pt->node_pt(0), tmp_ele_pt->node_pt(n_nodes - 1));
4166
4167 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
4168 tmp_ele_pt->node_pt(n_nodes - 1), tmp_ele_pt->node_pt(0));
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
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)
4244
4245 const unsigned n_nodes = tmp_ele_pt->nnode();
4246
4247 std::pair<Node*, Node*> tmp_pair = std::make_pair(
4248 tmp_ele_pt->node_pt(0), tmp_ele_pt->node_pt(n_nodes - 1));
4249
4250 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
4251 tmp_ele_pt->node_pt(n_nodes - 1), tmp_ele_pt->node_pt(0));
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;
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(
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;
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)
4385 Node* left_node_pt = ele_face_pt->node_pt(0);
4386 Node* right_node_pt = ele_face_pt->node_pt(nnod - 1);
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
4412 Node* local_left_node_pt = ele_face_pt->node_pt(0);
4413 Node* local_right_node_pt = ele_face_pt->node_pt(nnod - 1);
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;
4481 << "The number of segments is zero, but the number of nonhalo\n"
4482 << "elements is: (" << nnon_halo_face_elements << ")\n";
4483 throw OomphLibError(
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
4537 Node* first_node_pt = first_ele_pt->node_pt(0);
4539 {
4540 first_node_pt = first_ele_pt->node_pt(nnod - 1);
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
4563 Node* last_node_pt = last_ele_pt->node_pt(nnod - 1);
4565 {
4566 last_node_pt = last_ele_pt->node_pt(0);
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 {
4608 Node* nod_pt = el_pt->node_pt(k_nod);
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 {
4699
4702 for (unsigned i = 0; i < 2; i++)
4703 {
4706
4709 }
4710
4711 // Check if the boundary has an associated GeomObject
4712 if (this->boundary_geom_object_pt(b) != 0)
4713 {
4716
4719
4720 } // if (this->boundary_geom_object_pt(b)!=0)
4721 else
4722 {
4725
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
4738
4741
4744 // ------------------------------------------------------------------
4745 // .. and resize the storages for the new number of segments
4749
4750 // Check if the boundary has an associated GeomObject
4751 if (this->boundary_geom_object_pt(b) != 0)
4752 {
4755 }
4756 else
4757 {
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 {
4840 first_seg_node_pt = first_seg_ele_pt->node_pt(nnod - 1);
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
4856 Node* last_seg_node_pt = last_seg_ele_pt->node_pt(nnod - 1);
4858 {
4859 last_seg_node_pt = last_seg_ele_pt->node_pt(0);
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
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
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
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
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
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
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
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
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
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
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
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
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 {
5103 }
5104 else
5105 {
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;
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 {
5162 first_seg_node_pt = first_seg_ele_pt->node_pt(nnod - 1);
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
5178 Node* last_seg_node_pt = last_seg_ele_pt->node_pt(nnod - 1);
5180 {
5181 last_seg_node_pt = last_seg_ele_pt->node_pt(0);
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
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
5206
5207 } // if (this->boundary_geom_object_pt(b)!=0)
5208 else
5209 {
5210 // Re-assign the initial arclength for the current segment
5212
5213 } // else if (this->boundary_geom_object_pt(b)!=0)
5214
5215 // Re-assign the initial node coordinates
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
5227
5228 } // if (this->boundary_geom_object_pt(b)!=0)
5229 else
5230 {
5231 // Re-assign the final arclength for the current segment
5233
5234 } // else if (this->boundary_geom_object_pt(b)!=0)
5235
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
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 =
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
5328
5329 // Get the index of the face of element e along
5330 // boundary b
5331 int face_index =
5333
5334 // Create the face element
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
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
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
5432 dependant_face_ele_pt->node_pt(nnodes - 1);
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
5463 main_bulk_ele_pt->non_halo_proc_ID();
5465 dependant_bulk_ele_pt->non_halo_proc_ID();
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 {
5506 main_bulk_ele_pt->node_pt(inode)->x(idim);
5508 dependant_bulk_ele_pt->node_pt(inode)->x(idim);
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 =
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
5658
5659 // Get the index of the face of element e along boundary b
5660 int face_index =
5662
5663 // Create the face element
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
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
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
5761 dependant_face_ele_pt->node_pt(nnodes - 1);
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
5793 main_bulk_ele_pt->non_halo_proc_ID();
5795 dependant_bulk_ele_pt->non_halo_proc_ID();
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 {
5833 main_bulk_ele_pt->node_pt(inode)->x(idim);
5835 dependant_bulk_ele_pt->node_pt(inode)->x(idim);
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;
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 {
5988 Node* face_node_pt = ele_face_pt->node_pt(in);
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
5998 int int_nonhalo_ID = face_node_pt->non_halo_proc_ID();
5999#ifdef PARANOID
6000 if (int_nonhalo_ID < 0)
6001 {
6002 std::ostringstream 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 {
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;
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 {
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;
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
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
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 =
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
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 =
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
6640
6641 const unsigned n_nodes = tmp_ele_pt->nnode();
6642
6643 std::pair<Node*, Node*> tmp_pair = std::make_pair(
6644 tmp_ele_pt->node_pt(0), tmp_ele_pt->node_pt(n_nodes - 1));
6645
6646 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
6647 tmp_ele_pt->node_pt(n_nodes - 1), tmp_ele_pt->node_pt(0));
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
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)
6724
6725 const unsigned n_nodes = tmp_ele_pt->nnode();
6726
6727 std::pair<Node*, Node*> tmp_pair = std::make_pair(
6728 tmp_ele_pt->node_pt(0), tmp_ele_pt->node_pt(n_nodes - 1));
6729
6730 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
6731 tmp_ele_pt->node_pt(n_nodes - 1), tmp_ele_pt->node_pt(0));
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;
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;
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)
6864 Node* left_node_pt = ele_face_pt->node_pt(0);
6865 Node* right_node_pt = ele_face_pt->node_pt(nnod - 1);
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
6890 Node* local_left_node_pt = ele_face_pt->node_pt(0);
6891 Node* local_right_node_pt = ele_face_pt->node_pt(nnod - 1);
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;
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
7003 Node* first_node_pt = first_ele_pt->node_pt(0);
7005 {
7006 first_node_pt = first_ele_pt->node_pt(nnod - 1);
7007 }
7008
7009 // Get access to the last element on the segment
7011
7012 // Get the last node of the current segment
7013 Node* last_node_pt = last_ele_pt->node_pt(nnod - 1);
7015 {
7016 last_node_pt = last_ele_pt->node_pt(0);
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 {
7063 Node* nod_pt = el_pt->node_pt(k_nod);
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
7104
7107
7110
7111 // Get the zeta values for the first and last node in the boundary
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
7132 Node* first_node_pt = first_face_ele_pt->node_pt(0);
7134 {
7135 first_node_pt = first_face_ele_pt->node_pt(nnod - 1);
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
7146 Node* last_node_pt = last_face_ele_pt->node_pt(nnod - 1);
7148 {
7149 last_node_pt = last_face_ele_pt->node_pt(0);
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);
7170
7171 // Check if the boudary has an associated GeomObject
7172 if (this->boundary_geom_object_pt(b) != 0)
7173 {
7176 }
7177 else
7178 {
7179 // Re-assign the values and re-scale them
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
7220 TriangleHelper::initialise_triangulateio(triangulate_io);
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
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
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
7557
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;
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
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
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 =
7635 const double zeta_segment_final =
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 =
7652 const double arclength_segment_final =
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;
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;
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
7847
7850
7851 // The info. for the segments
7855
7858
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
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
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;
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;
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
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
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
8052 Node* tmp_node_pt = face_ele_pt->node_pt(i);
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;
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;
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;
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);
8727 if (diff < ToleranceForVertexMismatchInPolygons::Tolerable_error)
8728 {
8729 // The polyline already create a Polygon, then create it!!!
8730 // Create the curve section representation of the current root curve
8733
8734 // Copy the polylines into its curve section representation
8735 for (unsigned i = 0; i < nroot_curve_polyline; i++)
8736 {
8738 }
8739
8740 // ... and create the Polygon
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
8776 const unsigned ncurrent_curve_polyline = current_curve_pt.size();
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
8801 if (diff < ToleranceForVertexMismatchInPolygons::Tolerable_error)
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
8827 if (diff < ToleranceForVertexMismatchInPolygons::Tolerable_error)
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
8867 if (diff < ToleranceForVertexMismatchInPolygons::Tolerable_error)
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
8890 if (diff < ToleranceForVertexMismatchInPolygons::Tolerable_error)
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);
8931 if (diff < ToleranceForVertexMismatchInPolygons::Tolerable_error)
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;
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;
8958 for (std::list<Vector<TriangleMeshPolyLine*>>::iterator it =
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
8990 for (std::list<Vector<TriangleMeshPolyLine*>>::iterator it =
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;
9003 for (std::list<Vector<TriangleMeshPolyLine*>>::iterator it =
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
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
9044 const unsigned ninternal_open_curves = sorted_open_curves_pt.size();
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)
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
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
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
9185 Node* first_node_pt = face_ele_pt->node_pt(0);
9186 Node* last_node_pt = face_ele_pt->node_pt(n_node_face - 1);
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
9199 if (face_ele_pt->node_pt(n) == new_node_pt)
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
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
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
9270 Node* first_node_pt = face_ele_pt->node_pt(0);
9271 Node* last_node_pt = face_ele_pt->node_pt(n_node_face - 1);
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
9284 if (face_ele_pt->node_pt(n) == new_node_pt)
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
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
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 =
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 =
9779 dst_shd_bnd_ids.size();
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
9793
9794 if (poly_to_connect_pt != 0)
9795 {
9796 // Look for the vertex number in the destination
9797 // shared 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 =
9837 dst_shd_bnd_ids.size();
9838 for (unsigned ss = 0; ss < n_shd_bnd_overlap_int_bnd; ss++)
9839 {
9840 const unsigned 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;
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
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 =
10017 dst_shd_bnd_ids.size();
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
10035
10036 if (poly_to_connect_pt != 0)
10037 {
10038 // Look for the vertex number in the destination
10039 // shared 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
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 =
10142 dst_shd_bnd_ids.size();
10143 for (unsigned ss = 0; ss < n_shd_bnd_overlap_int_bnd; ss++)
10144 {
10145 const unsigned 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
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
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 =
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 =
10405 dst_shd_bnd_ids.size();
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
10419
10420 if (poly_to_connect_pt != 0)
10421 {
10422 // Look for the vertex number in the destination
10423 // shared 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 =
10463 dst_shd_bnd_ids.size();
10464 for (unsigned ss = 0; ss < n_shd_bnd_overlap_int_bnd; ss++)
10465 {
10466 const unsigned 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;
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
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 =
10644 dst_shd_bnd_ids.size();
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
10662
10663 if (poly_to_connect_pt != 0)
10664 {
10665 // Look for the vertex number in the destination
10666 // shared 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
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 =
10769 dst_shd_bnd_ids.size();
10770 for (unsigned ss = 0; ss < n_shd_bnd_overlap_int_bnd; ss++)
10771 {
10772 const unsigned 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 {
10896 Node* tmp_node_pt = ele_pt->node_pt(k);
10897 // Loop over the dimension
10898 for (unsigned d = 0; d < 2; d++)
10899 {
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
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
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
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
11302
11303 // If the vertex is inside the convex hull then mark it
11305 {
11306 // Set as inside the convex hull
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
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
11327 {
11328 // Copy the hole information
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>
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,
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(
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) ||
11655 (el_pt->must_be_kept_as_halo()))
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 {
11679 Node* nod_pt = finite_el_pt->node_pt(n);
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
11727 // Get the face index
11728 int face_index = this->face_index_at_boundary(b, e);
11729 // Create the face element
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
11735 Node* first_node_pt = face_ele_pt->node_pt(0);
11736 Node* last_node_pt = face_ele_pt->node_pt(nnodes - 1);
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)
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
11981 Node* first_node_pt = el_pt->node_pt(0);
11982 Node* second_node_pt = el_pt->node_pt(1);
11983 Node* third_node_pt = el_pt->node_pt(2);
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
12088 Node* first_node_pt = el_pt->node_pt(0);
12089 Node* second_node_pt = el_pt->node_pt(1);
12090 Node* third_node_pt = el_pt->node_pt(2);
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
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
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;
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 =
12513 edge_element_face[iproc][jproc].size();
12515 {
12516 std::stringstream 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;
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;
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
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;
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
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
13181 const unsigned n_final_sorted_nodes = final_sorted_nodes_pt.size();
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;
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(
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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 // ---------------------------------------------
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
14710 const unsigned n_shared_boundary_elements = bulk_bnd_ele_pt.size();
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;
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
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
14763
14764 // ... do this on both processors involved in the creation of
14765 // the shared boundary
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;
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;
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
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 =
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
14918
14919 // Get the old face index
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);
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);
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 =
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 {
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
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
15066 TriangleHelper::initialise_triangulateio(triangle_refine);
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
15301 const unsigned nmy_rank_shd_bnd = my_rank_shared_boundaries_ids.size();
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);
15440 const unsigned n_procs_from_shrd_bnd = procs_from_shrd_bnd.size();
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
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;
15496 if (Global_timings::Doc_comprehensive_timings)
15497 {
15498 tt_start = TimingHelpers::timer();
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
15541 double t_start_global_node_names_and_shared_nodes = TimingHelpers::timer();
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
15559 TimingHelpers::timer() - t_start_global_node_names_and_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
15581 TimingHelpers::timer();
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: "
15594 << TimingHelpers::timer() - t_start_send_info_shd_nodes_on_original_bnds
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
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
15643 Node* node_pt = ele_pt->node_pt(n);
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)
15723 TimingHelpers::timer();
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 =
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 {
15869 Node* node_pt = ele_pt->node_pt(j);
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)
15957 TimingHelpers::timer() -
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)
15980 TimingHelpers::timer();
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)
15994 TimingHelpers::timer() -
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 {
16019 tt_end = TimingHelpers::timer();
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
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 {
16039 tt_end = TimingHelpers::timer();
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
16146 Node* node_pt = ele_pt->node_pt(n);
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
16325 const unsigned n_udata_send_to_root = flat_packed_send_udata.size();
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
16390 if (root_flat_packed_receive_udata.size() == 0)
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
16472 const unsigned n_root_local_node_names = root_local_node_names.size();
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
16638 Vector<Vector<unsigned>>::iterator it =
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;
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;
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;
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;
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?
17288 if (node_pt->is_on_boundary(bb))
17289 {
17290 // Then save it as being on boundary bb
17291 on_original_boundaries.push_back(bb);
17292 // Get the boundary coordinate
17294 node_pt->get_coordinates_on_boundary(bb, zeta);
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;
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 =
17360 node_on_shd_bnd_pt[iproc].size();
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
17406 const unsigned n_original_boundaries = on_original_boundaries.size();
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;
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;
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
17628
17629 // Get the zeta boundary coordinate
17632 node_pt->set_coordinates_on_boundary(original_bound_id, zeta);
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
17677 const unsigned n_global_shared_nodes = global_shared_node_pt.size();
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
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
17733 Node* node_pt = ele_pt->node_pt(n);
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
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 =
17862 send_haloed_ele_pt[iproc].size();
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 {
17914 Node* node_pt = ele_pt->node_pt(j);
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
18020#ifdef PARANOID
18021 if (counter_face_indexes > 2)
18022 {
18023 std::stringstream 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
18047 const unsigned nassociated_boundaries = associated_boundaries.size();
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 =
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 ==
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
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
18252 const unsigned nmy_rank_shd_bnd = my_rank_shared_boundaries_ids.size();
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
18371 const unsigned n_original_boundaries = original_boundaries.size();
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
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(
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
19132
19133 // Add the element, it is a new element in the mesh
19135
19136 // Add halo element to this mesh
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*>(
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
19407 new_el_pt->node_pt(node_index) = new_nod_pt;
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;
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;
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;
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 {
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
19727 new_el_pt->node_pt(node_index) = new_nod_pt;
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;
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;
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 =
19930 new_el_pt->construct_boundary_node(node_index, time_stepper_pt);
19931 }
19932 else
19933 {
19934 new_nod_pt = new_el_pt->construct_boundary_node(node_index);
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
19941 new_el_pt->node_pt(node_index) = new_nod_pt;
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 {
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 {
19966 new_nod_pt = new_el_pt->construct_node(node_index, time_stepper_pt);
19967 }
19968 else
19969 {
19970 new_nod_pt = new_el_pt->construct_node(node_index);
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
19976 new_el_pt->node_pt(node_index) = new_nod_pt;
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
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
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(
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)
20110
20111 // Access the required geom objects from the
20112 // MacroElementNodeUpdateMesh
20113 MacroElementNodeUpdateMesh* macro_mesh_pt =
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
20119 new_el_pt->local_coordinate_of_node(node_index,
20121
20122 // Set node update info for this node
20123 macro_nod_pt->set_node_update_info(
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 {
20501 t_start_overall_load_balance = TimingHelpers::timer();
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
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(
20520 }
20521#endif
20522
20523 // Backup pointers to elements in this mesh
20525 for (unsigned e = 0; e < nelement_before_load_balance; e++)
20526 {
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 {
20538 tt_start_get_domains_halo_elements = TimingHelpers::timer();
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
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
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]: "
20813 << TimingHelpers::timer() - tt_start_get_domains_halo_elements
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 {
20831 tt_start_get_fe_version_from_ge_halo_ed = TimingHelpers::timer();
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
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
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]: "
20888 << TimingHelpers::timer() -
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 {
20909 tt_start_prepare_element_to_send = TimingHelpers::timer();
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
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
20943 Node* node_pt = ele_pt->node_pt(j);
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
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
20984 Node* node_pt = ele_pt->node_pt(j);
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]: "
21003 << TimingHelpers::timer() - tt_start_prepare_element_to_send
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 {
21029 tt_start_compute_new_local_halo_elements = TimingHelpers::timer();
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
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 {
21094 Node* node_pt = ele_pt->node_pt(j);
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
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
21212 Node* node_pt = ele_pt->node_pt(j);
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]: "
21268 << TimingHelpers::timer() - tt_start_compute_new_local_halo_elements
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 {
21290 tt_start_compute_new_local_shd_bnd_ele = TimingHelpers::timer();
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]: "
21335 << TimingHelpers::timer() -
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 {
21358 tt_start_send_elements_to_other_processors = TimingHelpers::timer();
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 {
21507 Node* node_pt = send_ele_pt->node_pt(j);
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
21753 const unsigned nreceived_elements = currently_created_elements.size();
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]: "
21834 << TimingHelpers::timer() -
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 {
21857 tt_start_compute_additional_shared_boundaries = TimingHelpers::timer();
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]: "
21926 << TimingHelpers::timer() -
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 {
21949 tt_start_sort_shared_boundaries = TimingHelpers::timer();
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
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
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
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
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]: "
22152 << TimingHelpers::timer() - tt_start_sort_shared_boundaries
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 {
22176 tt_start_create_new_shared_boundaries = TimingHelpers::timer();
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]: "
22244 << TimingHelpers::timer() - tt_start_create_new_shared_boundaries
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 {
22269 tt_start_delete_elements = TimingHelpers::timer();
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
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
22327 ele_pt->node_pt(j)->set_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
22372 ele_pt->node_pt(j)->set_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]: "
22389 << TimingHelpers::timer() - tt_start_delete_elements
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 {
22410 tt_start_re_etablish_halo_ed_info = TimingHelpers::timer();
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
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)
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
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
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]: "
22521 << TimingHelpers::timer() - tt_start_re_etablish_halo_ed_info
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 << "]: "
22537 << TimingHelpers::timer() - t_start_overall_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 {
22724 tt_start_get_edges_from_shd_bnd_face_ele = TimingHelpers::timer();
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
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();
22793 Node* first_face_node_pt = tmp_ele_pt->node_pt(0);
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
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]: "
22895 << TimingHelpers::timer() -
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 {
22920 tt_start_sort_shared_face_elements = TimingHelpers::timer();
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
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
22979 Node* node_pt = face_ele_pt->node_pt(0);
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
22989 Node* node_pt = face_ele_pt->node_pt(n);
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]: "
23134 << TimingHelpers::timer() - tt_start_sort_shared_face_elements
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 {
23159 tt_start_compute_valency_of_nodes = TimingHelpers::timer();
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]: "
23173 << TimingHelpers::timer() - tt_start_compute_valency_of_nodes
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 {
23194 tt_start_nodes_on_non_overlapped_shd_bnd = TimingHelpers::timer();
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]: "
23235 << TimingHelpers::timer() -
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 {
23262 tt_start_join_shd_bnd_face_ele = TimingHelpers::timer();
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
23317 const unsigned nunsorted_face_ele = unsorted_face_ele_pt[iproc].size();
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)
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 {
23389 initial_node_pt = root_ele_pt->node_pt(0);
23390 final_node_pt = root_ele_pt->node_pt(nnodes - 1);
23391 }
23392 else
23393 {
23394 initial_node_pt = root_ele_pt->node_pt(nnodes - 1);
23395 final_node_pt = root_ele_pt->node_pt(0);
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 {
23583 first_node_pt = tmp_ele_pt->node_pt(0);
23584 last_node_pt = tmp_ele_pt->node_pt(nnodes - 1);
23585 }
23586 else
23587 {
23588 first_node_pt = tmp_ele_pt->node_pt(nnodes - 1);
23589 last_node_pt = tmp_ele_pt->node_pt(0);
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
23898 const unsigned n_final_sorted_nodes = final_sorted_nodes_pt.size();
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]: "
23953 << TimingHelpers::timer() - tt_start_join_shd_bnd_face_ele
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 {
23984 tt_start_get_new_shared_boundaries_ids = TimingHelpers::timer();
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
23998 sorted_face_ele_pt[iproc].size();
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]: "
24266 << TimingHelpers::timer() - tt_start_get_new_shared_boundaries_ids
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 {
24290 tt_start_create_new_shared_boundaries_polylines = TimingHelpers::timer();
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
24427 Node* first_node_pt = first_face_ele_pt->node_pt(0);
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
24485 Node* node_pt = face_ele_pt->node_pt(n);
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
24493 Node* last_node_pt = face_ele_pt->node_pt(nnodes - 1);
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
24503 Node* node_pt = face_ele_pt->node_pt(n);
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
24511 Node* last_node_pt = face_ele_pt->node_pt(0);
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
24537
24538 // Updates bnd_id<--->curve section map
24540
24541 // Add the new created polyline to the list of unsorted
24542 // polylines
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;
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;
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]: "
24776 << TimingHelpers::timer() -
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 {
24798 tt_start_create_new_shared_curves = TimingHelpers::timer();
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]: "
24828 << TimingHelpers::timer() - tt_start_create_new_shared_curves
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
24993 if (package_unsigned_send_data_to_root.size() == 0)
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 =
25201 local_node_alias[iproc][jproc].size();
25202#ifdef PARANOID
25203 // Read the number of nodes shared in the other direction
25204 const unsigned n_shd_nodes_jproc_iproc =
25205 local_node_alias[jproc][iproc].size();
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 =
25233 local_node_alias[iproc][jproc][ishd].size();
25234 const unsigned n_alias_jproc_iproc =
25235 local_node_alias[jproc][iproc][ishd].size();
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
25362 n_current_alias = node_alias.size();
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
25883 const unsigned n_unsorted_face_ele = unsorted_face_ele_pt[iproc].size();
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
25891 Node* left_node_pt = face_ele_pt->node_pt(0);
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
25908 Node* right_node_pt = face_ele_pt->node_pt(n_nodes - 1);
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
25993 const unsigned n_unsorted_face_ele = unsorted_face_ele_pt[iproc].size();
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
26001 Node* left_node_pt = face_ele_pt->node_pt(0);
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
26006 Node* right_node_pt = face_ele_pt->node_pt(n_nodes - 1);
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
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
26095 const unsigned nhalo_face_shared_ele = halo_shared_face_ele_pt.size();
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;
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;
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
26156 Node* h_first_node_pt = halo_face_ele_pt->node_pt(0);
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
26193 shared_face_done.size())
26194 {
26195 std::ostringstream error_message;
26196 error_message << "The number of DONE shared boundary face elements ("
26197 << shared_face_done.size()
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();
26236 Node* first_node_pt = root_face_ele_pt->node_pt(0);
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
26394#ifdef PARANOID
26395 if (counter_face_indexes > 2)
26396 {
26397 std::stringstream 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
26422 const unsigned nassociated_boundaries = associated_boundaries.size();
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 =
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 ==
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
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
26628 const unsigned nmy_rank_shd_bnd = my_rank_shared_boundaries_ids.size();
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
26760 const unsigned nnew_nodes_on_domain = new_nodes_on_domain.size();
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
26854 const unsigned n_original_boundaries = original_boundaries.size();
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
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 =
27334 halo_element_number[jproc].size();
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
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;
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;
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
27896 new_el_pt->node_pt(node_index) = new_nod_pt;
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;
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;
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;
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
28277 const unsigned n_haloed_indexes = halo_element_number[my_rank].size();
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
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;
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
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;
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
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 {
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
28467 new_el_pt->node_pt(node_index) = new_nod_pt;
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;
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;
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 =
28661 new_el_pt->construct_boundary_node(node_index, time_stepper_pt);
28662 }
28663 else
28664 {
28665 new_nod_pt = new_el_pt->construct_boundary_node(node_index);
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
28673 new_el_pt->node_pt(node_index) = new_nod_pt;
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 {
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 {
28701 new_nod_pt = new_el_pt->construct_node(node_index, time_stepper_pt);
28702 }
28703 else
28704 {
28705 new_nod_pt = new_el_pt->construct_node(node_index);
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
28712 new_el_pt->node_pt(node_index) = new_nod_pt;
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
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
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(
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)
28858
28859 // Access the required geom objects from the
28860 // MacroElementNodeUpdateMesh
28861 MacroElementNodeUpdateMesh* macro_mesh_pt =
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
28867 new_el_pt->local_coordinate_of_node(node_index,
28869
28870 // Set node update info for this node
28871 macro_nod_pt->set_node_update_info(
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 =
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
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 =
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
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(
29151 tmp_ele_pt->node_pt(0), tmp_ele_pt->node_pt(n_nodes - 1));
29152
29153 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
29154 tmp_ele_pt->node_pt(n_nodes - 1), tmp_ele_pt->node_pt(0));
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
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
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(
29244 tmp_ele_pt->node_pt(0), tmp_ele_pt->node_pt(n_nodes - 1));
29245
29246 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
29247 tmp_ele_pt->node_pt(n_nodes - 1), tmp_ele_pt->node_pt(0));
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;
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)
29413 Node* left_node_pt = ele_face_pt->node_pt(0);
29414 Node* right_node_pt = ele_face_pt->node_pt(nnod - 1);
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
29441 Node* local_left_node_pt = ele_face_pt->node_pt(0);
29442 Node* local_right_node_pt = ele_face_pt->node_pt(nnod - 1);
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;
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
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
29542 Node* first_node_pt = first_ele_pt->node_pt(0);
29544 {
29545 first_node_pt = first_ele_pt->node_pt(nnod - 1);
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 {
29566 last_node_pt = ele_pt->node_pt(nnod - 1);
29567 }
29568 else
29569 {
29570 last_node_pt = ele_pt->node_pt(0);
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 {
29599 double t_start_overall = TimingHelpers::timer();
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
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
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(
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(
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(
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
29995 TimingHelpers::timer();
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 {
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
30058 TimingHelpers::timer() - t_start_first_stage_segments_connectivity;
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 {
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 // ----------------------------------------------------------
30295 const double t_start_building_background_mesh = TimingHelpers::timer();
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: "
30311 << TimingHelpers::timer() - t_start_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() =
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() =
30374
30376 TimingHelpers::timer();
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
30406 TimingHelpers::timer() - t_start_second_stage_segments_connectivity;
30407
30408 const double t_start_snap_nodes_bg_mesh = TimingHelpers::timer();
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 =
30418 TimingHelpers::timer() - t_start_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
30541 double t0_bin_diff = TimingHelpers::timer();
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];
30613 t_total_map += TimingHelpers::timer() - t_map;
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
30684 double t_start_transfer_target_areas = TimingHelpers::timer();
30685 double t0_loop_int_pts = TimingHelpers::timer();
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())
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 {
30821 unsigned length = new_transferred_target_area.size();
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
30862 TimingHelpers::timer() - t_start_transfer_target_areas;
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
30890 const double t_start_limit_target_areas = TimingHelpers::timer();
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 =
30970 TimingHelpers::timer() - t_start_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
31011 const double t_start_create_new_adapted_mesh = TimingHelpers::timer();
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
31038 TimingHelpers::timer() - t_start_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() =
31094
31095 // Reset the boundary coordinates if there is
31096 // a geometric object associated with the boundary
31097 new_mesh_pt->boundary_coordinate_limits() =
31099
31101 TimingHelpers::timer();
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
31135 TimingHelpers::timer() - t_start_third_stage_segments_connectivity;
31136
31137 const double t_start_snap_nodes_new_mesh = TimingHelpers::timer();
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 =
31147 TimingHelpers::timer() - t_start_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
31205 const double t_start_wait_other_processors = TimingHelpers::timer();
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
31242 TimingHelpers::timer() - t_start_wait_other_processors;
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
31308 double tt_start_projection = TimingHelpers::timer();
31309
31310 // Print info. for tranfering target areas
31311 if (Print_timings_projection)
31312 {
31313 // Switch timings and stats on
31314 Multi_domain_functions::Doc_timings = true;
31315 Multi_domain_functions::Doc_stats = true;
31316 Multi_domain_functions::Doc_full_stats = true;
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
31392 Multi_domain_functions::Doc_timings = false;
31393 Multi_domain_functions::Doc_stats = false;
31394 Multi_domain_functions::Doc_full_stats = false;
31395 }
31396
31397 // Get the total time for projection
31398 const double tt_projection =
31399 TimingHelpers::timer() - tt_start_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();
31510 Node_pt.resize(nnod);
31511 nel = new_mesh_pt->nelement();
31512 Element_pt.resize(nel);
31513 for (unsigned j = 0; j < nnod; j++)
31514 {
31515 Node_pt[j] = new_mesh_pt->node_pt(j);
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
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);
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);
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);
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);
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
31671 const unsigned nmy_rank_shd_bnd = my_rank_shared_boundaries_ids.size();
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 {
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
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);
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
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 =
31736 TriangleHelper::deep_copy_of_triangulateio_representation(
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
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
31797 double CPU_for_adaptation = TimingHelpers::timer() - t_start_overall;
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
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
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
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
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
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)
33074 const unsigned n_outer_boundaries = tmp_outer_polygons_pt.size();
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
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
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>
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;
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
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
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
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
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
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
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;
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
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
33657 polyline_pt->initial_vertex_connected_n_chunk() = sub_poly_to_connect;
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;
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
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
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
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
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
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
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;
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
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
34023 polyline_pt->final_vertex_connected_n_chunk() = sub_poly_to_connect;
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
34060 const unsigned n_final_poly = resume_final_connection_polyline_pt.size();
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
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
34108 if (error < ToleranceForVertexMismatchInPolygons::Tolerable_error)
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;
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(
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
34207 el_pt->node_pt(0)->get_coordinates_on_boundary(bound, bound_left);
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)
34221 ->get_coordinates_on_boundary(bound, bound_right);
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
34248 unsigned n_vertex = tmp_vector_vertex_node.size();
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,
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],
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(
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
34390 if (error > ToleranceForVertexMismatchInPolygons::Tolerable_error)
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 >
34404 ToleranceForVertexMismatchInPolygons::Tolerable_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 >
34435 ToleranceForVertexMismatchInPolygons::Tolerable_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 >
34451 ToleranceForVertexMismatchInPolygons::Tolerable_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 "
34462 << ToleranceForVertexMismatchInPolygons::Tolerable_error
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 "
34505 << ToleranceForVertexMismatchInPolygons::Tolerable_error
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
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;
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(
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
34687 el_pt->node_pt(0)->get_coordinates_on_boundary(bound, bound_left);
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)
34701 ->get_coordinates_on_boundary(bound, bound_right);
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
34728 unsigned n_vertex = tmp_vector_vertex_node.size();
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,
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],
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(
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
34876 if (error > ToleranceForVertexMismatchInPolygons::Tolerable_error)
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
34889 if (error > ToleranceForVertexMismatchInPolygons::Tolerable_error)
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 >
34918 ToleranceForVertexMismatchInPolygons::Tolerable_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 >
34932 ToleranceForVertexMismatchInPolygons::Tolerable_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 "
34942 << ToleranceForVertexMismatchInPolygons::Tolerable_error
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 "
34983 << ToleranceForVertexMismatchInPolygons::Tolerable_error
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
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
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))) <
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
35199 if (error < ToleranceForVertexMismatchInPolygons::Tolerable_error)
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))) <
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
35329 if (error < ToleranceForVertexMismatchInPolygons::Tolerable_error)
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
35360 n_vertex = compact_vector.size();
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
35366 if (n_vertex != vector_bnd_vertices.size())
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>
35394 Mesh* face_mesh_pt,
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
35448 mesh_geom_obj_pt->position(zeta_mid, R_mid);
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
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
35505 n_vertex = extended_vector.size();
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
35510 if (n_vertex != vector_bnd_vertices.size())
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>
35540 Mesh* face_mesh_pt,
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
35605 mesh_geom_obj_pt->position(zeta, vertex);
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
35623 n_vertex = extended_vector.size();
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
35628 if (n_vertex != vector_bnd_vertices.size())
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>
35659 Mesh* face_mesh_pt)
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
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,
35692 Mesh* face_mesh_pt,
35693 std::map<FiniteElement*, bool>& is_inverted,
35694 bool& inverted_face_mesh)
35695 {
35696 Mesh* tmp_unsorted_face_mesh_pt = new Mesh();
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
35748 Node* left_node_pt = first_el_pt->node_pt(0);
35750 {
35751 left_node_pt = first_el_pt->node_pt(nnod - 1);
35752 }
35753 Node* right_node_pt = last_el_pt->node_pt(nnod - 1);
35755 {
35756 right_node_pt = last_el_pt->node_pt(0);
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(
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
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!!)
35852 Node* first_node_pt = first_el_pt->node_pt(0);
35854 {
35855 first_node_pt = first_el_pt->node_pt(n_node - 1);
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
35865 if (error < ToleranceForVertexMismatchInPolygons::Tolerable_error)
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
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
35971 first_polyline_node_pt[p] = el_pt->node_pt(0);
35972 last_polyline_node_pt[p] = el_pt->node_pt(nnod - 1);
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
36009 Node* left_node_pt = first_el_pt->node_pt(0);
36011 {
36012 left_node_pt = first_el_pt->node_pt(nnod - 1);
36013 }
36014 Node* right_node_pt = last_el_pt->node_pt(nnod - 1);
36016 {
36017 right_node_pt = last_el_pt->node_pt(0);
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;
36027 first_polyline_node_pt[p] = el_pt->node_pt(0);
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;
36037 first_polyline_node_pt[p] = el_pt->node_pt(nnod - 1);
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;
36047 last_polyline_node_pt[p] = el_pt->node_pt(nnod - 1);
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;
36057 last_polyline_node_pt[p] = el_pt->node_pt(0);
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
36083 ordered_face_mesh_pt[p] = new Mesh;
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(
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
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 {
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
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
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
36804 const unsigned nsorted_boundaries_ids = sorted_boundaries_ids.size();
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
36828 if (nglobal_segments != nodes_ids.size())
36829 {
36830 std::ostringstream 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(
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;
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(
36857 }
36858#endif
36859
36860 // Get the number of sorted boundaries ids and check that it matches
36861 // with the total number of boundaries
36862 const unsigned nsorted_boundaries_ids = sorted_boundaries_ids.size();
36863#ifdef PARANOID
36864 if (nsorted_boundaries_ids != this->nboundary())
36865 {
36866 std::ostringstream 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(
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;
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(
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
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
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;
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
37207
37208#ifdef PARANOID
37209 if (!closed_polygon)
37210 {
37211 std::ostringstream 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
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
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;
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(
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
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;
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(
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;
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;
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
37754 const unsigned nnon_halo_face_element = non_halo_face_element_pt.size();
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
37831 ele_face_pt->node_pt(0)->get_coordinates_on_boundary(bound, bound_left);
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
37843 ele_face_pt->node_pt(nnode - 1)->get_coordinates_on_boundary(
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
37855 Node* first_node_pt = ele_face_pt->node_pt(0);
37856 Node* last_node_pt = ele_face_pt->node_pt(nnode - 1);
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
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
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();
37938 Node* left_node_pt = ele_face_pt->node_pt(0);
37939 Node* right_node_pt = ele_face_pt->node_pt(nnode - 1);
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
38005
38006#ifdef PARANOID
38008#endif
38009 for (unsigned eg = 0; eg < nele; eg++)
38010 {
38011 // Get the "eg-th" element
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;
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,
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(
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
38149 unsigned nnew_nodes = local_tmp_vector_vertex_node.size();
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
38197 unsigned nsub_boundaries_set = sub_vertex_nodes.size();
38199 {
38200 std::ostringstream 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
38244 unsigned n_vertex = tmp_vector_vertex_node.size();
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
38324 if (error > ToleranceForVertexMismatchInPolygons::Tolerable_error)
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 >
38338 ToleranceForVertexMismatchInPolygons::Tolerable_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 >
38369 ToleranceForVertexMismatchInPolygons::Tolerable_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 >
38385 ToleranceForVertexMismatchInPolygons::Tolerable_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 "
38396 << ToleranceForVertexMismatchInPolygons::Tolerable_error
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 "
38439 << ToleranceForVertexMismatchInPolygons::Tolerable_error
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
38706 Node* left_node_pt = ele_face_pt->node_pt(0);
38707 Node* right_node_pt = ele_face_pt->node_pt(nnodes - 1);
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 {
38721 Node* cleft_node_pt = cele_face_pt->node_pt(0);
38722 Node* cright_node_pt = cele_face_pt->node_pt(nnodes - 1);
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;
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
38858 if (!repeated_ele_face_pt->is_halo())
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
38912 ele_face_pt->node_pt(0)->get_coordinates_on_boundary(bound, bound_left);
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
38924 ele_face_pt->node_pt(nnode - 1)->get_coordinates_on_boundary(
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
38936 Node* first_node_pt = ele_face_pt->node_pt(0);
38937 Node* last_node_pt = ele_face_pt->node_pt(nnode - 1);
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
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
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 =
38996
38997#ifdef PARANOID
38999#endif
39000 for (unsigned eg = 0; eg < nele; eg++)
39001 {
39002 // Get the "eg-th" element
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
39068 if (!repeated_ele_face_pt->is_halo())
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();
39080 Node* left_node_pt = ele_face_pt->node_pt(0);
39081 Node* right_node_pt = ele_face_pt->node_pt(nlnode - 1);
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 =
39154
39155#ifdef PARANOID
39157#endif
39158 for (unsigned eg = 0; eg < nele; eg++)
39159 {
39160 // Get the "eg-th" element
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 =
39193
39194#ifdef PARANOID
39196#endif
39197 for (unsigned eg = 0; eg < nele; eg++)
39198 {
39199 // Get the "eg-th" element
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;
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,
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(
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;
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;
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
39494 unsigned n_vertex = tmp_vector_vertex_node.size();
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
39573 if (error > ToleranceForVertexMismatchInPolygons::Tolerable_error)
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 >
39587 ToleranceForVertexMismatchInPolygons::Tolerable_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 >
39618 ToleranceForVertexMismatchInPolygons::Tolerable_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 >
39633 ToleranceForVertexMismatchInPolygons::Tolerable_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 "
39644 << ToleranceForVertexMismatchInPolygons::Tolerable_error
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 "
39686 << ToleranceForVertexMismatchInPolygons::Tolerable_error
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
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
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
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
39965 const unsigned nhalo_face_shared_ele = halo_shared_face_ele_pt.size();
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;
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;
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
40026 Node* h_first_node_pt = halo_face_ele_pt->node_pt(0);
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;
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
40122 shared_face_done.size())
40123 {
40124 std::ostringstream error_message;
40125 error_message << "The number of DONE shared boundary face elements ("
40126 << shared_face_done.size()
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();
40162 Node* first_node_pt = root_face_ele_pt->node_pt(0);
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
40197 tmp_shared_face_ele_pt->node_pt(tmp_nnodes - 1);
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
40324 const unsigned n_shared_target_areas = sorted_shared_ele_pt.size();
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 {
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;
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;
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
40499
40500 // Copy the connection information from the old shared polyline to
40501 // the new one
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 =
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
40529
40531 {
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))) <
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 <
40696 ToleranceForVertexMismatchInPolygons::Tolerable_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
40774 n_vertex = compact_vector.size();
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
40785 unsigned ntarget_areas = compact_area_constraint.size();
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
40891 mesh_geom_obj_pt->position(zeta, vertex);
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
40916 n_vertex = new_vector.size();
40917 if (n_vertex != vector_bnd_vertices.size())
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 <
41044 ToleranceForVertexMismatchInPolygons::Tolerable_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
41084 unsigned n_area_constraint = area_constraint.size();
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
41124 n_vertex = compact_vector.size();
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
41134 unsigned ntarget_areas = compact_area_constraint.size();
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
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
41359 const unsigned nnon_halo_face_element = non_halo_face_element_pt.size();
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
41430 ele_face_pt->node_pt(0)->get_coordinates_on_boundary(bound, bound_left);
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
41442 ele_face_pt->node_pt(nnode - 1)->get_coordinates_on_boundary(
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
41454 Node* first_node_pt = ele_face_pt->node_pt(0);
41455 Node* last_node_pt = ele_face_pt->node_pt(nnode - 1);
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();
41484 Node* left_node_pt = ele_face_pt->node_pt(0);
41485 Node* right_node_pt = ele_face_pt->node_pt(nnode - 1);
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
41583 unsigned nnew_nodes = local_tmp_vector_vertex_node.size();
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
41624 unsigned nsub_boundaries_set = sub_vertex_nodes.size();
41626 {
41627 std::ostringstream 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
41672 unsigned n_vertex = tmp_vector_vertex_node.size();
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
41752 if (error > ToleranceForVertexMismatchInPolygons::Tolerable_error)
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 >
41766 ToleranceForVertexMismatchInPolygons::Tolerable_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 >
41797 ToleranceForVertexMismatchInPolygons::Tolerable_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 >
41813 ToleranceForVertexMismatchInPolygons::Tolerable_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 "
41824 << ToleranceForVertexMismatchInPolygons::Tolerable_error
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 "
41867 << ToleranceForVertexMismatchInPolygons::Tolerable_error
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
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
42115 Node* left_node_pt = ele_face_pt->node_pt(0);
42116 Node* right_node_pt = ele_face_pt->node_pt(nnodes - 1);
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 {
42131 Node* cleft_node_pt = cele_face_pt->node_pt(0);
42132 Node* cright_node_pt = cele_face_pt->node_pt(nnodes - 1);
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;
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
42267 if (!repeated_ele_face_pt->is_halo())
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
42316 ele_face_pt->node_pt(0)->get_coordinates_on_boundary(bound, bound_left);
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
42328 ele_face_pt->node_pt(nnode - 1)->get_coordinates_on_boundary(
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
42340 Node* first_node_pt = ele_face_pt->node_pt(0);
42341 Node* last_node_pt = ele_face_pt->node_pt(nnode - 1);
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
42370 if (!repeated_ele_face_pt->is_halo())
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();
42382 Node* left_node_pt = ele_face_pt->node_pt(0);
42383 Node* right_node_pt = ele_face_pt->node_pt(nlnode - 1);
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;
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;
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
42632 unsigned n_vertex = tmp_vector_vertex_node.size();
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
42712 if (error > ToleranceForVertexMismatchInPolygons::Tolerable_error)
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 >
42726 ToleranceForVertexMismatchInPolygons::Tolerable_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 >
42757 ToleranceForVertexMismatchInPolygons::Tolerable_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 >
42772 ToleranceForVertexMismatchInPolygons::Tolerable_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 "
42783 << ToleranceForVertexMismatchInPolygons::Tolerable_error
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 "
42826 << ToleranceForVertexMismatchInPolygons::Tolerable_error
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
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
43036 Node* first_node_pt = current_ele_pt->node_pt(0);
43037 Node* second_node_pt = current_ele_pt->node_pt(1);
43038 Node* third_node_pt = current_ele_pt->node_pt(2);
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
43106 const unsigned nnonhalo_edge_nodes = nonhalo_edge_nodes_pt.size();
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;
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
43377
43378 // Copy the connection information from the old shared polyline
43379 // to the new one
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 =
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
43407
43409 {
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
43540 // Add the node to the internal boundary
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
43564 // Add the element to the boundary elements storage of the
43565 // internal boundary
43567 // Get the face index of the boundary
43569 // Add the face index to the storage of the boundary
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 =
43587 for (unsigned ier = 0; ier < nele_ir; ier++)
43588 {
43589 // Get the boundary element in current region
43592 // Add the boundary element to the internal boundary in the
43593 // region
43595 bnd_ele_pt);
43596
43597 // Get the face index of the boundary
43600 // Add the face index to the storage of the boundary region
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
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>
43654 {
43655 // Quick return
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
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
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
43695 Node* tmp_node_pt = face_ele_pt->node_pt(i);
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
44029 mesh_geom_obj_pt->position(b_coord, new_x);
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 {
44053 dummy_six_node_element.construct_node(j);
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 {
44223 dummy_six_node_element.node_pt(j)->x(i) = el_pt->node_pt(j)->x(i);
44224 }
44225 }
44226
44227 // Local coordinate of enriched node
44228 unsigned j_enriched = 6;
44229 Vector<double> s(2);
44230 el_pt->local_coordinate_of_node(j_enriched, s);
44231
44232 // Get its position from non-enriched element
44233 Vector<double> x(2);
44234 dummy_six_node_element.interpolated_x(s, x);
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 {
44255 delete dummy_six_node_element.node_pt(j);
44256 }
44257 }
44258
44259#endif // #ifdef OOMPH_HAS_TRIANGLE_LIB
44260
44261} // namespace oomph
44262
44263#endif
Quadrilateral mesh generator; Uses input from Geompack++. See: http://members.shaw....
GeompackQuadScaffoldMesh * Tmp_mesh_pt
Temporary scaffold mesh.
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.
Helper object for dealing with the parameters used for the TriangleMesh objects.
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...
struct oomph::classcomp Bottom_left_sorter
bool operator()(const std::pair< double, double > &lhs, const std::pair< double, double > &rhs) const