multi_domain.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-2024 Matthias Heil and Andrew Hazel
7// LIC//
8// LIC// This library is free software; you can redistribute it and/or
9// LIC// modify it under the terms of the GNU Lesser General Public
10// LIC// License as published by the Free Software Foundation; either
11// LIC// version 2.1 of the License, or (at your option) any later version.
12// LIC//
13// LIC// This library is distributed in the hope that it will be useful,
14// LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16// LIC// Lesser General Public License for more details.
17// LIC//
18// LIC// You should have received a copy of the GNU Lesser General Public
19// LIC// License along with this library; if not, write to the Free Software
20// LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21// LIC// 02110-1301 USA.
22// LIC//
23// LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
24// LIC//
25// LIC//====================================================================
26// Templated multi-domain functions
27
28// Include guards to prevent multiple inclusion of the header
29#ifndef OOMPH_MULTI_DOMAIN_CC
30#define OOMPH_MULTI_DOMAIN_CC
31
32// Config header generated by autoconfig
33#ifdef HAVE_CONFIG_H
34#include <oomph-lib-config.h>
35#endif
36
37// Oomph-lib headers
38#include "geom_objects.h"
39#include "problem.h"
40#include "shape.h"
41
42#include "mesh.h"
44#include "algebraic_elements.h"
46#include "Qelements.h"
48#include "multi_domain.h"
50
51// Needed to check if elements have nonuniformly spaced nodes
52#include "refineable_elements.h"
53#include "Qspectral_elements.h"
54
55namespace oomph
56{
57 /// / Templated helper functions for multi-domain methods using locate_zeta
58
59
60 //============================================================================
61 /// Identify the \c FaceElements (stored in the mesh pointed to by
62 /// \c face_mesh_pt) that are adjacent to the bulk elements next to the
63 /// \c boundary_in_bulk_mesh -th boundary of the mesh pointed to by
64 /// \c bulk_mesh_pt. The \c FaceElements must be derived
65 /// from the \c ElementWithExternalElement base class and the adjacent
66 /// bulk elements are stored as their external elements.
67 ///
68 /// This is the vector-based version which deals with multiple bulk
69 /// mesh boundaries at the same time.
70 //============================================================================
71 template<class BULK_ELEMENT, unsigned DIM>
73 Problem* problem_pt,
75 Mesh* const& bulk_mesh_pt,
77 const unsigned& interaction)
78 {
80
81#ifdef PARANOID
82 // Check sizes match
84 {
85 std::ostringstream error_message;
86 error_message << "Sizes of vector of boundary ids in bulk mesh ("
88 << ") and vector of pointers\n"
89 << "to FaceElements (" << face_mesh_pt.size()
90 << " doesn't match.\n";
91 throw OomphLibError(
93 }
94#endif
95
96 // Create face meshes adjacent to the bulk mesh's b-th boundary.
97 // Each face mesh consists of FaceElements that may also be
98 // interpreted as GeomObjects
100
101 // Loop over all meshes
102 for (unsigned i_mesh = 0; i_mesh < n_mesh; i_mesh++)
103 {
108
109 // Loop over these new face elements and tell them the boundary number
110 // from the bulk mesh -- this is required to they can
111 // get access to the boundary coordinates!
112 unsigned n_face_element = bulk_face_mesh_pt[i_mesh]->nelement();
113 for (unsigned e = 0; e < n_face_element; e++)
114 {
115 // Cast the element pointer to the correct thing!
118 bulk_face_mesh_pt[i_mesh]->element_pt(e));
119
120 // Set bulk boundary number
121 el_pt->set_boundary_number_in_bulk_mesh(boundary_in_bulk_mesh[i_mesh]);
122
123 // Doc?
124 if (Doc_boundary_coordinate_file.is_open())
125 {
126 Vector<double> s(DIM - 1);
129 unsigned n_plot = 5;
131
132 // Loop over plot points
134 for (unsigned iplot = 0; iplot < num_plot_points; iplot++)
135 {
136 // Get local coordinates of plot point
140 for (unsigned i = 0; i < DIM; i++)
141 {
142 Doc_boundary_coordinate_file << x[i] << " ";
143 }
144 for (unsigned i = 0; i < DIM - 1; i++)
145 {
147 }
148 Doc_boundary_coordinate_file << std::endl;
149 }
151 n_plot);
152 }
153 }
154
155 // Now sort the elements based on the boundary coordinates.
156 // This may allow a faster implementation of the locate_zeta
157 // function for the MeshAsGeomObject representation of this
158 // mesh, but also creates a unique ordering of the elements
159 std::sort(bulk_face_mesh_pt[i_mesh]->element_pt().begin(),
160 bulk_face_mesh_pt[i_mesh]->element_pt().end(),
162 } // end of loop over meshes
163
164
165 // Setup the interactions for this problem using the surface mesh
166 // on the fluid mesh. The interaction parameter in this instance is
167 // given by the "face" parameter.
172
173
174 // Loop over all meshes to clean up
175 for (unsigned i_mesh = 0; i_mesh < n_mesh; i_mesh++)
176 {
177 unsigned n_face_element = bulk_face_mesh_pt[i_mesh]->nelement();
178
179 // The MeshAsGeomObject has already been deleted (in set_external_storage)
180
181 // Must be careful with the FaceMesh, because we cannot delete the nodes
182 // Loop over the elements backwards (paranoid!) and delete them
183 for (unsigned e = n_face_element; e > 0; e--)
184 {
185 delete bulk_face_mesh_pt[i_mesh]->element_pt(e - 1);
186 bulk_face_mesh_pt[i_mesh]->element_pt(e - 1) = 0;
187 }
188 // Now clear all element and node storage (should maybe fine-grain this)
189 bulk_face_mesh_pt[i_mesh]->flush_element_and_node_storage();
190
191 // Finally delete the mesh
193
194 } // end of loop over meshes
195 }
196
197
198 //========================================================================
199 /// Identify the \c FaceElements (stored in the mesh pointed to by
200 /// \c face_mesh_pt) that are adjacent to the bulk elements next to the
201 /// \c boundary_in_bulk_mesh -th boundary of the mesh pointed to by
202 /// \c bulk_mesh_pt. The \c FaceElements must be derived
203 /// from the \c ElementWithExternalElement base class and the adjacent
204 /// bulk elements are stored as their external elements.
205 //========================================================================
206 template<class BULK_ELEMENT, unsigned DIM>
208 Problem* problem_pt,
209 const unsigned& boundary_in_bulk_mesh,
210 Mesh* const& bulk_mesh_pt,
211 Mesh* const& face_mesh_pt,
212 const unsigned& interaction)
213 {
214 // Convert to vector-based storage
219
220 // Call vector-based version
222 problem_pt,
227 }
228
229
230 //========================================================================
231 /// Set up the two-way multi-domain interactions for the
232 /// problem pointed to by \c problem_pt.
233 /// Use this for cases where first_mesh_pt and second_mesh_pt
234 /// occupy the same physical space and are populated by
235 /// ELEMENT_0 and ELEMENT_1 respectively, and are combined to solve
236 /// a single problem. The elements in two meshes interact both ways
237 /// the elements in each mesh act as "external elements" for the
238 /// elements in the "other" mesh. The interaction indices allow the
239 /// specification of which interaction we're setting up in the two
240 /// meshes. They default to zero, which is appropriate if there's
241 /// only a single interaction.
242 //========================================================================
243 template<class ELEMENT_0, class ELEMENT_1>
245 Problem* problem_pt,
246 Mesh* const& first_mesh_pt,
247 Mesh* const& second_mesh_pt,
248 const unsigned& first_interaction,
249 const unsigned& second_interaction)
250 {
251 // Delete all the current external halo(ed) element and node storage
252 first_mesh_pt->delete_all_external_storage();
253 second_mesh_pt->delete_all_external_storage();
254
255 // Call setup_multi_domain_interaction in both directions
258
261 }
262
263
264 //========================================================================
265 /// Function to set up the one-way multi-domain interaction for
266 /// problems where the meshes pointed to by \c mesh_pt and \c external_mesh_pt
267 /// occupy the same physical space, and the elements in \c external_mesh_pt
268 /// act as "external elements" for the \c ElementWithExternalElements
269 /// in \c mesh_pt (but not vice versa):
270 /// - \c mesh_pt points to the mesh of ElemenWithExternalElements for which
271 /// the interaction is set up.
272 /// - \c external_mesh_pt points to the mesh that contains the elements
273 /// of type EXT_ELEMENT that act as "external elements" for the
274 /// \c ElementWithExternalElements in \c mesh_pt.
275 /// - The interaction_index parameter defaults to zero and must be otherwise
276 /// set by the user if there is more than one mesh that provides sources
277 /// for the Mesh pointed to by mesh_pt.
278 //========================================================================
279 template<class EXT_ELEMENT>
281 Problem* problem_pt,
282 Mesh* const& mesh_pt,
283 Mesh* const& external_mesh_pt,
284 const unsigned& interaction_index)
285 {
286 // Bulk elements must not be external elements in this case
288
289 // Call the auxiliary function with GEOM_OBJECT=EXT_ELEMENT
290 // and EL_DIM_EUL=EL_DIM_LAG=dimension returned from helper function
291 get_dim_helper(problem_pt, mesh_pt, external_mesh_pt);
292
293 if (Dim > 3)
294 {
295 std::ostringstream error_stream;
296 error_stream << "The elements within the two interacting meshes have a\n"
297 << "dimension not equal to 1, 2 or 3.\n"
298 << "The multi-domain method will not work in this case.\n"
299 << "The dimension is: " << Dim << "\n";
300 throw OomphLibError(
302 }
303
304 // Wrapper for each dimension (template parameter)
306 problem_pt, mesh_pt, external_mesh_pt, interaction_index);
307 }
308
309
310 //========================================================================
311 /// Function to set up the one-way multi-domain interaction for
312 /// FSI-like problems.
313 /// - \c mesh_pt points to the mesh of \c ElemenWithExternalElements for which
314 /// the interaction is set up. In an FSI example, this mesh would contain
315 /// the \c FSIWallElements (either beam/shell elements or the
316 /// \c FSISolidTractionElements that apply the traction to
317 /// a "bulk" solid mesh that is loaded by the fluid.)
318 /// - \c external_mesh_pt points to the mesh that contains the elements
319 /// of type EXT_ELEMENT that provide the "source" for the
320 /// \c ElementWithExternalElements. In an FSI example, this
321 /// mesh would contain the "bulk" fluid elements.
322 /// - \c external_face_mesh_pt points to the mesh of \c FaceElements
323 /// attached to the \c external_mesh_pt. The mesh pointed to by
324 /// \c external_face_mesh_pt has the same dimension as \c mesh_pt.
325 /// The elements contained in \c external_face_mesh_pt are of type
326 /// FACE_ELEMENT_GEOM_OBJECT. In an FSI example, these elements
327 /// are usually the \c FaceElementAsGeomObjects (templated by the
328 /// type of the "bulk" fluid elements to which they are attached)
329 /// that define the FSI boundary of the fluid domain.
330 /// - The interaction_index parameter defaults to zero and must otherwise be
331 /// set by the user if there is more than one mesh that provides "external
332 /// elements" for the Mesh pointed to by mesh_pt (e.g. in the case
333 /// when a beam or shell structure is loaded by fluid from both sides.)
334 //========================================================================
335 template<class EXT_ELEMENT, class FACE_ELEMENT_GEOM_OBJECT>
337 Problem* problem_pt,
338 Mesh* const& mesh_pt,
339 Mesh* const& external_mesh_pt,
341 const unsigned& interaction_index)
342 {
343 // Bulk elements must be external elements in this case
345
346 // Call the auxiliary routine with GEOM_OBJECT=FACE_ELEMENT_GEOM_OBJECT
347 // and EL_DIM_LAG=Dim, EL_DIM_EUL=Dim+1
348 get_dim_helper(problem_pt, mesh_pt, external_face_mesh_pt);
349
350 if (Dim > 2)
351 {
352 std::ostringstream error_stream;
353 error_stream << "The elements within the two interacting meshes have a\n"
354 << "dimension not equal to 1 or 2.\n"
355 << "The multi-domain method will not work in this case.\n"
356 << "The dimension is: " << Dim << "\n";
357 throw OomphLibError(
359 }
360
361 // Call the function that actually does the work
363 problem_pt,
364 mesh_pt,
368 }
369
370
371 //========================================================================
372 /// Function to set up the one-way multi-domain interaction for
373 /// FSI-like problems.
374 /// - \c mesh_pt points to the mesh of \c ElemenWithExternalElements for which
375 /// the interaction is set up. In an FSI example, this mesh would contain
376 /// the \c FSIWallElements (either beam/shell elements or the
377 /// \c FSISolidTractionElements that apply the traction to
378 /// a "bulk" solid mesh that is loaded by the fluid.)
379 /// - \c external_mesh_pt points to the mesh that contains the elements
380 /// of type EXT_ELEMENT that provide the "source" for the
381 /// \c ElementWithExternalElements. In an FSI example, this
382 /// mesh would contain the "bulk" fluid elements.
383 /// - \c external_face_mesh_pt points to the mesh of \c FaceElements
384 /// attached to the \c external_mesh_pt. The mesh pointed to by
385 /// \c external_face_mesh_pt has the same dimension as \c mesh_pt.
386 /// The elements contained in \c external_face_mesh_pt are of type
387 /// FACE_ELEMENT_GEOM_OBJECT. In an FSI example, these elements
388 /// are usually the \c FaceElementAsGeomObjects (templated by the
389 /// type of the "bulk" fluid elements to which they are attached)
390 /// that define the FSI boundary of the fluid domain.
391 /// - The interaction_index parameter defaults to zero and must otherwise be
392 /// set by the user if there is more than one mesh that provides "external
393 /// elements" for the Mesh pointed to by mesh_pt (e.g. in the case
394 /// when a beam or shell structure is loaded by fluid from both sides.)
395 /// .
396 /// Vector-based version operates simultaneously on the meshes contained
397 /// in the vectors.
398 //========================================================================
399 template<class EXT_ELEMENT, class FACE_ELEMENT_GEOM_OBJECT>
401 Problem* problem_pt,
402 const Vector<Mesh*>& mesh_pt,
403 Mesh* const& external_mesh_pt,
405 const unsigned& interaction_index)
406 {
407 // How many meshes do we have?
408 unsigned n_mesh = mesh_pt.size();
409
410#ifdef PARANOID
412 {
413 std::ostringstream error_stream;
414 error_stream << "Sizes of external_face_mesh_pt [ "
415 << external_face_mesh_pt.size() << " ] and "
416 << "mesh_pt [ " << n_mesh << " ] don't match.\n";
417 throw OomphLibError(
419 }
420#endif
421
422 // Bail out?
423 if (n_mesh == 0) return;
424
425 // Bulk elements must be external elements in this case
427
428 // Call the auxiliary routine with GEOM_OBJECT=FACE_ELEMENT_GEOM_OBJECT
429 // and EL_DIM_LAG=Dim, EL_DIM_EUL=Dim+1. Use first mesh only.
430 get_dim_helper(problem_pt, mesh_pt[0], external_face_mesh_pt[0]);
431
432
433#ifdef PARANOID
434 // Check consitency
435 unsigned old_dim = Dim;
436 for (unsigned i = 1; i < n_mesh; i++)
437 {
438 // Set up Dim again
439 get_dim_helper(problem_pt, mesh_pt[i], external_face_mesh_pt[i]);
440
441 if (Dim != old_dim)
442 {
443 std::ostringstream error_stream;
444 error_stream << "Inconsistency: Mesh " << i << " has Dim=" << Dim
445 << "while mesh 0 has Dim=" << old_dim << std::endl;
446 throw OomphLibError(
448 }
449 }
450#endif
451
452 if (Dim > 2)
453 {
454 std::ostringstream error_stream;
455 error_stream << "The elements within the two interacting meshes have a\n"
456 << "dimension not equal to 1 or 2.\n"
457 << "The multi-domain method will not work in this case.\n"
458 << "The dimension is: " << Dim << "\n";
459 throw OomphLibError(
461 }
462
463 // Now do the actual work for all meshes simultaneously
465 problem_pt,
466 mesh_pt,
470 }
471
472
473 //========================================================================
474 /// This routine calls the locate_zeta routine (simultaneously on each
475 /// processor for each individual processor's element set if necessary)
476 /// and sets up the external (halo) element and node storage as
477 /// necessary. The locate_zeta procedure here works for all multi-domain
478 /// problems where either two meshes occupy the same physical space but have
479 /// differing element types (e.g. a Boussinesq convection problem where
480 /// AdvectionDiffusion elements interact with Navier-Stokes type elements)
481 /// or two meshes interact along some boundary of the external mesh,
482 /// represented by a "face mesh", such as an FSI problem.
483 //========================================================================
484 template<class EXT_ELEMENT, class GEOM_OBJECT>
486 Problem* problem_pt,
487 Mesh* const& mesh_pt,
488 Mesh* const& external_mesh_pt,
489 const unsigned& interaction_index,
491 {
492 // Convert to vector-based storage
494 mesh_pt_vector[0] = mesh_pt;
497
498 // Call vector-based version
500 problem_pt,
505
506 } // end of aux_setup_multi_domain_interaction
507
508
509 //========================================================================
510 /// This routine calls the locate_zeta routine (simultaneously on each
511 /// processor for each individual processor's element set if necessary)
512 /// and sets up the external (halo) element and node storage as
513 /// necessary. The locate_zeta procedure here works for all multi-domain
514 /// problems where either two meshes occupy the same physical space but have
515 /// differing element types (e.g. a Boussinesq convection problem where
516 /// AdvectionDiffusion elements interact with Navier-Stokes type elements)
517 /// or two meshes interact along some boundary of the external mesh,
518 /// represented by a "face mesh", such as an FSI problem.
519 ///
520 /// Vector-based version operates simultaneously on the meshes contained
521 /// in the vectors.
522 //========================================================================
523 template<class EXT_ELEMENT, class GEOM_OBJECT>
525 Problem* problem_pt,
526 const Vector<Mesh*>& mesh_pt,
527 Mesh* const& external_mesh_pt,
528 const unsigned& interaction_index,
530 {
531 // How many meshes do we have?
532 unsigned n_mesh = mesh_pt.size();
533
534#ifdef PARANOID
536 {
537 std::ostringstream error_stream;
538 error_stream << "Sizes of external_face_mesh_pt [ "
539 << external_face_mesh_pt.size() << " ] and "
540 << "mesh_pt [ " << n_mesh << " ] don't match.\n";
541 throw OomphLibError(
543 }
544#endif
545
546 // Bail out?
547 if (n_mesh == 0) return;
548
549 // Multi-domain setup will not work for elements with
550 // nonuniformly spaced nodes
551 // Must check type of elements in the mesh and in the external mesh
552 //(assume element type is the same for all elements in each mesh)
553
554#ifdef PARANOID
555
556 // Pointer to first element in external mesh
558 if (external_mesh_pt->nelement() != 0)
559 {
560 ext_el_pt_0 = external_mesh_pt->element_pt(0);
561 }
562
563 // Loop over all meshes
564 for (unsigned i_mesh = 0; i_mesh < n_mesh; i_mesh++)
565 {
566 // Get pointer to first element in each mesh
568 if (mesh_pt[i_mesh]->nelement() != 0)
569 {
570 el_pt_0 = mesh_pt[i_mesh]->element_pt(0);
571 }
572
573 // Check they are not spectral elements
574 if (dynamic_cast<SpectralElement*>(el_pt_0) != 0 ||
575 dynamic_cast<SpectralElement*>(ext_el_pt_0) != 0)
576 {
577 throw OomphLibError(
578 "Multi-domain setup does not work with spectral elements.",
581 }
582
583 // Check they are not hp-refineable elements
584 if (dynamic_cast<PRefineableElement*>(el_pt_0) != 0 ||
585 dynamic_cast<PRefineableElement*>(ext_el_pt_0) != 0)
586 {
587 throw OomphLibError(
588 "Multi-domain setup does not work with hp-refineable elements.",
591 }
592 } // end over initial loop over meshes
593
594#endif
595
596
597#ifdef OOMPH_HAS_MPI
598 // Storage for number of processors and my rank
599 int n_proc = problem_pt->communicator_pt()->nproc();
600 int my_rank = problem_pt->communicator_pt()->my_rank();
601#endif
602
603 // Timing
604 double t_start = 0.0;
605 double t_end = 0.0;
606 double t_local = 0.0;
607 double t_set = 0.0;
608 double t_locate = 0.0;
609 double t_spiral_start = 0.0;
610#ifdef OOMPH_HAS_MPI
611 double t_loop_start = 0.0;
612 double t_sendrecv = 0.0;
613 double t_missing = 0.0;
614 double t_send_info = 0.0;
615 double t_create_halo = 0.0;
616#endif
617
618 if (Doc_timings)
619 {
621 }
622
623 // Initialize number of zeta coordinates not found yet
624 unsigned n_zeta_not_found = 0;
625
626 // Geometric objects used to represent the external (face) meshes
628
629#ifdef PARANOID
630
631 // Initialise lagrangian dimension of element (test only)
632 unsigned el_dim_lag = 0;
633
634#endif
635
636 // Create mesh as geom objects for all meshes
637 for (unsigned i_mesh = 0; i_mesh < n_mesh; i_mesh++)
638 {
639 // Are bulk elements used as external elements?
641 {
642 // Fix this when required
643 if (n_mesh != 1)
644 {
645 std::ostringstream error_stream;
647 << "Sorry I currently can't deal with non-bulk external elements\n"
648 << "in multi-domain setup for multiple meshes.\n"
649 << "The functionality should be easy to implement now that you\n"
650 << "have a test case. If you're not willinig to do this, call\n"
651 << "the multi-domain setup mesh-by-mesh instead (though this can\n"
652 << "be costly in parallel because of global comms. \n";
653 throw OomphLibError(error_stream.str(),
656 }
657
658 // Set the geometric object from the external mesh
660 }
661 else
662 {
663 // Set the geometric object from the external face mesh argument
666 }
667
668#ifdef PARANOID
669 unsigned old_el_dim_lag = el_dim_lag;
670
671 // Set lagrangian dimension of element
673
674 // Check consistency
675 if (i_mesh > 0)
676 {
678 {
679 std::ostringstream error_stream;
680 error_stream << "Lagrangian dimensions of elements don't match \n "
681 << "between meshes: " << el_dim_lag << " "
682 << old_el_dim_lag << "\n";
683 throw OomphLibError(error_stream.str(),
686 }
687 }
688#endif
689
690
691 } // end of loop over meshes
692
693 double t_setup_lookups = 0.0;
694 if (Doc_timings)
695 {
697 oomph_info << "CPU for creation of MeshAsGeomObjects and bin structure: "
698 << t_set - t_start << std::endl;
700 }
701
702 // Total number of integration points
703 unsigned tot_int = 0;
704
705 // Counter for total number of elements (in flat packed order)
706 unsigned e_count = 0;
707
708 // Loop over all meshes to get total number of elements
709 for (unsigned i_mesh = 0; i_mesh < n_mesh; i_mesh++)
710 {
711 e_count += mesh_pt[i_mesh]->nelement();
712 }
714
715 // Reset counter for elements in flat packed storage
716 e_count = 0;
717
718 // Loop over all meshes
719 for (unsigned i_mesh = 0; i_mesh < n_mesh; i_mesh++)
720 {
721 // Loop over (this processor's) elements and set lookup array
722 unsigned n_element = mesh_pt[i_mesh]->nelement();
723 for (unsigned e = 0; e < n_element; e++)
724 {
725 // Zero-sized vector means its a halo
728 dynamic_cast<ElementWithExternalElement*>(
729 mesh_pt[i_mesh]->element_pt(e));
730
731#ifdef OOMPH_HAS_MPI
732 // We're not setting up external elements for halo elements
733 if (!el_pt->is_halo())
734#endif
735 {
736 // We need to allocate storage for the external elements
737 // within the element. Memory will actually only be
738 // allocated the first time this function is called for
739 // each element, or if the number of interactions or integration
740 // points within the element has changed.
741 el_pt->initialise_external_element_storage();
742
743 // Clear any previous allocation
744 unsigned n_intpt = el_pt->integral_pt()->nweight();
745 for (unsigned ipt = 0; ipt < n_intpt; ipt++)
746 {
747 el_pt->external_element_pt(interaction_index, ipt) = 0;
748 }
749
751 for (unsigned ipt = 0; ipt < n_intpt; ipt++)
752 {
754 tot_int++;
755 }
756 }
757 // next element
758 e_count++;
759 }
760 } // end of loop over meshes
761
762 if (Doc_timings)
763 {
764 double t = TimingHelpers::timer();
766 << "CPU for setup of lookup schemes for located elements/coords: "
767 << t - t_setup_lookups << std::endl;
768 }
769
770 // Initialise maximum spiral level within the cartesian bin structure
771 // Used to terminate spiraling for non-refineable bin
772 unsigned n_max_level = 0;
773
774#ifdef OOMPH_HAS_MPI
775 unsigned max_level_reached = 1;
776#endif
777
778 // Max. number of sample points -- used to decide on termination of
779 // "spiraling"
781
782 // Loop over all meshes
783 for (unsigned i_mesh = 0; i_mesh < n_mesh; i_mesh++)
784 {
785 if (mesh_geom_obj_pt[i_mesh]->sample_point_container_version() ==
787 {
789 mesh_geom_obj_pt[i_mesh]->sample_point_container_pt());
790
792 ->last_sample_point_to_actually_lookup_during_locate_zeta() =
794 ->initial_last_sample_point_to_actually_lookup_during_locate_zeta();
796 ->first_sample_point_to_actually_lookup_during_locate_zeta() = 0;
797
798 unsigned nsp =
799 bin_array_pt->total_number_of_sample_points_computed_recursively();
801 {
803 }
804
805
806#ifdef OOMPH_HAS_MPI
807 // If the mesh has been distributed we want the max. number
808 // of sample points across all processors
809 if (problem_pt->communicator_pt()->nproc() > 1)
810 {
813
814 // Get maximum over all processors
817 1,
819 MPI_MAX,
820 problem_pt->communicator_pt()->mpi_comm());
821 }
822#endif
823 }
824 else if (mesh_geom_obj_pt[i_mesh]->sample_point_container_version() ==
826 {
828 dynamic_cast<NonRefineableBinArray*>(
829 mesh_geom_obj_pt[i_mesh]->sample_point_container_pt());
830
831 // Initialise spiral levels
832 bin_array_pt->current_min_spiral_level() = 0;
833 bin_array_pt->current_max_spiral_level() =
834 bin_array_pt->n_spiral_chunk() - 1;
835
836 // Find maximum spiral level within the cartesian bin structure
837 n_max_level = bin_array_pt->max_bin_dimension();
838
839 // Limit it
840 if (bin_array_pt->current_max_spiral_level() > n_max_level)
841 {
842 bin_array_pt->current_max_spiral_level() = n_max_level - 1;
843 }
844 }
845#ifdef OOMPH_HAS_CGAL
846 // CGAL
847 else if (mesh_geom_obj_pt[i_mesh]->sample_point_container_version() ==
849 {
851 dynamic_cast<CGALSamplePointContainer*>(
852 mesh_geom_obj_pt[i_mesh]->sample_point_container_pt());
854 ->last_sample_point_to_actually_lookup_during_locate_zeta() =
856 ->initial_last_sample_point_to_actually_lookup_during_locate_zeta();
858 ->first_sample_point_to_actually_lookup_during_locate_zeta() = 0;
859
860 unsigned nsp =
861 bin_array_pt->total_number_of_sample_points_computed_recursively();
863 {
865 }
866
867
868#ifdef OOMPH_HAS_MPI
869 // If the mesh has been distributed we want the max. number
870 // of sample points across all processors
871 if (problem_pt->communicator_pt()->nproc() > 1)
872 {
875
876 // Get maximum over all processors
879 1,
881 MPI_MAX,
882 problem_pt->communicator_pt()->mpi_comm());
883 }
884#endif
885 }
886#endif // cgal
887 }
888
889
890 // Storage for info about coordinate location
893
894 // Loop over "spirals/levels" away from the current position
895 // Note: All meshes go through their spirals simultaneously;
896 // read out spiral level from first one
897 unsigned i_level = 0;
900 {
901 // Record time at start of spiral loop
902 if (Doc_timings)
903 {
905 }
906
907 // Perform locate_zeta locally first! This looks locally for
908 // all not-yet-located zetas within the current spiral range.
911
912 // Store stats about successful locates for reporting later
913 if (Doc_stats)
914 {
915 unsigned count_locates = 0;
917 for (unsigned e = 0; e < n_ext_loc; e++)
918 {
920 for (unsigned ipt = 0; ipt < n_intpt; ipt++)
921 {
923 }
924 }
925
926 // Store percentage of integration points successfully located.
927 // Only assign if we had anything to allocte, otherwise 100%
928 // (default assignment; see above) is correct
929 if (tot_int != 0)
930 {
932 100.0 * double(count_locates) / double(tot_int));
933 }
934 else
935 {
936 // Had none to find so we found them all!
937 percentage_coords_located_locally.push_back(100.0);
938 }
939 }
940
941
942 // Now test whether anything needs to be broadcast elsewhere
943 // (i.e. were there any failures in the locate method above?)
944 // If there are, then the zetas for these failures need to be
945 // broadcast...
946
947 // How many zetas have we failed to find? [Note: Array is padded
948 // by Dim padded entries (DBL_MAX) for each mesh]
951
952 if (Doc_timings)
953 {
955 oomph_info << "CPU for local location of zeta coordinate [spiral level "
956 << i_level << "]: " << t_local - t_spiral_start << std::endl
957 << "Number of missing zetas: " << n_zeta_not_found
958 << std::endl;
959 }
960
961
962#ifdef OOMPH_HAS_MPI
963 // Only perform the reduction operation if there's more than one process
964 if (problem_pt->communicator_pt()->nproc() > 1)
965 {
969 1,
971 MPI_SUM,
972 problem_pt->communicator_pt()->mpi_comm());
973 }
974
975 // If we have missing zetas on any process
976 // and the problem is distributed, we need to locate elsewhere
977 if ((n_zeta_not_found != 0) &&
978 (problem_pt->problem_has_been_distributed()))
979 {
980 // Timings
981 double t_sendrecv_min = DBL_MAX;
982 double t_sendrecv_max = -DBL_MAX;
983 double t_sendrecv_tot = 0.0;
984
985 double t_missing_min = DBL_MAX;
986 double t_missing_max = -DBL_MAX;
987 double t_missing_tot = 0.0;
988
989 double t_send_info_min = DBL_MAX;
990 double t_send_info_max = -DBL_MAX;
991 double t_send_info_tot = 0.0;
992
993 double t_create_halo_min = DBL_MAX;
994 double t_create_halo_max = -DBL_MAX;
995 double t_create_halo_tot = 0.0;
996
997 // Start ring communication: Loop (number of processes - 1)
998 // starting from 1. The variable iproc represents the "distance" from
999 // the current process to the process for which it is attempting
1000 // to locate an element for the current set of not-yet-located
1001 // zeta coordinates
1002 unsigned ring_count = 0;
1003 for (int iproc = 1; iproc < n_proc; iproc++)
1004 {
1005 // Record time at start of loop
1006 if (Doc_timings)
1007 {
1009 }
1010
1011 // Send the zeta values you haven't found to the
1012 // next process, receive from the previous process:
1013 // (Padded) Flat_packed_zetas_not_found_locally are sent
1014 // to next processor where they are received as
1015 // (padded) Received_flat_packed_zetas_to_be_found.
1017
1018 if (Doc_timings)
1019 {
1020 ring_count++;
1027 }
1028
1029 // Perform the locate_zeta for the new set of zetas on this process
1032
1033 if (Doc_timings)
1034 {
1039 }
1040
1041 // Send any located coordinates back to the correct process, and
1042 // prepare to send on to the next process if necessary
1044
1045 if (Doc_timings)
1046 {
1053 }
1054
1055 // Create any located external halo elements on the current process
1057 iproc, mesh_pt, external_mesh_pt, problem_pt, interaction_index);
1058
1059 if (Doc_timings)
1060 {
1067 }
1068
1069 // Do we have any further locating to do or have we found
1070 // everything at this level of the ring communication?
1071 // Only perform the reduction operation if there's more than
1072 // one process [Note: Array is padded
1073 // by DIM times DBL_MAX entries for each mesh]
1076
1077
1078#ifdef OOMPH_HAS_MPI
1079 if (problem_pt->communicator_pt()->nproc() > 1)
1080 {
1084 1,
1086 MPI_SUM,
1087 problem_pt->communicator_pt()->mpi_comm());
1088 }
1089#endif
1090
1091 // If its is now zero then break out of the ring comms loop
1092 if (n_zeta_not_found == 0)
1093 {
1094 if (Doc_timings)
1095 {
1097 oomph_info << "BREAK N-1: CPU for entrire spiral [spiral level "
1098 << i_level << "]: " << t_local - t_spiral_start
1099 << std::endl;
1100 }
1101 break;
1102 }
1103 }
1104
1105
1106 // Doc timings
1107 if (Doc_timings)
1108 {
1109 oomph_info << "Ring-based search continued until iteration "
1110 << ring_count << " out of a maximum of "
1111 << problem_pt->communicator_pt()->nproc() - 1 << "\n";
1112 oomph_info << "Total, av, max, min CPU for send/recv of remaining "
1113 "zeta coordinates: "
1114 << t_sendrecv_tot << " "
1115 << t_sendrecv_tot / double(ring_count) << " "
1116 << t_sendrecv_max << " " << t_sendrecv_min << "\n";
1117 oomph_info << "Total, av, max, min CPU for location of missing zeta "
1118 "coordinates : "
1119 << t_missing_tot << " "
1120 << t_missing_tot / double(ring_count) << " "
1121 << t_missing_max << " " << t_missing_min << "\n";
1122 oomph_info << "Total, av, max, min CPU for send/recv of new element "
1123 "info : "
1124 << t_send_info_tot << " "
1125 << t_send_info_tot / double(ring_count) << " "
1126 << t_send_info_max << " " << t_send_info_min << "\n";
1127 oomph_info << "Total, av, max, min CPU for local creation of "
1128 "external halo objects: "
1129 << t_create_halo_tot << " "
1131 << t_create_halo_max << " " << t_create_halo_min << "\n";
1132 }
1133
1134 } // end if for missing zetas on any process
1135#endif
1136
1137
1138 // Store information about location of elements for integration points
1139 if (Doc_stats)
1140 {
1141 unsigned count_locates = 0;
1143 for (unsigned e = 0; e < n_ext_loc; e++)
1144 {
1145 unsigned n_intpt = External_element_located[e].size();
1146 for (unsigned ipt = 0; ipt < n_intpt; ipt++)
1147 {
1149 }
1150 }
1151
1152
1153 // Store total percentage of locates so far.
1154 // Only assign if we had anything to allocte, otherwise 100%
1155 // (default assignment) is correct
1156 if (tot_int != 0)
1157 {
1159 100.0 * double(count_locates) / double(tot_int));
1160 }
1161 else
1162 {
1163 // Had none to find so we found them all!
1164 percentage_coords_located_locally.push_back(100.0);
1165 }
1166 }
1167
1168 // Do we have any further locating to do? If so, the remaining
1169 // zetas will (hopefully) be found at the next spiral level.
1170 // Only perform the reduction operation if there's more than one process
1171 // [Note: Array is padded
1172 // by DIM times DBL_MAX entries for each mesh]
1175
1176
1177#ifdef OOMPH_HAS_MPI
1178 if (problem_pt->communicator_pt()->nproc() > 1)
1179 {
1183 1,
1185 MPI_SUM,
1186 problem_pt->communicator_pt()->mpi_comm());
1187 }
1188
1189 // Specify max level reached for later loop
1191#endif
1192
1193 /// If it's is now zero then break out of the spirals loop
1194 if (n_zeta_not_found == 0)
1195 {
1196 if (Doc_timings)
1197 {
1199 oomph_info << "BREAK N: CPU for entrire spiral [spiral level "
1200 << i_level << "]: " << t_local - t_spiral_start
1201 << std::endl;
1202 }
1203 break;
1204 }
1205
1206 if (Doc_timings)
1207 {
1209 oomph_info << "CPU for entrire spiral [spiral level " << i_level
1210 << "]: " << t_local - t_spiral_start << std::endl;
1211 }
1212
1213 // Bump up spiral levels for all meshes
1214 i_level++;
1215 for (unsigned i_mesh = 0; i_mesh < n_mesh; i_mesh++)
1216 {
1217 if (mesh_geom_obj_pt[i_mesh]->sample_point_container_version() ==
1219 {
1221 mesh_geom_obj_pt[i_mesh]->sample_point_container_pt());
1223 ->first_sample_point_to_actually_lookup_during_locate_zeta() =
1225 ->last_sample_point_to_actually_lookup_during_locate_zeta();
1227 ->last_sample_point_to_actually_lookup_during_locate_zeta() *=
1229 ->multiplier_for_max_sample_point_to_actually_lookup_during_locate_zeta();
1230 }
1231 else if (mesh_geom_obj_pt[i_mesh]->sample_point_container_version() ==
1233 {
1235 dynamic_cast<NonRefineableBinArray*>(
1236 mesh_geom_obj_pt[i_mesh]->sample_point_container_pt());
1237
1238 bin_array_pt->current_min_spiral_level() +=
1239 bin_array_pt->n_spiral_chunk();
1240 bin_array_pt->current_max_spiral_level() +=
1241 bin_array_pt->n_spiral_chunk();
1242 }
1243#ifdef OOMPH_HAS_CGAL
1244 else if (mesh_geom_obj_pt[i_mesh]->sample_point_container_version() ==
1246 {
1248 dynamic_cast<CGALSamplePointContainer*>(
1249 mesh_geom_obj_pt[i_mesh]->sample_point_container_pt());
1251 ->first_sample_point_to_actually_lookup_during_locate_zeta() =
1253 ->last_sample_point_to_actually_lookup_during_locate_zeta();
1255 ->last_sample_point_to_actually_lookup_during_locate_zeta() *=
1257 ->multiplier_for_max_sample_point_to_actually_lookup_during_locate_zeta();
1258 }
1259#endif // cgal
1260 }
1261
1262 // Check termination criterion for while loop
1263 if (mesh_geom_obj_pt[0]->sample_point_container_version() ==
1265 {
1267 mesh_geom_obj_pt[0]->sample_point_container_pt());
1268
1269 if (bin_array_pt
1270 ->first_sample_point_to_actually_lookup_during_locate_zeta() <=
1272 {
1274 }
1275 else
1276 {
1278 }
1279 }
1280 else if (mesh_geom_obj_pt[0]->sample_point_container_version() ==
1282 {
1284 dynamic_cast<NonRefineableBinArray*>(
1285 mesh_geom_obj_pt[0]->sample_point_container_pt());
1286
1287 if (bin_array_pt->current_max_spiral_level() < n_max_level)
1288 {
1290 }
1291 else
1292 {
1294 }
1295 }
1296#ifdef OOMPH_HAS_CGAL
1297 else if (mesh_geom_obj_pt[0]->sample_point_container_version() ==
1299 {
1301 dynamic_cast<CGALSamplePointContainer*>(
1302 mesh_geom_obj_pt[0]->sample_point_container_pt());
1303
1304 if (bin_array_pt
1305 ->first_sample_point_to_actually_lookup_during_locate_zeta() <=
1307 {
1309 }
1310 else
1311 {
1313 }
1314 }
1315#endif // cgal
1316 } // end of "spirals" loop
1317
1318
1319 // If we haven't found all zetas we're dead now...
1320 //-------------------------------------------------
1321 if (n_zeta_not_found != 0)
1322 {
1323 // Shout?
1325 {
1326 std::ostringstream error_stream;
1328 << "Multi_domain_functions::locate_zeta_for_local_coordinates()"
1329 << "\nhas failed ";
1330
1331#ifdef OOMPH_HAS_MPI
1332 if (problem_pt->communicator_pt()->nproc() > 1)
1333 {
1334 error_stream << " on proc: "
1335 << problem_pt->communicator_pt()->my_rank() << std::endl;
1336 }
1337#endif
1339 << "\n\n\nThis is most likely to arise because the two meshes\n"
1340 << "that are to be matched don't overlap perfectly or\n"
1341 << "because the elements are distorted and too small a \n"
1342 << "number of sampling points has been used when setting\n"
1343 << "up the bin structure.\n\n"
1344 << "You should try to increase the value of \n"
1345 << "the number of sample points defined in \n\n"
1346 << " "
1347 "SamplePointContainerParameters::Default_nsample_points_generated_"
1348 "per_element"
1349 << "\n\n from its current value of "
1350 << SamplePointContainerParameters::
1351 Default_nsample_points_generated_per_element
1352 << "\n"
1353 << "\n\n"
1354 << "NOTE: You can suppress this error and \"accept failure\""
1355 << " by setting the public boolean \n\n"
1356 << " "
1357 "Multi_domain_functions::Accept_failed_locate_zeta_in_setup_multi_"
1358 "domain_interaction\n\n"
1359 << " to true. In this case, the pointers to external elements\n"
1360 << " that couldn't be located will remain null\n";
1361
1362 std::ostringstream modifier;
1363#ifdef OOMPH_HAS_MPI
1364 if (problem_pt->communicator_pt()->nproc() > 1)
1365 {
1366 modifier << "_proc" << problem_pt->communicator_pt()->my_rank();
1367 }
1368#endif
1369
1370 // Loop over all meshes
1371 for (unsigned i_mesh = 0; i_mesh < n_mesh; i_mesh++)
1372 {
1373 // Add yet another modifier to distinguish meshes if reqd
1374 if (n_mesh > 1)
1375 {
1376 modifier << "_mesh" << i_mesh;
1377 }
1378
1379 std::ofstream outfile;
1380 char filename[100];
1381 sprintf(
1382 filename, "missing_coords_mesh%s.dat", modifier.str().c_str());
1383 outfile.open(filename);
1384 unsigned nel = mesh_pt[i_mesh]->nelement();
1385 for (unsigned e = 0; e < nel; e++)
1386 {
1387 mesh_pt[i_mesh]->finite_element_pt(e)->output(outfile);
1388 // FiniteElement::output(outfile);
1389 }
1390 outfile.close();
1391
1392 sprintf(
1393 filename, "missing_coords_ext_mesh%s.dat", modifier.str().c_str());
1394 outfile.open(filename);
1395 nel = external_mesh_pt->nelement();
1396 for (unsigned e = 0; e < nel; e++)
1397 {
1398 external_mesh_pt->finite_element_pt(e)->output(outfile);
1399 // FiniteElement::output(outfile);
1400 }
1401 outfile.close();
1402
1403 BinArray* bin_array_pt = dynamic_cast<BinArray*>(
1404 mesh_geom_obj_pt[i_mesh]->sample_point_container_pt());
1405 if (bin_array_pt != 0)
1406 {
1407 sprintf(
1408 filename, "missing_coords_bin%s.dat", modifier.str().c_str());
1409 outfile.open(filename);
1410 bin_array_pt->output_bins(outfile);
1411 outfile.close();
1412 }
1413
1414 sprintf(filename, "missing_coords%s.dat", modifier.str().c_str());
1415 outfile.open(filename);
1416 unsigned n = External_element_located.size();
1417 error_stream << "Number of unlocated elements " << n << std::endl;
1418 for (unsigned e = 0; e < n; e++)
1419 {
1420 unsigned n_intpt = External_element_located[e].size();
1421 if (n_intpt == 0)
1422 {
1423 error_stream << "Failure to locate in halo element! "
1424 << "Why are we searching there?" << std::endl;
1425 }
1426 for (unsigned ipt = 0; ipt < n_intpt; ipt++)
1427 {
1428 if (External_element_located[e][ipt] == 0)
1429 {
1430 error_stream << "Failure at element/intpt: " << e << " " << ipt
1431 << std::endl;
1432
1433 // Cast
1435 dynamic_cast<ElementWithExternalElement*>(
1436 mesh_pt[i_mesh]->element_pt(e));
1437
1438 unsigned n_dim_el = el_pt->dim();
1440 for (unsigned i = 0; i < n_dim_el; i++)
1441 {
1442 s[i] = el_pt->integral_pt()->knot(ipt, i);
1443 }
1444 unsigned n_dim = el_pt->node_pt(0)->ndim();
1446 el_pt->interpolated_x(s, x);
1447 for (unsigned i = 0; i < n_dim; i++)
1448 {
1449 error_stream << x[i] << " ";
1450 outfile << x[i] << " ";
1451 }
1452 error_stream << std::endl;
1453 outfile << std::endl;
1454 }
1455 }
1456 }
1457 outfile.close();
1458 }
1459
1461 << "Mesh and external mesh documented in missing_coords_mesh*.dat\n"
1462 << "and missing_coords_ext_mesh*.dat, respectively. Missing \n"
1463 << "coordinates in missing_coords*.dat\n";
1464 throw OomphLibError(
1466 }
1467 // Failure is deeemed to be acceptable
1468 else
1469 {
1471 << "NOTE: Haven't found " << n_zeta_not_found
1472 << " external elements in \n"
1473 << "Multi_domain_functions::aux_setup_multi_domain_interaction(...)\n"
1474 << "but this deemed to be acceptable because \n"
1475 << "Multi_domain_functions::Accept_failed_locate_zeta_in_setup_multi_"
1476 "domain_interaction\n"
1477 << "is true.\n";
1478 }
1479 }
1480
1481
1482 // Doc timings if required
1483 if (Doc_timings)
1484 {
1487 << "Total CPU for location and creation of all external elements: "
1488 << t_locate - t_start << std::endl;
1489 }
1490
1491 // Delete the geometric object representing the mesh
1492 for (unsigned i_mesh = 0; i_mesh < n_mesh; i_mesh++)
1493 {
1494 delete mesh_geom_obj_pt[i_mesh];
1495 }
1496
1497 // Clean up all the (extern) Vectors associated with creating the
1498 // external storage information
1499 clean_up();
1500
1501#ifdef OOMPH_HAS_MPI
1502
1503 // Output information about external storage if required
1504 if (Doc_stats)
1505 {
1506 // Report stats regarding location method
1507 bool comm_was_required = false;
1508 oomph_info << "-------------------------------------------" << std::endl;
1509 oomph_info << "- Cumulative percentage of locate success -" << std::endl;
1510 oomph_info << "--- Spiral -- Found local -- Found else ---" << std::endl;
1511 for (unsigned level = 0; level < max_level_reached; level++)
1512 {
1513 oomph_info << "--- " << level << " -- "
1514 << percentage_coords_located_locally[level] << " -- "
1515 << percentage_coords_located_elsewhere[level] << " ---"
1516 << std::endl;
1517 // Has communication with other processors at this level actually
1518 // produced any results?
1521 {
1522 comm_was_required = true;
1523 }
1524 }
1525 oomph_info << "-------------------------------------------" << std::endl;
1526
1527
1528 // No need for any of this malaki if we're not running in parallel
1529 if (problem_pt->communicator_pt()->nproc() > 1)
1530 {
1531 // Initialise to indicate that none of the zetas required
1532 // on this processor were located through parallel ring search,
1533 // i.e. comm was not required and we could have done some
1534 // more local searching first
1535 oomph_info << std::endl;
1536 oomph_info << "ASSESSMENT OF NEED FOR PARALLEL SEARCH: \n";
1537 oomph_info << "=======================================\n";
1538 unsigned status = 0;
1540 {
1541 oomph_info << "- Ring-based parallel search did successfully locate "
1542 "zetas on proc "
1543 << my_rank << std::endl;
1544 status = 1;
1545 }
1546 else
1547 {
1548 if (max_level_reached > 1)
1549 {
1551 << "- Ring-based parallel search did NOT locate zetas on proc"
1552 << my_rank << std::endl;
1553 }
1554 else
1555 {
1557 << "- No ring-based parallel search was performed on proc"
1558 << my_rank << std::endl;
1559 }
1560 }
1561
1562 // Allreduce to check if anyone has benefitted from parallel ring
1563 // search
1564 unsigned overall_status = 0;
1567 1,
1569 MPI_MAX,
1570 problem_pt->communicator_pt()->mpi_comm());
1571
1572 // Report of mpi was useful to anyone
1573 if (overall_status == 1)
1574 {
1575 oomph_info << "- Ring-based, parallel search did succesfully\n";
1576 oomph_info << " locate zetas on at least one other proc, so it\n";
1577 oomph_info << " was worthwhile.\n";
1578 oomph_info << std::endl;
1579 }
1580 else
1581 {
1582 if (max_level_reached > 1)
1583 {
1585 << "- Ring-based, parallel search did NOT locate zetas\n";
1586 oomph_info << " on ANY other procs, i.e it was useless.\n";
1588 << " --> We should really have done more local search\n";
1590 << " by reducing number of bins, or doing more spirals\n";
1591 oomph_info << " in one go before initiating parallel search.\n";
1592 oomph_info << std::endl;
1593 }
1594 else
1595 {
1596 oomph_info << "- No ring-based, parallel search was performed\n";
1597 oomph_info << " or necessary. Perfect!\n";
1598 oomph_info << std::endl;
1599 }
1600 }
1601 }
1602
1603 // How many external elements does the external mesh have now?
1604 oomph_info << "------------------------------------------" << std::endl;
1605 oomph_info << "External mesh: I have " << external_mesh_pt->nelement()
1606 << " elements, and " << std::endl
1607 << external_mesh_pt->nexternal_halo_element()
1608 << " external halo elements, "
1609 << external_mesh_pt->nexternal_haloed_element()
1610 << " external haloed elements" << std::endl;
1611
1612 // How many external nodes does each submesh have now?
1613 oomph_info << "------------------------------------------" << std::endl;
1614 oomph_info << "External mesh: I have " << external_mesh_pt->nnode()
1615 << " nodes, and " << std::endl
1616 << external_mesh_pt->nexternal_halo_node()
1617 << " external halo nodes, "
1618 << external_mesh_pt->nexternal_haloed_node()
1619 << " external haloed nodes" << std::endl;
1620 oomph_info << "------------------------------------------" << std::endl;
1621 }
1622
1623 // Output further information about (external) halo(ed)
1624 // elements and nodes if required
1625 if (Doc_full_stats)
1626 {
1627 // How many elements does this submesh have for each of the processors?
1628 for (int iproc = 0; iproc < n_proc; iproc++)
1629 {
1630 oomph_info << "----------------------------------------" << std::endl;
1631 oomph_info << "With process " << iproc << " there are "
1632 << external_mesh_pt->nroot_halo_element(iproc)
1633 << " root halo elements, and "
1634 << external_mesh_pt->nroot_haloed_element(iproc)
1635 << " root haloed elements" << std::endl
1636 << "and there are "
1637 << external_mesh_pt->nexternal_halo_element(iproc)
1638 << " external halo elements, and "
1639 << external_mesh_pt->nexternal_haloed_element(iproc)
1640 << " external haloed elements." << std::endl;
1641
1642 oomph_info << "----------------------------------------" << std::endl;
1643 oomph_info << "With process " << iproc << " there are "
1644 << external_mesh_pt->nhalo_node(iproc) << " halo nodes, and "
1645 << external_mesh_pt->nhaloed_node(iproc) << " haloed nodes"
1646 << std::endl
1647 << "and there are "
1648 << external_mesh_pt->nexternal_halo_node(iproc)
1649 << " external halo nodes, and "
1650 << external_mesh_pt->nexternal_haloed_node(iproc)
1651 << " external haloed nodes." << std::endl;
1652 }
1653 oomph_info << "-----------------------------------------" << std::endl
1654 << std::endl;
1655 }
1656
1657#endif
1658
1659 // Doc timings if required
1660 if (Doc_timings)
1661 {
1663 oomph_info << "CPU for (one way) aux_setup_multi_domain_interaction: "
1664 << t_end - t_start << std::endl;
1665 }
1666
1667 } // end of aux_setup_multi_domain_interaction
1668
1669#ifdef OOMPH_HAS_MPI
1670
1671 //=====================================================================
1672 /// Creates external (halo) elements on the loop process based on the
1673 /// information received from each locate_zeta call on other processes.
1674 /// vector based version
1675 //=====================================================================
1676 template<class EXT_ELEMENT>
1678 int& iproc,
1679 const Vector<Mesh*>& mesh_pt,
1680 Mesh* const& external_mesh_pt,
1681 Problem* problem_pt,
1682 const unsigned& interaction_index)
1683 {
1684 OomphCommunicator* comm_pt = problem_pt->communicator_pt();
1685 int my_rank = comm_pt->my_rank();
1686
1687 // Reset counters for flat packed unsigneds (namespace data because
1688 // it's also accessed by helper functions)
1691
1692 // Initialise counter for stepping through zetas
1693 unsigned zeta_counter = 0;
1694
1695 // Initialise counter for stepping through flat-packed located
1696 // coordinates
1697 unsigned counter_for_located_coord = 0;
1698
1699 // Counter for elements in flag packed storage
1700 unsigned e_count = 0;
1701
1702 // Loop over all meshes
1703 unsigned n_mesh = mesh_pt.size();
1704 for (unsigned i_mesh = 0; i_mesh < n_mesh; i_mesh++)
1705 {
1706 // The creation all happens on the current processor
1707 // Loop over this processors elements
1708 unsigned n_element = mesh_pt[i_mesh]->nelement();
1709 for (unsigned e = 0; e < n_element; e++)
1710 {
1711 // Cast to ElementWithExternalElement to set external element
1712 // (if located)
1714 dynamic_cast<ElementWithExternalElement*>(
1715 mesh_pt[i_mesh]->element_pt(e));
1716
1717 // We're not setting up external elements for halo elements
1718 // (Note: this is consistent with padding introduced when
1719 // External_element_located was first assigned)
1720 if (!el_pt->is_halo())
1721 {
1722 // Loop over integration points
1723 unsigned n_intpt = el_pt->integral_pt()->nweight();
1724 for (unsigned ipt = 0; ipt < n_intpt; ipt++)
1725 {
1726 // Has an external element been assigned to this integration point?
1728 {
1729 // Was a (non-halo) element located for this integration point
1731 my_rank) ||
1733 {
1734 // Either it was already found, or not found on the current
1735 // proc. In either case, we don't need to do anything for this
1736 // integration point
1737 }
1738 else
1739 {
1740 // Get the process number on which the element was located
1741 unsigned loc_p =
1743
1744 // Is it a new external halo element or not?
1745 // If so, then create it, populate it, and add it as a
1746 // source; if not, then find the right one which
1747 // has already been created and use it as the source
1748 // element.
1749
1750 // FiniteElement stored at this integration point
1752
1753 // Is it a new element?
1755 {
1756 // Create a new element from the communicated values
1757 // and coords from the process that located zeta
1759
1760 // Add external halo element to this mesh
1761 external_mesh_pt->add_external_halo_element_pt(loc_p,
1762 new_el_pt);
1763
1764 // Cast to the FE pointer
1765 f_el_pt = dynamic_cast<FiniteElement*>(new_el_pt);
1766
1767 // We need the number of interpolated values if Refineable
1768 int n_cont_inter_values = -1;
1769 if (dynamic_cast<RefineableElement*>(new_el_pt) != 0)
1770 {
1772 dynamic_cast<RefineableElement*>(new_el_pt)
1773 ->ncont_interpolated_values();
1774 }
1775
1776 // If we're using macro elements to update
1777#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
1780 << " Using macro element node update "
1782 << std::endl;
1783#endif
1786 {
1787 // Set the macro element
1789 dynamic_cast<MacroElementNodeUpdateMesh*>(
1791
1792#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
1794 << " Number of macro element "
1797 << std::endl;
1798#endif
1802 macro_mesh_pt->macro_domain_pt()->macro_element_pt(
1803 macro_el_num));
1804
1805
1806 // We need to receive the lower left
1807 // and upper right coordinates of the macro element
1809 dynamic_cast<QElementBase*>(new_el_pt);
1810 if (q_el_pt != 0)
1811 {
1812 unsigned el_dim = q_el_pt->dim();
1813 for (unsigned i_dim = 0; i_dim < el_dim; i_dim++)
1814 {
1815 q_el_pt->s_macro_ll(i_dim) = Flat_packed_doubles
1817 q_el_pt->s_macro_ur(i_dim) = Flat_packed_doubles
1819 }
1820 }
1821 else // Throw an error, since this is only implemented for Q
1822 {
1823 std::ostringstream error_stream;
1824 error_stream << "Using MacroElement node update\n"
1825 << "in a case with non-QElements\n"
1826 << "has not yet been implemented.\n";
1827 throw OomphLibError(error_stream.str(),
1830 }
1831 }
1832
1833 // Now we add nodes to the new element
1834 unsigned n_node = f_el_pt->nnode();
1835 for (unsigned j = 0; j < n_node; j++)
1836 {
1837 Node* new_nod_pt = 0;
1838
1839 // Call the add external halo node helper function
1841 new_nod_pt,
1843 loc_p,
1844 j,
1845 f_el_pt,
1847 problem_pt);
1848 }
1849 }
1850 else // the element already exists as an external_halo
1851 {
1852#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
1855 << " Index of existing external halo element "
1857 << std::endl;
1858#endif
1859 // The index itself is in Flat_packed_unsigneds[...]
1860 unsigned external_halo_el_index =
1862
1863 // Use this index to get the element
1864 f_el_pt = dynamic_cast<FiniteElement*>(
1865 external_mesh_pt->external_halo_element_pt(
1867
1868 // If it's not a finite element die
1869 if (f_el_pt == 0)
1870 {
1871 throw OomphLibError(
1872 "External halo element is not a FiniteElement\n",
1875 }
1876 }
1877
1878 // The source element storage was initialised but
1879 // not filled earlier, so do it now
1880 // The located coordinates are required
1881 // (which could be a different dimension to zeta, e.g. in FSI)
1882 unsigned el_dim = f_el_pt->dim();
1884 for (unsigned i = 0; i < el_dim; i++)
1885 {
1886 s_located[i] =
1889 }
1890
1891 // Set the element for this integration point
1892 el_pt->external_element_pt(interaction_index, ipt) = f_el_pt;
1893 el_pt->external_element_local_coord(interaction_index, ipt) =
1894 s_located;
1895
1896 // Set the lookup array to true
1898 }
1899
1900 // Increment the integration point counter
1901 zeta_counter++;
1902 }
1903 } // end loop over integration points
1904 } // end of is halo
1905
1906 // Bump flat-packed element counter
1907 e_count++;
1908
1909 } // end of loop over elements
1910
1911 // Bump up zeta counter to skip over padding entry at end of
1912 // mesh
1913 zeta_counter++;
1914
1915 } // end loop over meshes
1916 }
1917
1918
1919 //============start of add_external_halo_node_to_storage===============
1920 /// Helper function to add external halo nodes, including any masters,
1921 /// based on information received from the haloed process
1922 //=========================================================================
1923 template<class EXT_ELEMENT>
1925 Node*& new_nod_pt,
1926 Mesh* const& external_mesh_pt,
1927 unsigned& loc_p,
1928 unsigned& node_index,
1929 FiniteElement* const& new_el_pt,
1931 Problem* problem_pt)
1932 {
1933 // Add the external halo node if required
1934 add_external_halo_node_helper(new_nod_pt,
1936 loc_p,
1937 node_index,
1938 new_el_pt,
1940 problem_pt);
1941
1942 // Recursively add masters
1944 new_nod_pt,
1946 loc_p,
1947 node_index,
1948 new_el_pt,
1950 problem_pt);
1951 }
1952
1953
1954 //========================================================================
1955 /// Recursively add masters of external halo nodes (and their masters, etc)
1956 /// based on information received from the haloed process
1957 //=========================================================================
1958 template<class EXT_ELEMENT>
1961 Node*& new_nod_pt,
1962 Mesh* const& external_mesh_pt,
1963 unsigned& loc_p,
1964 unsigned& node_index,
1965 FiniteElement* const& new_el_pt,
1967 Problem* problem_pt)
1968 {
1969 for (int i_cont = -1; i_cont < n_cont_inter_values; i_cont++)
1970 {
1971#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
1973 << " Boolean to indicate that continuously interpolated "
1974 "variable i_cont "
1975 << i_cont << " is hanging "
1977 << std::endl;
1978#endif
1980 {
1981#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
1983 << " Number of master nodes "
1985 << std::endl;
1986#endif
1987 unsigned n_master =
1989
1990 // Setup new HangInfo
1992 for (unsigned m = 0; m < n_master; m++)
1993 {
1994 Node* master_nod_pt = 0;
1995 // Get the master node (creating and adding it if required)
1997 new_nod_pt,
1999 loc_p,
2001 problem_pt);
2002
2003 // Get the weight and set the HangInfo
2004 double master_weight =
2006 hang_pt->set_master_node_pt(m, master_nod_pt, master_weight);
2007
2008 // Recursively add masters of master
2012 loc_p,
2013 node_index,
2014 new_el_pt,
2016 problem_pt);
2017 }
2018 new_nod_pt->set_hanging_pt(hang_pt, i_cont);
2019 }
2020 } // end loop over continous interpolated values
2021 }
2022
2023 //========================================================================
2024 /// Helper function to add external halo node that is a master
2025 //========================================================================
2026 template<class EXT_ELEMENT>
2029 Node*& new_nod_pt,
2030 Mesh* const& external_mesh_pt,
2031 unsigned& loc_p,
2032 int& ncont_inter_values,
2033 Problem* problem_pt)
2034 {
2035 // Given the node and the external mesh, and received information
2036 // about them from process loc_p, construct them on the current process
2037#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2040 << " Boolean to trigger construction of new external halo master node "
2042#endif
2044 {
2045 // Construct a new node based upon sent information
2048 }
2049 else
2050 {
2051#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2053 << " index of existing external halo master node "
2055 << std::endl;
2056#endif
2057 // Copy node from received location
2058 new_master_nod_pt = external_mesh_pt->external_halo_node_pt(
2060 }
2061 }
2062
2063 //======start of construct_new_external_halo_master_node_helper===========
2064 /// Helper function which constructs a new external halo master node
2065 /// with the required information sent from the haloed process
2066 //========================================================================
2067 template<class EXT_ELEMENT>
2070 Node*& nod_pt,
2071 unsigned& loc_p,
2072 Mesh* const& external_mesh_pt,
2073 Problem* problem_pt)
2074 {
2075 // First three sent numbers are dimension, position type and nvalue
2076 // (to be used in Node constructors)
2077#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2079 << " ndim for external halo master node "
2081 << std::endl;
2082#endif
2084#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2086 << " nposition type for external halo master node "
2088 << std::endl;
2089#endif
2090 unsigned n_position_type =
2092#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2094 << " nvalue for external halo master node "
2096 << std::endl;
2097#endif
2098 unsigned n_value =
2100
2101 // If it's a solid node also receive the lagrangian dimension and pos type
2102 SolidNode* solid_nod_pt = dynamic_cast<SolidNode*>(nod_pt);
2103 unsigned n_lag_dim;
2104 unsigned n_lag_type;
2105 if (solid_nod_pt != 0)
2106 {
2107#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2109 << " nlagrdim for external halo master solid node "
2111 << std::endl;
2112#endif
2114#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2116 << " nlagrtype for external halo master solid node "
2118 << std::endl;
2119#endif
2121 }
2122
2123 // Null TimeStepper for now
2124 TimeStepper* time_stepper_pt = 0;
2125 // Default number of previous values to 1
2126 unsigned n_prev = 1;
2127
2128 // The first entry in nodal_info indicates
2129 // the timestepper required for this halo node
2130#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2132 << " Boolean: need timestepper "
2134 << std::endl;
2135#endif
2137 {
2138#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2140 << " Index minus one of timestepper "
2142 << std::endl;
2143#endif
2144 // Index minus one!
2145 time_stepper_pt = problem_pt->time_stepper_pt(
2147
2148 // Check whether number of prev values is "sent" across
2149 n_prev = time_stepper_pt->ntstorage();
2150 }
2151
2152 // Is the node for which the master is required Algebraic, Macro or Solid?
2153 AlgebraicNode* alg_nod_pt = dynamic_cast<AlgebraicNode*>(nod_pt);
2155 dynamic_cast<MacroElementNodeUpdateNode*>(nod_pt);
2156
2157 // What type of node was the node for which we are constructing a master?
2158 if (alg_nod_pt != 0)
2159 {
2160 // The master node should also be algebraic
2161#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2163 << " Boolean for algebraic boundary node "
2165 << std::endl;
2166#endif
2167 // If this master node's haloed copy is on a boundary then
2168 // it needs to be on the same boundary here
2170 {
2171 // Create a new BoundaryNode (not attached to an element)
2172 if (time_stepper_pt != 0)
2173 {
2175 time_stepper_pt, n_dim, n_position_type, n_value);
2176 }
2177 else
2178 {
2181 }
2182
2183 // How many boundaries does the algebraic master node live on?
2184#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2186 << " Number of boundaries the algebraic master node is on: "
2188 << std::endl;
2189#endif
2190 unsigned nb =
2192 for (unsigned i = 0; i < nb; i++)
2193 {
2194 // Boundary number
2195#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2197 << " Algebraic master node is on boundary "
2199 << std::endl;
2200#endif
2201 unsigned i_bnd =
2203 external_mesh_pt->add_boundary_node(i_bnd, new_master_nod_pt);
2204 }
2205
2206
2207 // Do we have additional values created by face elements?
2208#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2210 << "Number of additional values created by face element "
2211 << "for master node "
2213 << std::endl;
2214#endif
2215 unsigned n_entry =
2217 if (n_entry > 0)
2218 {
2219 // Create storage, if it doesn't already exist, for the map
2220 // that will contain the position of the first entry of
2221 // this face element's additional values,
2223 dynamic_cast<BoundaryNodeBase*>(new_master_nod_pt);
2224#ifdef PARANOID
2225 if (bnew_master_nod_pt == 0)
2226 {
2227 throw OomphLibError("Failed to cast new node to boundary node\n",
2230 }
2231#endif
2233 ->index_of_first_value_assigned_by_face_element_pt() == 0)
2234 {
2236 ->index_of_first_value_assigned_by_face_element_pt() =
2237 new std::map<unsigned, unsigned>;
2238 }
2239
2240 // Get pointer to the map of indices associated with
2241 // additional values created by face elements
2242 std::map<unsigned, unsigned>* map_pt =
2244 ->index_of_first_value_assigned_by_face_element_pt();
2245
2246 // Loop over number of entries in map
2247 for (unsigned i = 0; i < n_entry; i++)
2248 {
2249 // Read out pairs...
2250
2251#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2254 << " Key of map entry for master node"
2256 << std::endl;
2257#endif
2258 unsigned first =
2260
2261#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2264 << " Value of map entry for master node"
2266 << std::endl;
2267#endif
2268 unsigned second =
2270
2271 // ...and assign
2272 (*map_pt)[first] = second;
2273 }
2274 }
2275 }
2276 else
2277 {
2278 // Create node (not attached to any element)
2279 if (time_stepper_pt != 0)
2280 {
2282 new AlgebraicNode(time_stepper_pt, n_dim, n_position_type, n_value);
2283 }
2284 else
2285 {
2288 }
2289 }
2290
2291 // Add this as an external halo node BEFORE considering node update!
2292 external_mesh_pt->add_external_halo_node_pt(loc_p, new_master_nod_pt);
2293
2294 // The external mesh is itself Algebraic...
2296 dynamic_cast<AlgebraicMesh*>(external_mesh_pt);
2297
2298#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2300 << " algebraic node update id for master node "
2302 << std::endl;
2303#endif
2304 /// The first entry of All_unsigned_values is the default node update id
2305 unsigned update_id =
2307
2308 // Setup algebraic node update info for this new node
2309 Vector<double> ref_value;
2310
2311#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2313 << " algebraic node number of ref values for master node "
2315 << std::endl;
2316#endif
2317 // The size of this vector is in the next entry
2318 unsigned n_ref_val =
2320
2321 // The reference values are in the subsequent entries of All_double_values
2322 ref_value.resize(n_ref_val);
2323 for (unsigned i_ref = 0; i_ref < n_ref_val; i_ref++)
2324 {
2325 ref_value[i_ref] =
2327 }
2328
2329 // Also require a Vector of geometric objects
2330 Vector<GeomObject*> geom_object_pt;
2331
2332#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2334 << " algebraic node number of geom objects for master node "
2336 << std::endl;
2337#endif
2338
2339 // The size of this vector is in the next entry of All_unsigned_values
2340 unsigned n_geom_obj =
2342
2343 // The remaining indices are in the rest of
2344 // All_alg_nodal_info
2345 geom_object_pt.resize(n_geom_obj);
2346 for (unsigned i_geom = 0; i_geom < n_geom_obj; i_geom++)
2347 {
2348#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2350 << " algebraic node: " << i_geom << "-th out of "
2351 << n_geom_obj << "-th geom index "
2353 << std::endl;
2354#endif
2355 unsigned geom_index =
2357
2358 // This index indicates which (if any) of the AlgebraicMesh's
2359 // stored geometric objects should be used
2360 geom_object_pt[i_geom] = alg_mesh_pt->geom_object_list_pt(geom_index);
2361 }
2362
2364 dynamic_cast<AlgebraicNode*>(new_master_nod_pt);
2365
2366 /// ... so for the specified update_id, call
2367 /// add_node_update_info
2368 alg_master_nod_pt->add_node_update_info(
2369 update_id, alg_mesh_pt, geom_object_pt, ref_value);
2370
2371 /// Now call update_node_update
2372 alg_mesh_pt->update_node_update(alg_master_nod_pt);
2373 }
2374 else if (macro_nod_pt != 0)
2375 {
2376 // The master node should also be a macro node
2377 // If this master node's haloed copy is on a boundary then
2378 // it needs to be on the same boundary here
2379#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2381 << " Boolean for master algebraic node is boundary node "
2383 << std::endl;
2384#endif
2386 {
2387 // Create a new BoundaryNode (not attached to an element)
2388 if (time_stepper_pt != 0)
2389 {
2391 time_stepper_pt, n_dim, n_position_type, n_value);
2392 }
2393 else
2394 {
2397 }
2398
2399
2400 // How many boundaries does the macro element master node live on?
2401#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2404 << " Number of boundaries the macro element master node is on: "
2406 << std::endl;
2407#endif
2408 unsigned nb =
2410 for (unsigned i = 0; i < nb; i++)
2411 {
2412 // Boundary number
2413#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2415 << " Macro element master node is on boundary "
2417 << std::endl;
2418#endif
2419 unsigned i_bnd =
2421 external_mesh_pt->add_boundary_node(i_bnd, new_master_nod_pt);
2422 }
2423
2424 // Do we have additional values created by face elements?
2425#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2427 << " Number of additional values created by face element "
2428 << "for macro element master node "
2430 << std::endl;
2431#endif
2432 unsigned n_entry =
2434 if (n_entry > 0)
2435 {
2436 // Create storage, if it doesn't already exist, for the map
2437 // that will contain the position of the first entry of
2438 // this face element's additional values,
2440 dynamic_cast<BoundaryNodeBase*>(new_master_nod_pt);
2441#ifdef PARANOID
2442 if (bnew_master_nod_pt == 0)
2443 {
2444 throw OomphLibError("Failed to cast new node to boundary node\n",
2447 }
2448#endif
2450 ->index_of_first_value_assigned_by_face_element_pt() == 0)
2451 {
2453 ->index_of_first_value_assigned_by_face_element_pt() =
2454 new std::map<unsigned, unsigned>;
2455 }
2456
2457 // Get pointer to the map of indices associated with
2458 // additional values created by face elements
2459 std::map<unsigned, unsigned>* map_pt =
2461 ->index_of_first_value_assigned_by_face_element_pt();
2462
2463 // Loop over number of entries in map
2464 for (unsigned i = 0; i < n_entry; i++)
2465 {
2466 // Read out pairs...
2467
2468#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2471 << " Key of map entry for macro element master node"
2473 << std::endl;
2474#endif
2475 unsigned first =
2477
2478#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2481 << " Value of map entry for macro element master node"
2483 << std::endl;
2484#endif
2485 unsigned second =
2487
2488 // ...and assign
2489 (*map_pt)[first] = second;
2490 }
2491 }
2492 }
2493 else
2494 {
2495 // Create node (not attached to any element)
2496 if (time_stepper_pt != 0)
2497 {
2499 time_stepper_pt, n_dim, n_position_type, n_value);
2500 }
2501 else
2502 {
2505 }
2506 }
2507
2508 // Add this as an external halo node
2509 external_mesh_pt->add_external_halo_node_pt(loc_p, new_master_nod_pt);
2510
2511 // Create a new node update element for this master node if required
2513#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2515 << " Bool: need new external halo element "
2517 << std::endl;
2518#endif
2520 {
2522
2523 // Add external hal element to this mesh
2524 external_mesh_pt->add_external_halo_element_pt(loc_p,
2526
2527 // Cast to finite element
2529 dynamic_cast<FiniteElement*>(new_node_update_el_pt);
2530
2531 // Need number of interpolated values if Refineable
2533 if (dynamic_cast<RefineableElement*>(new_node_update_f_el_pt) != 0)
2534 {
2537 ->ncont_interpolated_values();
2538 }
2539 else
2540 {
2542 }
2543#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2545 << " Bool: we have a macro element mesh "
2547 << std::endl;
2548#endif
2549 // If we're using macro elements to update,
2551 {
2552 // Set the macro element
2555
2556#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2558 << " Number of macro element "
2560 << std::endl;
2561#endif
2562 unsigned macro_el_num =
2565 macro_mesh_pt->macro_domain_pt()->macro_element_pt(macro_el_num));
2566
2567 // we need to receive
2568 // the lower left and upper right coordinates of the macro
2570 dynamic_cast<QElementBase*>(new_node_update_f_el_pt);
2571 if (q_el_pt != 0)
2572 {
2573 unsigned el_dim = q_el_pt->dim();
2574 for (unsigned i_dim = 0; i_dim < el_dim; i_dim++)
2575 {
2576 q_el_pt->s_macro_ll(i_dim) =
2578 q_el_pt->s_macro_ur(i_dim) =
2580 }
2581 }
2582 else // Throw an error
2583 {
2584 std::ostringstream error_stream;
2585 error_stream << "You are using a MacroElement node update\n"
2586 << "in a case with non-QElements. This has not\n"
2587 << "yet been implemented.\n";
2588 throw OomphLibError(error_stream.str(),
2591 }
2592 }
2593
2594 unsigned n_node = new_node_update_f_el_pt->nnode();
2595 for (unsigned j = 0; j < n_node; j++)
2596 {
2597 Node* new_nod_pt = 0;
2599 new_nod_pt,
2601 loc_p,
2602 j,
2605 problem_pt);
2606 }
2607 }
2608 else // The node update element exists already
2609 {
2610#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2612 << " Number of already existing external halo element "
2614 << std::endl;
2615#endif
2616 new_node_update_f_el_pt = dynamic_cast<FiniteElement*>(
2617 external_mesh_pt->external_halo_element_pt(
2619 }
2620
2621 // Remaining required information to create functioning
2622 // MacroElementNodeUpdateNode...
2623
2624 // Get the required geom objects for the node update
2625 // from the mesh
2626 Vector<GeomObject*> geom_object_vector_pt;
2629 geom_object_vector_pt = macro_mesh_pt->geom_object_vector_pt();
2630
2631 // Cast to MacroElementNodeUpdateNode
2634
2635 // Set all required information - node update element,
2636 // local coordinate in this element, and then set node update info
2637 macro_master_nod_pt->node_update_element_pt() = new_node_update_f_el_pt;
2638
2639 // Need to get the local node index of the macro_master_nod_pt
2640 unsigned local_node_index;
2641 unsigned n_node = new_node_update_f_el_pt->nnode();
2642 for (unsigned j = 0; j < n_node; j++)
2643 {
2645 {
2647 break;
2648 }
2649 }
2650
2654
2655 macro_master_nod_pt->set_node_update_info(new_node_update_f_el_pt,
2657 geom_object_vector_pt);
2658 }
2659 else if (solid_nod_pt != 0)
2660 {
2661 // The master node should also be a SolidNode
2662 // If this node was on a boundary then it needs to
2663 // be on the same boundary here
2664#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2666 << " Bool master is a boundary (solid) node "
2668 << std::endl;
2669#endif
2671 {
2672 // Construct a new boundary node
2673 if (time_stepper_pt != 0)
2674 {
2675 new_master_nod_pt = new BoundaryNode<SolidNode>(time_stepper_pt,
2676 n_lag_dim,
2677 n_lag_type,
2678 n_dim,
2680 n_value);
2681 }
2682 else
2683 {
2686 }
2687
2688
2689 // How many boundaries does the macro element master node live on?
2690#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2692 << " Number of boundaries the solid master node is on: "
2694 << std::endl;
2695#endif
2696 unsigned nb =
2698 for (unsigned i = 0; i < nb; i++)
2699 {
2700 // Boundary number
2701#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2703 << " Solid master node is on boundary "
2705 << std::endl;
2706#endif
2707 unsigned i_bnd =
2709 external_mesh_pt->add_boundary_node(i_bnd, new_master_nod_pt);
2710 }
2711
2712 // Do we have additional values created by face elements?
2713#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2715 << " Number of additional values created by face element "
2716 << "for solid master node "
2718 << std::endl;
2719#endif
2720 unsigned n_entry =
2722 if (n_entry > 0)
2723 {
2724 // Create storage, if it doesn't already exist, for the map
2725 // that will contain the position of the first entry of
2726 // this face element's additional values,
2728 dynamic_cast<BoundaryNodeBase*>(new_master_nod_pt);
2729#ifdef PARANOID
2730 if (bnew_master_nod_pt == 0)
2731 {
2732 throw OomphLibError("Failed to cast new node to boundary node\n",
2735 }
2736#endif
2738 ->index_of_first_value_assigned_by_face_element_pt() == 0)
2739 {
2741 ->index_of_first_value_assigned_by_face_element_pt() =
2742 new std::map<unsigned, unsigned>;
2743 }
2744
2745 // Get pointer to the map of indices associated with
2746 // additional values created by face elements
2747 std::map<unsigned, unsigned>* map_pt =
2749 ->index_of_first_value_assigned_by_face_element_pt();
2750
2751 // Loop over number of entries in map
2752 for (unsigned i = 0; i < n_entry; i++)
2753 {
2754 // Read out pairs...
2755
2756#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2759 << " Key of map entry for solid master node"
2761 << std::endl;
2762#endif
2763 unsigned first =
2765
2766#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2769 << " Value of map entry for solid master node"
2771 << std::endl;
2772#endif
2773 unsigned second =
2775
2776 // ...and assign
2777 (*map_pt)[first] = second;
2778 }
2779 }
2780 }
2781 else
2782 {
2783 // Construct an ordinary (non-boundary) node
2784 if (time_stepper_pt != 0)
2785 {
2786 new_master_nod_pt = new SolidNode(time_stepper_pt,
2787 n_lag_dim,
2788 n_lag_type,
2789 n_dim,
2791 n_value);
2792 }
2793 else
2794 {
2797 }
2798 }
2799
2800 // Add this as an external halo node
2801 external_mesh_pt->add_external_halo_node_pt(loc_p, new_master_nod_pt);
2802
2803 // Copy across particular info required for SolidNode
2804 // NOTE: Are there any problems with additional values for SolidNodes?
2806 dynamic_cast<SolidNode*>(new_master_nod_pt);
2807 unsigned n_solid_val =
2808 solid_master_nod_pt->variable_position_pt()->nvalue();
2809 for (unsigned i_val = 0; i_val < n_solid_val; i_val++)
2810 {
2811 for (unsigned t = 0; t < n_prev; t++)
2812 {
2813 solid_master_nod_pt->variable_position_pt()->set_value(
2815 }
2816 }
2817 }
2818 else // Just an ordinary node!
2819 {
2820#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2822 << " Bool node is on boundary "
2824 << std::endl;
2825#endif
2826
2827 // If this node was on a boundary then it needs to
2828 // be on the same boundary here
2830 {
2831 // Construct a new boundary node
2832 if (time_stepper_pt != 0)
2833 {
2835 time_stepper_pt, n_dim, n_position_type, n_value);
2836 }
2837 else
2838 {
2841 }
2842
2843 // How many boundaries does the master node live on?
2844#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2846 << " Number of boundaries the master node is on: "
2848 << std::endl;
2849#endif
2850 unsigned nb =
2852 for (unsigned i = 0; i < nb; i++)
2853 {
2854 // Boundary number
2855#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2857 << " Master node is on boundary "
2859 << std::endl;
2860#endif
2861 unsigned i_bnd =
2863 external_mesh_pt->add_boundary_node(i_bnd, new_master_nod_pt);
2864 }
2865
2866
2867 // Do we have additional values created by face elements?
2868#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2870 << " Number of additional values created by face element "
2871 << "for master node "
2873 << std::endl;
2874#endif
2875 unsigned n_entry =
2877 if (n_entry > 0)
2878 {
2879 // Create storage, if it doesn't already exist, for the map
2880 // that will contain the position of the first entry of
2881 // this face element's additional values,
2883 dynamic_cast<BoundaryNodeBase*>(new_master_nod_pt);
2884#ifdef PARANOID
2885 if (bnew_master_nod_pt == 0)
2886 {
2887 throw OomphLibError("Failed to cast new node to boundary node\n",
2890 }
2891#endif
2893 ->index_of_first_value_assigned_by_face_element_pt() == 0)
2894 {
2896 ->index_of_first_value_assigned_by_face_element_pt() =
2897 new std::map<unsigned, unsigned>;
2898 }
2899
2900 // Get pointer to the map of indices associated with
2901 // additional values created by face elements
2902 std::map<unsigned, unsigned>* map_pt =
2904 ->index_of_first_value_assigned_by_face_element_pt();
2905
2906 // Loop over number of entries in map
2907 for (unsigned i = 0; i < n_entry; i++)
2908 {
2909 // Read out pairs...
2910
2911#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2914 << " Key of map entry for master node"
2916 << std::endl;
2917#endif
2918 unsigned first =
2920
2921#ifdef ANNOTATE_MULTI_DOMAIN_COMMUNICATION
2924 << " Value of map entry for master node"
2926 << std::endl;
2927#endif
2928 unsigned second =
2930
2931 // ...and assign
2932 (*map_pt)[first] = second;
2933 }
2934 }
2935 }
2936 else
2937 {
2938 // Construct an ordinary (non-boundary) node
2939 if (time_stepper_pt != 0)
2940 {
2942 new Node(time_stepper_pt, n_dim, n_position_type, n_value);
2943 }
2944 else
2945 {
2947 }
2948 }
2949
2950 // Add this as an external halo node
2951 external_mesh_pt->add_external_halo_node_pt(loc_p, new_master_nod_pt);
2952 }
2953
2954 // Remaining info received for all node types
2955 // Get copied history values
2956 // unsigned n_val=new_master_nod_pt->nvalue();
2957 for (unsigned i_val = 0; i_val < n_value; i_val++)
2958 {
2959 for (unsigned t = 0; t < n_prev; t++)
2960 {
2961 new_master_nod_pt->set_value(
2963 }
2964 }
2965
2966 // Get copied history values for positions
2967 unsigned n_nod_dim = new_master_nod_pt->ndim();
2968 for (unsigned idim = 0; idim < n_nod_dim; idim++)
2969 {
2970 for (unsigned t = 0; t < n_prev; t++)
2971 {
2972 // Copy to coordinate
2973 new_master_nod_pt->x(t, idim) =
2975 }
2976 }
2977 }
2978
2979
2980#endif
2981
2982
2983} // namespace oomph
2984
2985#endif
e
Definition cfortran.h:571
static char t char * s
Definition cfortran.h:568
cstr elem_len * i
Definition cfortran.h:603
char t
Definition cfortran.h:568
///////////////////////////////////////////////////////////////////// ///////////////////////////////...
///////////////////////////////////////////////////////////////////// ///////////////////////////////...
///////////////////////////////////////////////////////////////////////////// ///////////////////////...
///////////////////////////////////////////////////////////////////// ///////////////////////////////...
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
A class that contains the information required by Nodes that are located on Mesh boundaries....
Definition nodes.h:1996
This is a base class for all elements that require external sources (e.g. FSI, multi-domain problems ...
A general Finite Element class.
Definition elements.h:1317
virtual void set_macro_elem_pt(MacroElement *macro_elem_pt)
Set pointer to macro element – can be overloaded in derived elements to perform additional tasks.
Definition elements.h:1876
Integral *const & integral_pt() const
Return the pointer to the integration scheme (const version)
Definition elements.h:1967
virtual void local_coordinate_of_node(const unsigned &j, Vector< double > &s) const
Get local coordinates of node j in the element; vector sets its own size (broken virtual)
Definition elements.h:1846
virtual std::string tecplot_zone_string(const unsigned &nplot) const
Return string for tecplot zone header (when plotting nplot points in each "coordinate direction")
Definition elements.h:3165
void interpolated_zeta(const Vector< double > &s, Vector< double > &zeta) const
Calculate the interpolated value of zeta, the intrinsic coordinate of the element when viewed as a co...
Definition elements.cc:4705
double size() const
Calculate the size of the element (length, area, volume,...) in Eulerian computational coordinates....
Definition elements.cc:4320
virtual double interpolated_x(const Vector< double > &s, const unsigned &i) const
Return FE interpolated coordinate x[i] at local coordinate s.
Definition elements.cc:3992
unsigned dim() const
Return the spatial dimension of the element, i.e. the number of local coordinates required to paramet...
Definition elements.h:2615
unsigned nnode() const
Return the number of nodes.
Definition elements.h:2214
virtual void get_s_plot(const unsigned &i, const unsigned &nplot, Vector< double > &s, const bool &shifted_to_interior=false) const
Get cector of local coordinates of plot point i (when plotting nplot points in each "coordinate direc...
Definition elements.h:3152
virtual unsigned nplot_points(const unsigned &nplot) const
Return total number of plot points (when plotting nplot points in each "coordinate direction")
Definition elements.h:3190
Node *& node_pt(const unsigned &n)
Return a pointer to the local node n.
Definition elements.h:2179
virtual void write_tecplot_zone_footer(std::ostream &outfile, const unsigned &nplot) const
Add tecplot zone "footer" to output stream (when plotting nplot points in each "coordinate direction"...
Definition elements.h:3178
A Generalised Element class.
Definition elements.h:73
bool is_halo() const
Is this element a halo?
Definition elements.h:1167
unsigned ndim() const
Access function to # of Eulerian coordinates.
unsigned nlagrangian() const
Access function to # of Lagrangian coordinates.
Class that contains data for hanging nodes.
Definition nodes.h:742
virtual double knot(const unsigned &i, const unsigned &j) const =0
Return local coordinate s[j] of i-th integration point.
virtual unsigned nweight() const =0
Return the number of integration points of the scheme.
MacroElementNodeUpdateMeshes contain MacroElementNodeUpdateNodes which have their own node update fun...
////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////// ///////////////////////////////...
A general mesh class.
Definition mesh.h:67
Nodes are derived from Data, but, in addition, have a definite (Eulerian) position in a space of a gi...
Definition nodes.h:906
unsigned ndim() const
Return (Eulerian) spatial dimension of the node.
Definition nodes.h:1054
An oomph-lib wrapper to the MPI_Comm communicator object. Just contains an MPI_Comm object (which is ...
An OomphLibError object which should be thrown when an run-time error is encountered....
/////////////////////////////////////////////////////////////////// /////////////////////////////////...
////////////////////////////////////////////////////////////////// //////////////////////////////////...
Definition problem.h:154
OomphCommunicator * communicator_pt()
access function to the oomph-lib communicator
Definition problem.h:1266
bool problem_has_been_distributed()
Access to Problem_has_been_distributed flag.
Definition problem.h:2283
TimeStepper *& time_stepper_pt()
Access function for the pointer to the first (presumably only) timestepper.
Definition problem.h:1544
/////////////////////////////////////////////////////////////////// /////////////////////////////////...
Definition Qelements.h:91
RefineableElements are FiniteElements that may be subdivided into children to provide a better local ...
A Class for nodes that deform elastically (i.e. position is an unknown in the problem)....
Definition nodes.h:1686
//////////////////////////////////////////////////////////////////////
void output(std::ostream &outfile)
Output function: x,y,u or x,y,z,u.
////////////////////////////////////////////////////////////////////// //////////////////////////////...
unsigned ntstorage() const
Return the number of doubles required to represent history (one for steady)
Vector< int > Proc_id_plus_one_of_external_element
Proc_id_plus_one_of_external_element[i] contains the processor id (plus one) of the processor on whic...
Vector< double > Flat_packed_located_coordinates
Vector of flat-packed local coordinates for zeta tuples that have been located.
void send_and_receive_located_info(int &iproc, Mesh *const &external_mesh_pt, Problem *problem_pt)
Send location information from current process; Received location information from (current process +...
unsigned Counter_for_flat_packed_unsigneds
Counter used when processing vector of flat-packed unsigneds – this is really "private" data,...
Vector< Vector< unsigned > > External_element_located
Lookup scheme for whether a local element's integration point has had an external element assigned to...
unsigned Dim
Dimension of zeta tuples (set by get_dim_helper) – needed because we store the scalar coordinates in ...
Vector< double > Flat_packed_doubles
Vector of flat-packed doubles to be communicated with other processors.
void send_and_receive_missing_zetas(Problem *problem_pt)
Send the zeta coordinates from the current process to the next process; receive from the previous pro...
void setup_bulk_elements_adjacent_to_face_mesh(Problem *problem_pt, Vector< unsigned > &boundary_in_bulk_mesh, Mesh *const &bulk_mesh_pt, Vector< Mesh * > &face_mesh_pt, const unsigned &interaction=0)
Identify the FaceElements (stored in the mesh pointed to by face_mesh_pt) that are adjacent to the bu...
void clean_up()
Helper function that clears all the information used during the external storage creation.
void setup_multi_domain_interactions(Problem *problem_pt, Mesh *const &first_mesh_pt, Mesh *const &second_mesh_pt, const unsigned &first_interaction=0, const unsigned &second_interaction=0)
Set up the two-way multi-domain interactions for the problem pointed to by problem_pt....
Vector< unsigned > Flat_packed_unsigneds
Vector of flat-packed unsigneds to be communicated with other processors – this is really "private" d...
void add_external_halo_node_to_storage(Node *&new_nod_pt, Mesh *const &external_mesh_pt, unsigned &loc_p, unsigned &node_index, FiniteElement *const &new_el_pt, int &n_cont_inter_values, Problem *problem_pt)
Helper function to add external halo nodes, including any masters, based on information received from...
void get_dim_helper(Problem *problem_pt, Mesh *const &mesh_pt, Mesh *const &external_mesh_pt)
Helper function that computes the dimension of the elements within each of the specified meshes (and ...
std::ofstream Doc_boundary_coordinate_file
Output file to document the boundary coordinate along the mesh boundary of the bulk mesh during call ...
Vector< double > Flat_packed_zetas_not_found_locally
Vector of flat-packed zeta coordinates for which the external element could not be found during curre...
void recursively_add_masters_of_external_halo_node_to_storage(Node *&new_nod_pt, Mesh *const &external_mesh_pt, unsigned &loc_p, unsigned &node_index, FiniteElement *const &new_el_pt, int &n_cont_inter_values, Problem *problem_pt)
Recursively add masters of external halo nodes (and their masters, etc) based on information received...
void locate_zeta_for_local_coordinates(const Vector< Mesh * > &mesh_pt, Mesh *const &external_mesh_pt, Vector< MeshAsGeomObject * > &mesh_geom_obj_pt, const unsigned &interaction_index)
locate zeta for current set of "local" coordinates vector-based version
void construct_new_external_halo_master_node_helper(Node *&new_master_nod_pt, Node *&nod_pt, unsigned &loc_p, Mesh *const &external_mesh_pt, Problem *problem_pt)
Helper function which constructs a new external halo master node with the information sent from the h...
void create_external_halo_elements(int &iproc, const Vector< Mesh * > &mesh_pt, Mesh *const &external_mesh_pt, Problem *problem_pt, const unsigned &interaction_index)
Create external (halo) elements on the loop process based on the information received from each locat...
void add_external_halo_master_node_helper(Node *&new_master_nod_pt, Node *&new_nod_pt, Mesh *const &external_mesh_pt, unsigned &loc_p, int &n_cont_inter_values, Problem *problem_pt)
Helper function to add external halo node that is a master.
bool Use_bulk_element_as_external
Boolean to indicate when to use the bulk element as the external element. Defaults to false,...
void setup_multi_domain_interaction(Problem *problem_pt, Mesh *const &mesh_pt, Mesh *const &external_mesh_pt, const unsigned &interaction_index=0)
Function to set up the one-way multi-domain interaction for problems where the meshes pointed to by m...
void aux_setup_multi_domain_interaction(Problem *problem_pt, Mesh *const &mesh_pt, Mesh *const &external_mesh_pt, const unsigned &interaction_index, Mesh *const &external_face_mesh_pt=0)
Auxiliary helper function.
Vector< unsigned > Located_element_status
Vector to indicate (to another processor) whether a located element (that will have to represented as...
unsigned Counter_for_flat_packed_doubles
Counter used when processing vector of flat-packed doubles – this is really "private" data,...
void locate_zeta_for_missing_coordinates(int &iproc, Mesh *const &external_mesh_pt, Problem *problem_pt, Vector< MeshAsGeomObject * > &mesh_geom_obj_pt)
Locate zeta for current set of missing coordinates; vector-based version.
bool Accept_failed_locate_zeta_in_setup_multi_domain_interaction
Boolean to indicate that failure in setup multi domain functions is acceptable; defaults to false....
double timer()
returns the time in seconds after some point in past
//////////////////////////////////////////////////////////////////// ////////////////////////////////...
OomphInfo oomph_info
Single (global) instantiation of the OomphInfo object – this is used throughout the library as a "rep...