missing_masters.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-2022 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// Non-templated missing masters functions which help to reconcile the hanging
27// status of nodes in the halo layer of distributed meshes
28
29// oomph-lib header
30#include "missing_masters.h"
32#include "mesh.h"
33#include "algebraic_elements.h"
35#include "Qelements.h"
36
37namespace oomph
38{
39 //======================================================================
40 // Namespace for "global" missing-master-locating functions
41 //======================================================================
42 namespace Missing_masters_functions
43 {
44 // Workspace for locate zeta methods
45 //----------------------------------
46
47#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
48
49 // Temporary vector of strings to enable full annotation of multi domain
50 // comms (but keep alive because it would be such a bloody pain to
51 // rewrite it if things ever go wrong again...)
52 // This is left over from the multi-domain stuff and should work
53 // in the same way, but it has not been tested.
55
56#endif
57
58 /// Boolean to indicate whether to doc timings or not.
59 bool Doc_timings = false;
60
61 /// Boolean to indicate whether to output basic info during
62 /// setup_multi_domain_interaction() routines
63 bool Doc_stats = false;
64
65 /// Boolean to indicate whether to output further info during
66 /// setup_multi_domain_interaction() routines
67 bool Doc_full_stats = false;
68
69#ifdef OOMPH_HAS_MPI
70
71
72 // Functions for location method in multi-domain problems
73
74 //========start of add_external_haloed_node_to_storage====================
75 /// Helper function to add external haloed nodes, including any masters
76 //========================================================================
78 Node* nod_pt,
79 Mesh* const& mesh_pt,
80 int& n_cont_inter_values,
81 Vector<unsigned>& send_unsigneds,
82 Vector<double>& send_doubles)
83 {
84 // Add the node if required
86 nod_pt,
87 mesh_pt,
88 n_cont_inter_values,
89 send_unsigneds,
90 send_doubles);
91
92 // Recursively add any master nodes (and their master nodes etc)
94 nod_pt,
95 mesh_pt,
96 n_cont_inter_values,
97 send_unsigneds,
98 send_doubles);
99 }
100
101
102 //========================================================================
103 /// Recursively add any master nodes (and their master nodes etc) of
104 /// external nodes
105 //========================================================================
107 int& iproc,
108 Node* nod_pt,
109 Mesh* const& mesh_pt,
110 int& n_cont_inter_values,
111 Vector<unsigned>& send_unsigneds,
112 Vector<double>& send_doubles)
113 {
114 // Loop over continuously interpolated values and add masters
115 for (int i_cont = -1; i_cont < n_cont_inter_values; i_cont++)
116 {
117 if (nod_pt->is_hanging(i_cont))
118 {
119 // Indicate that this node is a hanging node so the other
120 // process knows to create HangInfo and masters, etc.
121 send_unsigneds.push_back(1);
122#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
123 Flat_packed_unsigneds_string.push_back("Is hanging");
124#endif
125 // If this is a hanging node then add all its masters as
126 // external halo nodes if they have not yet been added
127 HangInfo* hang_pt = nod_pt->hanging_pt(i_cont);
128 // Loop over masters
129 unsigned n_master = hang_pt->nmaster();
130
131 // Indicate number of master nodes to add on other process
132 send_unsigneds.push_back(n_master);
133#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
134 Flat_packed_unsigneds_string.push_back("nmaster");
135#endif
136 for (unsigned m = 0; m < n_master; m++)
137 {
138 Node* master_nod_pt = hang_pt->master_node_pt(m);
139
140 // Call the helper function for master nodes
142 master_nod_pt,
143 mesh_pt,
144 n_cont_inter_values,
145 send_unsigneds,
146 send_doubles);
147
148 // Indicate the weight of this master
149 send_doubles.push_back(hang_pt->master_weight(m));
150
151 // Recursively add any master nodes (and their master nodes etc)
153 master_nod_pt,
154 mesh_pt,
155 n_cont_inter_values,
156 send_unsigneds,
157 send_doubles);
158 }
159 }
160 else
161 {
162 // Indicate that it's not a hanging node in this variable
163 send_unsigneds.push_back(0);
164#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
165 Flat_packed_unsigneds_string.push_back("Not hanging");
166#endif
167 }
168 } // end loop over continously interpolated values
169 }
170
171 //==========start of add_external_haloed_node_helper======================
172 /// Helper to add external haloed node that is not a master
173 //========================================================================
175 Node* nod_pt,
176 Mesh* const& mesh_pt,
177 int& n_cont_inter_values,
178 Vector<unsigned>& send_unsigneds,
179 Vector<double>& send_doubles)
180 {
181 // Check to see if this haloed node already exists in (internal)
182 // haloed node storage with any processor
183 bool found_internally = false;
184 unsigned shared_node_index = 0;
185
186 // Get vector of all shared nodes with processor iproc
187 Vector<Node*> shared_node_pt;
188 mesh_pt->get_shared_node_pt(iproc, shared_node_pt);
189
190 // Search the internal haloed storage for this node
192 std::find(shared_node_pt.begin(), shared_node_pt.end(), nod_pt);
193
194 // Check if the node was found in shared storage
195 if (it != shared_node_pt.end())
196 {
197 // Node found in (internal) haloed storage
198 found_internally = true;
199 // Store the index in this storage
200 shared_node_index = it - shared_node_pt.begin();
201 }
202
203 /// / Slow search version without additional access function in Mesh class
204 /// /Search the internal shared node storage for this node
205 // for(unsigned i=0; i<mesh_pt->nshared_node(iproc); i++)
206 // {
207 // if(nod_pt == mesh_pt->shared_node_pt(iproc,i))
208 // {
209 // //Node found in (internal) shared storage
210 // found_internally = true;
211 // shared_node_index = i;
212 // break;
213 // }
214 // }
215
216 // If we've found the node internally
217 if (found_internally)
218 {
219 // Indicate that this node doesn not need to be constructed on
220 // the other process
221 send_unsigneds.push_back(0);
222#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
223 std::stringstream junk;
224 junk << "Node was already added [size=" << send_unsigneds.size()
225 << "]; last entry: " << send_unsigneds[send_unsigneds.size() - 1];
226
227 Flat_packed_unsigneds_string.push_back(junk.str());
228#endif
229
230 // This node is already shared with processor iproc, so tell the other
231 // processor its index in the shared node storage
232 send_unsigneds.push_back(1);
233#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
234 Flat_packed_unsigneds_string.push_back("haloed node found internally");
235#endif
236 send_unsigneds.push_back(shared_node_index);
237#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
238 Flat_packed_unsigneds_string.push_back("(internal) haloed node index");
239#endif
240 }
241 else
242 {
243 // Attempt to add this node as an external haloed node
244 unsigned n_ext_haloed_nod = mesh_pt->nexternal_haloed_node(iproc);
245 unsigned external_haloed_node_index;
246 external_haloed_node_index =
247 mesh_pt->add_external_haloed_node_pt(iproc, nod_pt);
248
249 // If it was added then the new index should match the size of the
250 // storage
251 if (external_haloed_node_index == n_ext_haloed_nod)
252 {
253 // Indicate that this node needs to be constructed on
254 // the other process
255 send_unsigneds.push_back(1);
256#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
257 std::stringstream junk;
258 junk << "Node needs to be constructed [size=" << send_unsigneds.size()
259 << "]; last entry: "
260 << send_unsigneds[send_unsigneds.size() - 1];
261 Flat_packed_unsigneds_string.push_back(junk.str());
262#endif
263
264 // This helper function gets all the required information for the
265 // specified node and stores it into MPI-sendable information
266 // so that a halo copy can be made on the receiving process
268 nod_pt,
269 mesh_pt,
270 n_cont_inter_values,
271 send_unsigneds,
272 send_doubles);
273 }
274 else // It was already added
275 {
276 // Indicate that this node doesn not need to be constructed on
277 // the other process
278 send_unsigneds.push_back(0);
279#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
280 std::stringstream junk;
281 junk << "Node was already added [size=" << send_unsigneds.size()
282 << "]; last entry: "
283 << send_unsigneds[send_unsigneds.size() - 1];
284
285 Flat_packed_unsigneds_string.push_back(junk.str());
286#endif
287
288 // This node is already an external haloed node, so tell
289 // the other process its index in the equivalent external halo storage
290 send_unsigneds.push_back(0);
291#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
293 "haloed node found externally");
294#endif
295 send_unsigneds.push_back(external_haloed_node_index);
296#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
297 Flat_packed_unsigneds_string.push_back("external haloed node index");
298#endif
299 }
300 }
301 }
302
303
304 //==========start of add_external_haloed_master_node_helper===============
305 /// Helper function to add external haloed node that is a master
306 //========================================================================
308 int& iproc,
309 Node* master_nod_pt,
310 Mesh* const& mesh_pt,
311 int& n_cont_inter_values,
312 Vector<unsigned>& send_unsigneds,
313 Vector<double>& send_doubles)
314 {
315 // Check to see if this haloed node already exists in (internal)
316 // haloed node storage with any processor
317 bool found_internally = false;
318 unsigned shared_node_index = 0;
319
320 // Get vector of all shared nodes with processor iproc
321 Vector<Node*> shared_node_pt;
322 mesh_pt->get_shared_node_pt(iproc, shared_node_pt);
323
324 // Search the internal haloed storage for this node
326 std::find(shared_node_pt.begin(), shared_node_pt.end(), master_nod_pt);
327
328 // Check if the node was found in shared storage
329 if (it != shared_node_pt.end())
330 {
331 // Node found in (internal) haloed storage
332 found_internally = true;
333 // Store the index in this storage
334 shared_node_index = it - shared_node_pt.begin();
335 }
336
337 /// / Slow search version without additional access function in Mesh class
338 /// /Search the internal shared node storage for this node
339 // for(unsigned i=0; i<mesh_pt->nshared_node(iproc); i++)
340 // {
341 // if(master_nod_pt == mesh_pt->shared_node_pt(iproc,i))
342 // {
343 // //Node found in (internal) shared storage
344 // found_internally = true;
345 // shared_node_index = i;
346 // break;
347 // }
348 // }
349
350 // If we've found the node internally
351 if (found_internally)
352 {
353 // Indicate that this node doesn not need to be constructed on
354 // the other process
355 send_unsigneds.push_back(0);
356#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
357 std::stringstream junk;
358 junk << "Node was already added [size=" << send_unsigneds.size()
359 << "]; last entry: " << send_unsigneds[send_unsigneds.size() - 1];
360
361 Flat_packed_unsigneds_string.push_back(junk.str());
362#endif
363
364 // This node is already shared with processor iproc, so tell the other
365 // processor its index in the shared node storage
366 send_unsigneds.push_back(1);
367#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
368 Flat_packed_unsigneds_string.push_back("haloed node found internally");
369#endif
370 send_unsigneds.push_back(shared_node_index);
371#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
372 Flat_packed_unsigneds_string.push_back("(internal) haloed node index");
373#endif
374 }
375 else
376 {
377 // Attempt to add node as an external haloed node
378 unsigned n_ext_haloed_nod = mesh_pt->nexternal_haloed_node(iproc);
379 unsigned external_haloed_node_index;
380 external_haloed_node_index =
381 mesh_pt->add_external_haloed_node_pt(iproc, master_nod_pt);
382
383 // If it was added the returned index is the same as current storage
384 // size
385 if (external_haloed_node_index == n_ext_haloed_nod)
386 {
387 // Indicate that this node needs to be constructed on
388 // the other process
389 send_unsigneds.push_back(1);
390#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
392 "Node needs to be constructed[2]");
393#endif
394
395 // This gets all the required information for the specified
396 // master node and stores it into MPI-sendable information
397 // so that a halo copy can be made on the receiving process
399 master_nod_pt,
400 mesh_pt,
401 n_cont_inter_values,
402 send_unsigneds,
403 send_doubles);
404 }
405 else // It was already added
406 {
407 // Indicate that this node doesn not need to be constructed on
408 // the other process
409 send_unsigneds.push_back(0);
410#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
411 Flat_packed_unsigneds_string.push_back("Node was already added[2]");
412#endif
413
414 // This node is already an external haloed node, so tell
415 // the other process its index in the equivalent external halo storage
416 send_unsigneds.push_back(0);
417#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
419 "haloed node found externally");
420#endif
421 send_unsigneds.push_back(external_haloed_node_index);
422#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
424 "external haloed node index[2]");
425#endif
426 }
427 }
428 }
429
430
431 //========start of get_required_nodal_information_helper==================
432 /// Helper function to get the required nodal information from an
433 /// external haloed node so that a fully-functional external halo
434 /// node (and therefore element) can be created on the receiving process
435 //========================================================================
437 Node* nod_pt,
438 Mesh* const& mesh_pt,
439 int& n_cont_inter_values,
440 Vector<unsigned>& send_unsigneds,
441 Vector<double>& send_doubles)
442 {
443 // Tell the halo copy of this node how many values there are
444 // [NB this may be different for nodes within the same element, e.g.
445 // when using Lagrange multipliers]
446 unsigned n_val = nod_pt->nvalue();
447 send_unsigneds.push_back(n_val);
448#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
449 Flat_packed_unsigneds_string.push_back("Number of values");
450#endif
451
452 unsigned n_dim = nod_pt->ndim();
453 TimeStepper* time_stepper_pt = nod_pt->time_stepper_pt();
454
455 // Default number of previous values to 1
456 unsigned n_prev = 1;
457 if (time_stepper_pt != 0)
458 {
459 // Add number of history values to n_prev
460 n_prev = time_stepper_pt->ntstorage();
461 }
462
463 // Is the node on any boundaries?
464 if (nod_pt->is_on_boundary())
465 {
466 send_unsigneds.push_back(1);
467#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
468 Flat_packed_unsigneds_string.push_back("Node is on boundary");
469#endif
470
471 // Loop over the boundaries of the external mesh
472 Vector<unsigned> boundaries;
473 unsigned n_bnd = mesh_pt->nboundary();
474 for (unsigned i_bnd = 0; i_bnd < n_bnd; i_bnd++)
475 {
476 // Which boundaries (could be more than one) is it on?
477 if (nod_pt->is_on_boundary(i_bnd))
478 {
479 boundaries.push_back(i_bnd);
480 }
481 }
482 unsigned nb = boundaries.size();
483 send_unsigneds.push_back(nb);
484#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
485 std::stringstream junk;
486 junk << "Node is on " << nb << " boundaries";
487 Flat_packed_unsigneds_string.push_back(junk.str());
488#endif
489 for (unsigned i = 0; i < nb; i++)
490 {
491 send_unsigneds.push_back(boundaries[i]);
492#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
493 std::stringstream junk;
494 junk << "Node is on boundary " << boundaries[i] << " of " << n_bnd;
495 Flat_packed_unsigneds_string.push_back(junk.str());
496#endif
497 }
498
499 // Get pointer to the map of indices associated with
500 // additional values created by face elements
501 BoundaryNodeBase* bnod_pt = dynamic_cast<BoundaryNodeBase*>(nod_pt);
502#ifdef PARANOID
503 if (bnod_pt == 0)
504 {
505 throw OomphLibError("Failed to cast new node to boundary node\n",
506 OOMPH_CURRENT_FUNCTION,
507 OOMPH_EXCEPTION_LOCATION);
508 }
509#endif
510 std::map<unsigned, unsigned>* map_pt =
512
513 // No additional values created
514 if (map_pt == 0)
515 {
516 send_unsigneds.push_back(0);
517#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
518 std::stringstream junk;
520 "No additional values were created by face element");
521#endif
522 }
523 // Created additional values
524 else
525 {
526 // How many?
527 send_unsigneds.push_back(map_pt->size());
528#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
529 std::stringstream junk;
530 junk << "Map size " << map_pt->size() << n_bnd;
531 Flat_packed_unsigneds_string.push_back(junk.str());
532#endif
533 // Loop over entries in map and add to send data
534 for (std::map<unsigned, unsigned>::iterator p = map_pt->begin();
535 p != map_pt->end();
536 p++)
537 {
538 send_unsigneds.push_back((*p).first);
539#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
540 std::stringstream junk;
541 Flat_packed_unsigneds_string.push_back("Key of map entry");
542#endif
543 send_unsigneds.push_back((*p).second);
544#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
545 Flat_packed_unsigneds_string.push_back("Value of map entry");
546#endif
547 }
548 }
549 }
550 else
551 {
552 // Not on any boundary
553 send_unsigneds.push_back(0);
554#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
555 Flat_packed_unsigneds_string.push_back("Node is not on any boundary");
556#endif
557 }
558
559 // Is the Node algebraic? If so, send its ref values and
560 // an indication of its geometric objects if they are stored
561 // in the algebraic mesh
562 AlgebraicNode* alg_nod_pt = dynamic_cast<AlgebraicNode*>(nod_pt);
563 if (alg_nod_pt != 0)
564 {
565 // The external mesh should be algebraic
566 AlgebraicMesh* alg_mesh_pt = dynamic_cast<AlgebraicMesh*>(mesh_pt);
567
568 // Get default node update function ID
569 unsigned update_id = alg_nod_pt->node_update_fct_id();
570 send_unsigneds.push_back(update_id);
571#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
572 Flat_packed_unsigneds_string.push_back("Alg Node update id");
573#endif
574
575 // Get reference values at default...
576 unsigned n_ref_val = alg_nod_pt->nref_value();
577 send_unsigneds.push_back(n_ref_val);
578#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
579 Flat_packed_unsigneds_string.push_back("Alg Node n ref values");
580#endif
581 for (unsigned i_ref_val = 0; i_ref_val < n_ref_val; i_ref_val++)
582 {
583 send_doubles.push_back(alg_nod_pt->ref_value(i_ref_val));
584 }
585
586 // Access geometric objects at default...
587 unsigned n_geom_obj = alg_nod_pt->ngeom_object();
588 send_unsigneds.push_back(n_geom_obj);
589#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
590 Flat_packed_unsigneds_string.push_back("Alg Node n geom objects");
591#endif
592 for (unsigned i_geom = 0; i_geom < n_geom_obj; i_geom++)
593 {
594 GeomObject* geom_obj_pt = alg_nod_pt->geom_object_pt(i_geom);
595
596 // Check this against the stored geometric objects in mesh
597 unsigned n_geom_list = alg_mesh_pt->ngeom_object_list_pt();
598
599 // Default found index to zero
600 unsigned found_geom_object = 0;
601 for (unsigned i_list = 0; i_list < n_geom_list; i_list++)
602 {
603 if (geom_obj_pt == alg_mesh_pt->geom_object_list_pt(i_list))
604 {
605 found_geom_object = i_list;
606 }
607 }
608 send_unsigneds.push_back(found_geom_object);
609#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
610 Flat_packed_unsigneds_string.push_back("Found geom object");
611#endif
612 }
613 }
614
615 // If it is a MacroElementNodeUpdateNode, everything has been
616 // dealt with by the new element already
617
618 // Is it a SolidNode?
619 SolidNode* solid_nod_pt = dynamic_cast<SolidNode*>(nod_pt);
620 if (solid_nod_pt != 0)
621 {
622 unsigned n_solid_val = solid_nod_pt->variable_position_pt()->nvalue();
623 for (unsigned i_val = 0; i_val < n_solid_val; i_val++)
624 {
625 for (unsigned t = 0; t < n_prev; t++)
626 {
627 send_doubles.push_back(
628 solid_nod_pt->variable_position_pt()->value(t, i_val));
629 }
630 }
631 }
632
633 // Finally copy info required for all node types
634 for (unsigned i_val = 0; i_val < n_val; i_val++)
635 {
636 for (unsigned t = 0; t < n_prev; t++)
637 {
638 send_doubles.push_back(nod_pt->value(t, i_val));
639 }
640 }
641
642 // Now do positions
643 for (unsigned idim = 0; idim < n_dim; idim++)
644 {
645 for (unsigned t = 0; t < n_prev; t++)
646 {
647 send_doubles.push_back(nod_pt->x(t, idim));
648 }
649 }
650 }
651
652 //=========start of get_required_master_nodal_information_helper==========
653 /// Helper function to get the required master nodal information from an
654 /// external haloed master node so that a fully-functional external halo
655 /// master node (and possible element) can be created on the receiving
656 /// process
657 //========================================================================
659 int& iproc,
660 Node* master_nod_pt,
661 Mesh* const& mesh_pt,
662 int& n_cont_inter_values,
663 Vector<unsigned>& send_unsigneds,
664 Vector<double>& send_doubles)
665 {
666 // Need to send over dimension, position type and number of values
667 send_unsigneds.push_back(master_nod_pt->ndim());
668#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
669 Flat_packed_unsigneds_string.push_back("Master node ndim");
670#endif
671 send_unsigneds.push_back(master_nod_pt->nposition_type());
672#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
673 Flat_packed_unsigneds_string.push_back("Master node npos_type");
674#endif
675 send_unsigneds.push_back(master_nod_pt->nvalue());
676#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
677 Flat_packed_unsigneds_string.push_back("Master node nvalue");
678#endif
679 if (master_nod_pt->is_halo())
680 {
681 send_unsigneds.push_back(master_nod_pt->non_halo_proc_ID());
682 }
683 else
684 {
685 send_unsigneds.push_back(mesh_pt->communicator_pt()->my_rank());
686 }
687#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
689 "Master node non-halo processor ID");
690#endif
691
692 // If it's a solid node, also need to send lagrangian dim and type
693 SolidNode* solid_nod_pt = dynamic_cast<SolidNode*>(master_nod_pt);
694 if (solid_nod_pt != 0)
695 {
696 send_unsigneds.push_back(solid_nod_pt->nlagrangian());
697#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
698 Flat_packed_unsigneds_string.push_back("Master solid node nlagr");
699#endif
700 send_unsigneds.push_back(solid_nod_pt->nlagrangian_type());
701#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
702 Flat_packed_unsigneds_string.push_back("Master solid node nlagr_type");
703#endif
704 }
705
706 unsigned n_dim = master_nod_pt->ndim();
707 TimeStepper* time_stepper_pt = master_nod_pt->time_stepper_pt();
708
709 // Default number of previous values to 1
710 unsigned n_prev = 1;
711 if (time_stepper_pt != 0)
712 {
713 // Add number of history values to n_prev
714 n_prev = time_stepper_pt->ntstorage();
715 }
716
717 // Is the node on any boundaries?
718 if (master_nod_pt->is_on_boundary())
719 {
720 send_unsigneds.push_back(1);
721#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
722 Flat_packed_unsigneds_string.push_back("Master node is on boundary");
723#endif
724 // Loop over the boundaries of the external mesh
725 Vector<unsigned> boundaries;
726 unsigned n_bnd = mesh_pt->nboundary();
727 for (unsigned i_bnd = 0; i_bnd < n_bnd; i_bnd++)
728 {
729 // Which boundaries (could be more than one) is it on?
730 if (master_nod_pt->is_on_boundary(i_bnd))
731 {
732 boundaries.push_back(i_bnd);
733 }
734 }
735 unsigned nb = boundaries.size();
736 send_unsigneds.push_back(nb);
737#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
738 std::stringstream junk;
739 junk << "Master node is on " << nb << " boundaries";
740 Flat_packed_unsigneds_string.push_back(junk.str());
741#endif
742 for (unsigned i = 0; i < nb; i++)
743 {
744 send_unsigneds.push_back(boundaries[i]);
745#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
746 std::stringstream junk;
747 junk << "Master noode is on boundary " << boundaries[i] << " of "
748 << n_bnd;
749 Flat_packed_unsigneds_string.push_back(junk.str());
750#endif
751 }
752
753 // Get pointer to the map of indices associated with
754 // additional values created by face elements
755 BoundaryNodeBase* bnod_pt =
756 dynamic_cast<BoundaryNodeBase*>(master_nod_pt);
757#ifdef PARANOID
758 if (bnod_pt == 0)
759 {
760 throw OomphLibError("Failed to cast new node to boundary node\n",
761 OOMPH_CURRENT_FUNCTION,
762 OOMPH_EXCEPTION_LOCATION);
763 }
764#endif
765 std::map<unsigned, unsigned>* map_pt =
767
768 // No additional values created
769 if (map_pt == 0)
770 {
771 send_unsigneds.push_back(0);
772#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
773 std::stringstream junk;
775 "No additional values were created by face element for this master "
776 "node");
777#endif
778 }
779 // Created additional values
780 else
781 {
782 // How many?
783 send_unsigneds.push_back(map_pt->size());
784#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
785 std::stringstream junk;
786 junk << "Map size for master node " << map_pt->size() << n_bnd;
787 Flat_packed_unsigneds_string.push_back(junk.str());
788#endif
789 // Loop over entries in map and add to send data
790 for (std::map<unsigned, unsigned>::iterator p = map_pt->begin();
791 p != map_pt->end();
792 p++)
793 {
794 send_unsigneds.push_back((*p).first);
795#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
796 std::stringstream junk;
798 "Key of map entry for master node");
799#endif
800 send_unsigneds.push_back((*p).second);
801#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
803 "Value of map entry for master node");
804#endif
805 }
806 }
807 }
808 else
809 {
810 // Not on any boundary
811 send_unsigneds.push_back(0);
812#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
814 "Master node is not on any boundary");
815#endif
816 }
817
818 // Is the Node algebraic? If so, send its ref values and
819 // an indication of its geometric objects if they are stored
820 // in the algebraic mesh
821 AlgebraicNode* alg_nod_pt = dynamic_cast<AlgebraicNode*>(master_nod_pt);
822 if (alg_nod_pt != 0)
823 {
824 // The external mesh should be algebraic
825 AlgebraicMesh* alg_mesh_pt = dynamic_cast<AlgebraicMesh*>(mesh_pt);
826
827 // Get default node update function ID
828 unsigned update_id = alg_nod_pt->node_update_fct_id();
829 send_unsigneds.push_back(update_id);
830#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
831 Flat_packed_unsigneds_string.push_back("Master Alg Node update id");
832#endif
833
834 // Get reference values at default...
835 unsigned n_ref_val = alg_nod_pt->nref_value();
836 send_unsigneds.push_back(n_ref_val);
837#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
838 Flat_packed_unsigneds_string.push_back("Master Alg Node n ref values");
839#endif
840 for (unsigned i_ref_val = 0; i_ref_val < n_ref_val; i_ref_val++)
841 {
842 send_doubles.push_back(alg_nod_pt->ref_value(i_ref_val));
843 }
844
845 // Access geometric objects at default...
846 unsigned n_geom_obj = alg_nod_pt->ngeom_object();
847 send_unsigneds.push_back(n_geom_obj);
848#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
850 "Master Alg Node n geom objects");
851#endif
852 for (unsigned i_geom = 0; i_geom < n_geom_obj; i_geom++)
853 {
854 GeomObject* geom_obj_pt = alg_nod_pt->geom_object_pt(i_geom);
855 // Check this against the stored geometric objects in mesh
856 unsigned n_geom_list = alg_mesh_pt->ngeom_object_list_pt();
857 // Default found index to zero
858 unsigned found_geom_object = 0;
859 for (unsigned i_list = 0; i_list < n_geom_list; i_list++)
860 {
861 if (geom_obj_pt == alg_mesh_pt->geom_object_list_pt(i_list))
862 {
863 found_geom_object = i_list;
864 }
865 }
866 send_unsigneds.push_back(found_geom_object);
867#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
869 "Master node Found geom object");
870#endif
871 }
872 } // end AlgebraicNode check
873
874 // Is it a MacroElementNodeUpdateNode?
875 MacroElementNodeUpdateNode* macro_nod_pt =
876 dynamic_cast<MacroElementNodeUpdateNode*>(master_nod_pt);
877 if (macro_nod_pt != 0)
878 {
879 oomph_info << "Adding external haloed master node: " << master_nod_pt
880 << " at " << master_nod_pt->x(0) << ", "
881 << master_nod_pt->x(1) << " ]" << std::endl;
882 // Loop over current external haloed elements - has the element which
883 // controls the node update for this node been added yet?
884 GeneralisedElement* macro_node_update_el_pt =
885 macro_nod_pt->node_update_element_pt();
886
887 // BENFLAG: Check that the node's macro element node update element
888 // actually contains the node
889 oomph_info << "Master node's macro update element:" << std::endl;
890 bool really_bad = true;
891 FiniteElement* mac_el_pt =
892 dynamic_cast<FiniteElement*>(macro_node_update_el_pt);
893 for (unsigned j = 0; j < mac_el_pt->nnode(); j++)
894 {
895 oomph_info << mac_el_pt->node_pt(j) << ": [ "
896 << mac_el_pt->node_pt(j)->x(0) << ", "
897 << mac_el_pt->node_pt(j)->x(1) << " ] " << std::endl;
898 if (mac_el_pt->node_pt(j) == master_nod_pt)
899 {
900 really_bad = false;
901 // oomph_info << "Found it!" << std::endl;
902 }
903 }
904 if (really_bad == true)
905 {
906 oomph_info << "This is REALLY BAD! The master node is not part of "
907 "its own update element..."
908 << std::endl;
909 }
910
911 // BENFLAG: Search internal storage for node update element
912 Vector<GeneralisedElement*> int_haloed_el_pt(
913 mesh_pt->haloed_element_pt(iproc));
914 // unsigned n_int_haloed_el=int_haloed_el_pt.size();
916 std::find(int_haloed_el_pt.begin(),
917 int_haloed_el_pt.end(),
918 macro_node_update_el_pt);
919 if (it != int_haloed_el_pt.end())
920 {
921 // Found in internal haloed storage
922 unsigned int_haloed_el_index = it - int_haloed_el_pt.begin();
923 oomph_info << "Found internally at index " << int_haloed_el_index
924 << std::endl;
925 // BENFLAG: Check index corresponds to correct element
926 if ((mesh_pt->haloed_element_pt(iproc))[int_haloed_el_index] !=
927 macro_node_update_el_pt)
928 {
929 oomph_info << "Found wrong index!!!" << std::endl;
930 throw;
931 }
932 else
933 {
934 oomph_info << "index and proc are correct in internal storage."
935 << std::endl;
936 oomph_info << "i.e. "
937 << (mesh_pt->haloed_element_pt(
938 iproc))[int_haloed_el_index]
939 << "==" << macro_node_update_el_pt << std::endl;
940 }
941
942
943 // Say haloed element already exists
944 send_unsigneds.push_back(0);
945#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
947 "External haloed element already exists");
948#endif
949 // Say found internally
950 send_unsigneds.push_back(1);
951#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
953 "Haloed element found internally");
954#endif
955 // Say what the index is
956 send_unsigneds.push_back(int_haloed_el_index);
957#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
959 "Index of existing internal haloed element");
960#endif
961 // BENFLAG:
962 FiniteElement* tmp_el_pt = dynamic_cast<FiniteElement*>(
963 (mesh_pt->haloed_element_pt(iproc))[int_haloed_el_index]);
964 oomph_info << "Internal haloed element (" << tmp_el_pt
965 << ") already exists..." << std::endl;
966 oomph_info << "on proc " << iproc << " at index "
967 << int_haloed_el_index << std::endl;
968 for (unsigned j = 0; j < tmp_el_pt->nnode(); j++)
969 {
970 oomph_info << tmp_el_pt->node_pt(j) << "=="
971 << dynamic_cast<FiniteElement*>(
972 (mesh_pt->haloed_element_pt(
973 iproc))[int_haloed_el_index])
974 ->node_pt(j)
975 << " at [ " << tmp_el_pt->node_pt(j)->x(0) << ", "
976 << tmp_el_pt->node_pt(j)->x(1) << " ]" << std::endl;
977 }
978
979 // oomph_info << "now " << tmp_el_pt << "==" <<
980 // (mesh_pt->haloed_element_pt(iproc))[int_haloed_el_index] << "==" <<
981 // macro_node_update_el_pt << std::endl;
982
983 // //BENFLAG: Add to external storage too
984 // unsigned n_ext_haloed_el=mesh_pt->
985 // nexternal_haloed_element(iproc);
986 // unsigned external_haloed_el_index;
987 // external_haloed_el_index=mesh_pt->
988 // add_external_haloed_element_pt(iproc,macro_node_update_el_pt);
989 //
990 // // If it was already added, say
991 // if (external_haloed_el_index!=n_ext_haloed_el)
992 // {
993 // oomph_info << "Element (" << tmp_el_pt << "==" <<
994 // macro_node_update_el_pt << "==" <<
995 // mesh_pt->external_haloed_element_pt(iproc,external_haloed_el_index)
996 // << ") also exists in external storage with proc " << iproc
997 // << " at index " << external_haloed_el_index << " of " <<
998 // n_ext_haloed_el << "..." << std::endl;
999 // // Say also exists in external storage
1000 // send_unsigneds.push_back(1234);
1001 //#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
1002 // Flat_packed_unsigneds_string.push_back("Haloed element also
1003 // found externally");
1004 //#endif
1005 // // Say what the index is
1006 // send_unsigneds.push_back(external_haloed_el_index);
1007 //#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
1008 // Flat_packed_unsigneds_string.push_back("Index of existing
1009 // internal haloed element");
1010 //#endif
1011 // oomph_info << "sent external_haloed_el_index = " <<
1012 // external_haloed_el_index << std::endl;
1013 //
1014 // }
1015 // else
1016 // {
1017 // oomph_info << "Element didn't exist in external storage
1018 // with proc " << iproc << "..." << std::endl;
1019 // // Say doesn't exists in external storage
1020 // send_unsigneds.push_back(0);
1021 //#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
1022 // Flat_packed_unsigneds_string.push_back("Haloed element not
1023 // also found externally");
1024 //#endif
1025 // }
1026 }
1027 else
1028 {
1029 unsigned n_ext_haloed_el = mesh_pt->nexternal_haloed_element(iproc);
1030 unsigned external_haloed_el_index;
1031 external_haloed_el_index = mesh_pt->add_external_haloed_element_pt(
1032 iproc, macro_node_update_el_pt);
1033
1034 // If it wasn't already added, we need to create a halo copy
1035 if (external_haloed_el_index == n_ext_haloed_el)
1036 {
1037 send_unsigneds.push_back(1);
1038#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
1040 "Master Node needs to be constructed");
1041#endif
1042 // Cast to a finite elemnet
1043 FiniteElement* macro_node_update_finite_el_pt =
1044 dynamic_cast<FiniteElement*>(macro_node_update_el_pt);
1045
1046 // We're using macro elements to update...
1047 MacroElementNodeUpdateMesh* macro_mesh_pt =
1048 dynamic_cast<MacroElementNodeUpdateMesh*>(mesh_pt);
1049 if (macro_mesh_pt != 0)
1050 {
1051 send_unsigneds.push_back(1);
1052#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
1054 "Mesh is macro element mesh");
1055#endif
1056 // Need to send the macro element number in the mesh across
1057 MacroElement* macro_el_pt =
1058 macro_node_update_finite_el_pt->macro_elem_pt();
1059 unsigned macro_el_num = macro_el_pt->macro_element_number();
1060 send_unsigneds.push_back(macro_el_num);
1061#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
1062 Flat_packed_unsigneds_string.push_back("Number of macro element");
1063#endif
1064 // Also need to send
1065 // the lower left and upper right coordinates of the macro element
1066 QElementBase* q_el_pt =
1067 dynamic_cast<QElementBase*>(macro_node_update_el_pt);
1068 if (q_el_pt != 0)
1069 {
1070 // The macro element needs to be set first before
1071 // its lower left and upper right coordinates can be accessed
1072 // Now send the lower left and upper right coordinates
1073 unsigned el_dim = q_el_pt->dim();
1074 for (unsigned i_dim = 0; i_dim < el_dim; i_dim++)
1075 {
1076 send_doubles.push_back(q_el_pt->s_macro_ll(i_dim));
1077 send_doubles.push_back(q_el_pt->s_macro_ur(i_dim));
1078 }
1079 }
1080 else // Throw an error
1081 {
1082 std::ostringstream error_stream;
1083 error_stream << "You are using a MacroElement node update\n"
1084 << "in a case with non-QElements. This has not\n"
1085 << "yet been implemented.\n";
1086 throw OomphLibError(error_stream.str(),
1087 OOMPH_CURRENT_FUNCTION,
1088 OOMPH_EXCEPTION_LOCATION);
1089 }
1090 }
1091 else // Not using macro elements for node update... umm, we're
1092 // already inside a loop over macro elements, so this
1093 // should never get here... an error should be thrown I suppose
1094 {
1095 send_unsigneds.push_back(0);
1096#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
1098 "Mesh is not a macro element mesh");
1099#endif
1100 }
1101
1102 // If the element is p-refineable we need to send the p-order so
1103 // that the halo version can be constructed correctly
1104 PRefineableElement* p_refineable_el_pt =
1105 dynamic_cast<PRefineableElement*>(macro_node_update_finite_el_pt);
1106 if (p_refineable_el_pt != 0)
1107 {
1108 send_unsigneds.push_back(1);
1109#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
1110 Flat_packed_unsigneds_string.push_back("Element is p-refineable");
1111#endif
1112 // Send p-order of macro element node update element
1113 unsigned p_order = p_refineable_el_pt->p_order();
1114 send_unsigneds.push_back(p_order);
1115#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
1116 Flat_packed_unsigneds_string.push_back("p-order of element");
1117#endif
1118 }
1119 else
1120 {
1121 send_unsigneds.push_back(0);
1122#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
1124 "Element is not p-refineable");
1125#endif
1126 }
1127
1128 // This element needs to be fully functioning on the other
1129 // process, so send all the information required to create it
1130 unsigned n_node = macro_node_update_finite_el_pt->nnode();
1131 for (unsigned j = 0; j < n_node; j++)
1132 {
1133 Node* new_nod_pt = macro_node_update_finite_el_pt->node_pt(j);
1135 new_nod_pt,
1136 mesh_pt,
1137 n_cont_inter_values,
1138 send_unsigneds,
1139 send_doubles);
1140 }
1141 }
1142 else // The external haloed element already exists
1143 {
1144 // Say haloed element already exists
1145 send_unsigneds.push_back(0);
1146#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
1148 "External haloed element already exists");
1149#endif
1150 // Say found externally
1151 send_unsigneds.push_back(0);
1152#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
1154 "Haloed element found externally");
1155#endif
1156 send_unsigneds.push_back(external_haloed_el_index);
1157#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
1159 "Index of existing external haloed element");
1160#endif
1161 // BENFLAG:
1162 oomph_info << "External haloed element already exists..."
1163 << std::endl;
1164 FiniteElement* tmp_el_pt =
1165 dynamic_cast<FiniteElement*>(mesh_pt->external_haloed_element_pt(
1166 iproc, external_haloed_el_index));
1167 oomph_info << "on proc " << iproc << " at index "
1168 << external_haloed_el_index << std::endl;
1169 for (unsigned j = 0; j < tmp_el_pt->nnode(); j++)
1170 {
1171 oomph_info << tmp_el_pt->node_pt(j) << " at [ "
1172 << tmp_el_pt->node_pt(j)->x(0) << ", "
1173 << tmp_el_pt->node_pt(j)->x(1) << " ]" << std::endl;
1174 }
1175 }
1176 } // End of case where not found internally
1177
1178 } // end of MacroElementNodeUpdateNode check
1179
1180 // Is it a SolidNode?
1181 if (solid_nod_pt != 0)
1182 {
1183 unsigned n_val = solid_nod_pt->variable_position_pt()->nvalue();
1184 for (unsigned i_val = 0; i_val < n_val; i_val++)
1185 {
1186 for (unsigned t = 0; t < n_prev; t++)
1187 {
1188 send_doubles.push_back(
1189 solid_nod_pt->variable_position_pt()->value(t, i_val));
1190 }
1191 }
1192 }
1193
1194 // Finally copy info required for all node types
1195
1196 // Halo copy needs to know all the history values
1197 unsigned n_val = master_nod_pt->nvalue();
1198 for (unsigned i_val = 0; i_val < n_val; i_val++)
1199 {
1200 for (unsigned t = 0; t < n_prev; t++)
1201 {
1202 send_doubles.push_back(master_nod_pt->value(t, i_val));
1203 }
1204 }
1205
1206 // Now do positions
1207 for (unsigned idim = 0; idim < n_dim; idim++)
1208 {
1209 for (unsigned t = 0; t < n_prev; t++)
1210 {
1211 send_doubles.push_back(master_nod_pt->x(t, idim));
1212 }
1213 }
1214 }
1215
1216
1217 //=======start of add_external_halo_node_helper===========================
1218 /// Helper functiono to add external halo node that is not a master
1219 //========================================================================
1221 Mesh* const& mesh_pt,
1222 unsigned& loc_p,
1223 unsigned& node_index,
1224 FiniteElement* const& new_el_pt,
1225 int& n_cont_inter_values,
1226 unsigned& counter_for_recv_unsigneds,
1227 Vector<unsigned>& recv_unsigneds,
1228 unsigned& counter_for_recv_doubles,
1229 Vector<double>& recv_doubles)
1230 {
1231 // Given the node and the external mesh, and received information
1232 // about them from process loc_p, construct them on the current process
1233#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
1234 oomph_info << "Rec:" << counter_for_recv_unsigneds
1235 << " Bool: New node needs to be constructed "
1236 << recv_unsigneds[counter_for_recv_unsigneds] << std::endl;
1237#endif
1238 if (recv_unsigneds[counter_for_recv_unsigneds++] == 1)
1239 {
1240 // Construct a new node based upon sent information
1242 loc_p,
1243 node_index,
1244 new_el_pt,
1245 mesh_pt,
1246 counter_for_recv_unsigneds,
1247 recv_unsigneds,
1248 counter_for_recv_doubles,
1249 recv_doubles);
1250 }
1251 else
1252 {
1253 // Need to check which storage we should copy this halo node from
1254#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
1255 oomph_info << "Rec:" << counter_for_recv_unsigneds
1256 << " Existing external halo node was found externally (0) "
1257 "or internally (1): "
1258 << recv_unsigneds[counter_for_recv_unsigneds] << std::endl;
1259#endif
1260 unsigned node_found_internally =
1261 recv_unsigneds[counter_for_recv_unsigneds++];
1262 if (node_found_internally)
1263 {
1264#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
1265 oomph_info << "Rec:" << counter_for_recv_unsigneds
1266 << " index of existing (internal) halo master node "
1267 << recv_unsigneds[counter_for_recv_unsigneds] << std::endl;
1268#endif
1269
1270 // Copy node from received location
1271 new_nod_pt = mesh_pt->shared_node_pt(
1272 loc_p, recv_unsigneds[counter_for_recv_unsigneds++]);
1273
1274 new_el_pt->node_pt(node_index) = new_nod_pt;
1275 }
1276 else
1277 {
1278#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
1279 oomph_info << "Rec:" << counter_for_recv_unsigneds
1280 << " Index of existing external halo node "
1281 << recv_unsigneds[counter_for_recv_unsigneds] << std::endl;
1282#endif
1283
1284 // Copy node from received location
1285 new_nod_pt = mesh_pt->external_halo_node_pt(
1286 loc_p, recv_unsigneds[counter_for_recv_unsigneds++]);
1287
1288 new_el_pt->node_pt(node_index) = new_nod_pt;
1289 }
1290 }
1291 }
1292
1293
1294 //========start of construct_new_external_halo_node_helper=================
1295 /// Helper function which constructs a new external halo node (on new
1296 /// element) with the required information sent from the haloed process
1297 //========================================================================
1299 Node*& new_nod_pt,
1300 unsigned& loc_p,
1301 unsigned& node_index,
1302 FiniteElement* const& new_el_pt,
1303 Mesh* const& mesh_pt,
1304 unsigned& counter_for_recv_unsigneds,
1305 Vector<unsigned>& recv_unsigneds,
1306 unsigned& counter_for_recv_doubles,
1307 Vector<double>& recv_doubles)
1308 {
1309 // The first entry indicates the number of values at this new Node
1310 // (which may be different across the same element e.g. Lagrange
1311 // multipliers)
1312#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
1313 oomph_info << "Rec:" << counter_for_recv_unsigneds
1314 << " Number of values of external halo node "
1315 << recv_unsigneds[counter_for_recv_unsigneds] << std::endl;
1316#endif
1317 unsigned n_val = recv_unsigneds[counter_for_recv_unsigneds++];
1318
1319 // Null TimeStepper for now
1320 TimeStepper* time_stepper_pt = 0;
1321 // Default number of previous values to 1
1322 unsigned n_prev = 1;
1323
1324 // Just take timestepper from a node
1325 // Let's use first node of first element since this must exist
1326 time_stepper_pt =
1327 mesh_pt->finite_element_pt(0)->node_pt(0)->time_stepper_pt();
1328
1329 // If this node was on a boundary then it needs to
1330 // be on the same boundary here
1331#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
1332 oomph_info << "Rec:" << counter_for_recv_unsigneds
1333 << " Is node on boundary? "
1334 << recv_unsigneds[counter_for_recv_unsigneds] << std::endl;
1335#endif
1336 if (recv_unsigneds[counter_for_recv_unsigneds++] == 1)
1337 {
1338 // Construct a new boundary node
1339 if (time_stepper_pt != 0)
1340 {
1341 new_nod_pt =
1342 new_el_pt->construct_boundary_node(node_index, time_stepper_pt);
1343 }
1344 else
1345 {
1346 new_nod_pt = new_el_pt->construct_boundary_node(node_index);
1347 }
1348
1349 // How many boundaries does the node live on?
1350#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
1351 oomph_info << "Rec:" << counter_for_recv_unsigneds
1352 << " Number of boundaries the node is on: "
1353 << recv_unsigneds[counter_for_recv_unsigneds] << std::endl;
1354#endif
1355 unsigned nb = recv_unsigneds[counter_for_recv_unsigneds++];
1356 for (unsigned i = 0; i < nb; i++)
1357 {
1358 // Boundary number
1359#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
1360 oomph_info << "Rec:" << counter_for_recv_unsigneds
1361 << " Node is on boundary "
1362 << recv_unsigneds[counter_for_recv_unsigneds] << std::endl;
1363#endif
1364 unsigned i_bnd = recv_unsigneds[counter_for_recv_unsigneds++];
1365 mesh_pt->add_boundary_node(i_bnd, new_nod_pt);
1366 }
1367
1368 // Do we have additional values created by face elements?
1369#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
1370 oomph_info << "Rec:" << counter_for_recv_unsigneds
1371 << " Number of additional values created by face element "
1372 << recv_unsigneds[counter_for_recv_unsigneds] << std::endl;
1373#endif
1374 unsigned n_entry = recv_unsigneds[counter_for_recv_unsigneds++];
1375 if (n_entry > 0)
1376 {
1377 // Create storage, if it doesn't already exist, for the map
1378 // that will contain the position of the first entry of
1379 // this face element's additional values,
1380 BoundaryNodeBase* bnew_nod_pt =
1381 dynamic_cast<BoundaryNodeBase*>(new_nod_pt);
1382#ifdef PARANOID
1383 if (bnew_nod_pt == 0)
1384 {
1385 throw OomphLibError("Failed to cast new node to boundary node\n",
1386 OOMPH_CURRENT_FUNCTION,
1387 OOMPH_EXCEPTION_LOCATION);
1388 }
1389#endif
1391 0)
1392 {
1394 new std::map<unsigned, unsigned>;
1395 }
1396
1397 // Get pointer to the map of indices associated with
1398 // additional values created by face elements
1399 std::map<unsigned, unsigned>* map_pt =
1401
1402 // Loop over number of entries in map
1403 for (unsigned i = 0; i < n_entry; i++)
1404 {
1405 // Read out pairs...
1406
1407#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
1408 oomph_info << "Rec:" << counter_for_recv_unsigneds
1409 << " Key of map entry"
1410 << recv_unsigneds[counter_for_recv_unsigneds]
1411 << std::endl;
1412#endif
1413 unsigned first = recv_unsigneds[counter_for_recv_unsigneds++];
1414
1415#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
1416 oomph_info << "Rec:" << counter_for_recv_unsigneds
1417 << " Value of map entry"
1418 << recv_unsigneds[counter_for_recv_unsigneds]
1419 << std::endl;
1420#endif
1421 unsigned second = recv_unsigneds[counter_for_recv_unsigneds++];
1422
1423 // ...and assign
1424 (*map_pt)[first] = second;
1425 }
1426 }
1427 }
1428 else
1429 {
1430 // Construct an ordinary (non-boundary) node
1431 if (time_stepper_pt != 0)
1432 {
1433 new_nod_pt = new_el_pt->construct_node(node_index, time_stepper_pt);
1434 }
1435 else
1436 {
1437 new_nod_pt = new_el_pt->construct_node(node_index);
1438 }
1439 }
1440
1441 // Node constructed: add to external halo nodes
1442 mesh_pt->add_external_halo_node_pt(loc_p, new_nod_pt);
1443
1444 // Is the new constructed node Algebraic?
1445 AlgebraicNode* new_alg_nod_pt = dynamic_cast<AlgebraicNode*>(new_nod_pt);
1446
1447 // If it is algebraic, its node update functions will
1448 // not yet have been set up properly
1449 if (new_alg_nod_pt != 0)
1450 {
1451 // The AlgebraicMesh is the external mesh
1452 AlgebraicMesh* alg_mesh_pt = dynamic_cast<AlgebraicMesh*>(mesh_pt);
1453
1454 /// The first entry of All_alg_nodal_info contains
1455 /// the default node update id
1456 /// e.g. for the quarter circle there are
1457 /// "Upper_left_box", "Lower right box" etc...
1458#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
1459 oomph_info << "Rec:" << counter_for_recv_unsigneds
1460 << " Alg node update id "
1461 << recv_unsigneds[counter_for_recv_unsigneds] << std::endl;
1462#endif
1463
1464 unsigned update_id = recv_unsigneds[counter_for_recv_unsigneds++];
1465
1466 Vector<double> ref_value;
1467
1468 // The size of this vector is in the next entry
1469 // of All_alg_nodal_info
1470#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
1471 oomph_info << "Rec:" << counter_for_recv_unsigneds
1472 << " Alg node # of ref values "
1473 << recv_unsigneds[counter_for_recv_unsigneds] << std::endl;
1474#endif
1475 unsigned n_ref_val = recv_unsigneds[counter_for_recv_unsigneds++];
1476
1477 // The reference values themselves are in
1478 // All_alg_ref_value
1479 ref_value.resize(n_ref_val);
1480 for (unsigned i_ref = 0; i_ref < n_ref_val; i_ref++)
1481 {
1482 ref_value[i_ref] = recv_doubles[counter_for_recv_doubles++];
1483 }
1484
1485 Vector<GeomObject*> geom_object_pt;
1486 /// again we need the size of this vector as it varies
1487 /// between meshes; we also need some indication
1488 /// as to which geometric object should be used...
1489
1490 // The size of this vector is in the next entry
1491 // of All_alg_nodal_info
1492#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
1493 oomph_info << "Rec:" << counter_for_recv_unsigneds
1494 << " Alg node # of geom objects "
1495 << recv_unsigneds[counter_for_recv_unsigneds] << std::endl;
1496#endif
1497 unsigned n_geom_obj = recv_unsigneds[counter_for_recv_unsigneds++];
1498
1499 // The remaining indices are in the rest of
1500 // All_alg_nodal_info
1501 geom_object_pt.resize(n_geom_obj);
1502 for (unsigned i_geom = 0; i_geom < n_geom_obj; i_geom++)
1503 {
1504#ifdef ANNOTATE_MISSING_MASTERS_COMMUNICATION
1505 oomph_info << "Rec:" << counter_for_recv_unsigneds
1506 << " Alg node: geom object index "
1507 << recv_unsigneds[counter_for_recv_unsigneds] << std::endl;
1508#endif
1509 unsigned geom_index = recv_unsigneds[counter_for_recv_unsigneds++];
1510 // This index indicates which of the AlgebraicMesh's
1511 // stored geometric objects should be used
1512 // (0 is a null pointer; everything else should have
1513 // been filled in by the specific Mesh). If it
1514 // hasn't been filled in then the update_node_update
1515 // call should fix it
1516 geom_object_pt[i_geom] = alg_mesh_pt->geom_object_list_pt(geom_index);
1517 }
1518
1519 /// For the received update_id, ref_value, geom_object
1520 /// call add_node_update_info
1521 new_alg_nod_pt->add_node_update_info(
1522 update_id, alg_mesh_pt, geom_object_pt, ref_value);
1523
1524 /// Now call update_node_update
1525 alg_mesh_pt->update_node_update(new_alg_nod_pt);
1526 }
1527
1528 // Is the node a MacroElementNodeUpdateNode?
1529 MacroElementNodeUpdateNode* macro_nod_pt =
1530 dynamic_cast<MacroElementNodeUpdateNode*>(new_nod_pt);
1531
1532 if (macro_nod_pt != 0)
1533 {
1534 // Need to call set_node_update_info; this requires
1535 // a Vector<GeomObject*> (taken from the mesh)
1536 Vector<GeomObject*> geom_object_vector_pt;
1537
1538 // Access the required geom objects from the
1539 // MacroElementNodeUpdateMesh
1540 MacroElementNodeUpdateMesh* macro_mesh_pt =
1541 dynamic_cast<MacroElementNodeUpdateMesh*>(mesh_pt);
1542 geom_object_vector_pt = macro_mesh_pt->geom_object_vector_pt();
1543
1544 // Get local coordinate of node in new element
1545 Vector<double> s_in_macro_node_update_element;
1546 new_el_pt->local_coordinate_of_node(node_index,
1547 s_in_macro_node_update_element);
1548
1549 // Set node update info for this node
1550 macro_nod_pt->set_node_update_info(
1551 new_el_pt, s_in_macro_node_update_element, geom_object_vector_pt);
1552 }
1553
1554 // Is the new node a SolidNode?
1555 SolidNode* solid_nod_pt = dynamic_cast<SolidNode*>(new_nod_pt);
1556 if (solid_nod_pt != 0)
1557 {
1558 unsigned n_solid_val = solid_nod_pt->variable_position_pt()->nvalue();
1559 for (unsigned i_val = 0; i_val < n_solid_val; i_val++)
1560 {
1561 for (unsigned t = 0; t < n_prev; t++)
1562 {
1563 solid_nod_pt->variable_position_pt()->set_value(
1564 t, i_val, recv_doubles[counter_for_recv_doubles++]);
1565 }
1566 }
1567 }
1568
1569 // If there are additional values, resize the node
1570 unsigned n_new_val = new_nod_pt->nvalue();
1571 if (n_val > n_new_val)
1572 {
1573 new_nod_pt->resize(n_val);
1574 }
1575
1576 // Get copied history values
1577 // unsigned n_val=new_nod_pt->nvalue();
1578 for (unsigned i_val = 0; i_val < n_val; i_val++)
1579 {
1580 for (unsigned t = 0; t < n_prev; t++)
1581 {
1582 new_nod_pt->set_value(
1583 t, i_val, recv_doubles[counter_for_recv_doubles++]);
1584 }
1585 }
1586
1587 // Get copied history values for positions
1588 unsigned n_dim = new_nod_pt->ndim();
1589 for (unsigned idim = 0; idim < n_dim; idim++)
1590 {
1591 for (unsigned t = 0; t < n_prev; t++)
1592 {
1593 // Copy to coordinate
1594 new_nod_pt->x(t, idim) = recv_doubles[counter_for_recv_doubles++];
1595 }
1596 }
1597 }
1598
1599
1600#endif
1601
1602 } // namespace Missing_masters_functions
1603
1604} // namespace oomph
cstr elem_len * i
Definition: cfortran.h:603
char t
Definition: cfortran.h:568
////////////////////////////////////////////////////////////////////
virtual void update_node_update(AlgebraicNode *&node_pt)=0
Update the node update info for given node, following mesh adaptation. Must be implemented for every ...
GeomObject * geom_object_list_pt(const unsigned &i)
Access function to the ith GeomObject.
unsigned ngeom_object_list_pt()
Return number of geometric objects associated with AlgebraicMesh.
////////////////////////////////////////////////////////////////////
unsigned ngeom_object(const int &id)
Number of geometric objects involved in id-th update function.
unsigned nref_value(const int &id)
Number of reference values involved in id-th update function.
GeomObject * geom_object_pt(const unsigned &i)
Return pointer to i-th geometric object involved in default (usually first) update function.
int node_update_fct_id()
Default (usually first if there are multiple ones) node update fct id.
double ref_value(const unsigned &i)
Return i-th reference value involved in default (usually first) update function.
void add_node_update_info(const int &id, AlgebraicMesh *mesh_pt, const Vector< GeomObject * > &geom_object_pt, const Vector< double > &ref_value, const bool &called_from_constructor=false)
Add algebraic update information for node: What's the ID of the mesh update function (typically used ...
A class that contains the information required by Nodes that are located on Mesh boundaries....
Definition: nodes.h:1996
std::map< unsigned, unsigned > *& index_of_first_value_assigned_by_face_element_pt()
Return pointer to the map giving the index of the first face element value.
Definition: nodes.h:2046
TimeStepper *& time_stepper_pt()
Return the pointer to the timestepper.
Definition: nodes.h:238
bool is_halo() const
Is this Data a halo?
Definition: nodes.h:532
void set_value(const unsigned &i, const double &value_)
Set the i-th stored data value to specified value. The only reason that we require an explicit set fu...
Definition: nodes.h:271
unsigned nvalue() const
Return number of values stored in data object (incl pinned ones).
Definition: nodes.h:483
double value(const unsigned &i) const
Return i-th stored value. This function is not virtual so that it can be inlined. This means that if ...
Definition: nodes.h:293
int non_halo_proc_ID()
ID of processor ID that holds non-halo counterpart of halo node; negative if not a halo.
Definition: nodes.h:539
A general Finite Element class.
Definition: elements.h:1313
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:1842
virtual Node * construct_node(const unsigned &n)
Construct the local node n and return a pointer to the newly created node object.
Definition: elements.h:2509
unsigned dim() const
Return the spatial dimension of the element, i.e. the number of local coordinates required to paramet...
Definition: elements.h:2611
unsigned nnode() const
Return the number of nodes.
Definition: elements.h:2210
MacroElement * macro_elem_pt()
Access function to pointer to macro element.
Definition: elements.h:1878
virtual Node * construct_boundary_node(const unsigned &n)
Construct the local node n as a boundary node; that is a node that MAY be placed on a mesh boundary a...
Definition: elements.h:2538
Node *& node_pt(const unsigned &n)
Return a pointer to the local node n.
Definition: elements.h:2175
A Generalised Element class.
Definition: elements.h:73
/////////////////////////////////////////////////////////////////////
Definition: geom_objects.h:101
Class that contains data for hanging nodes.
Definition: nodes.h:742
Node *const & master_node_pt(const unsigned &i) const
Return a pointer to the i-th master node.
Definition: nodes.h:791
unsigned nmaster() const
Return the number of master nodes.
Definition: nodes.h:785
double const & master_weight(const unsigned &i) const
Return weight for dofs on i-th master node.
Definition: nodes.h:808
MacroElementNodeUpdateMeshes contain MacroElementNodeUpdateNodes which have their own node update fun...
Vector< GeomObject * > geom_object_vector_pt()
Access function to the vector of GeomObject.
////////////////////////////////////////////////////////////////////
void set_node_update_info(FiniteElement *node_update_element_pt, const Vector< double > &s_in_node_update_element, const Vector< GeomObject * > &geom_object_pt)
Set node update information for node: Pass the pointer to the element that performs the update operat...
FiniteElement *& node_update_element_pt()
Pointer to finite element that performs the update by referring to its macro-element representation (...
Base class for MacroElement s that are used during mesh refinement in domains with curvlinear and/or ...
Definition: macro_element.h:73
unsigned & macro_element_number()
Access function to the Macro_element_number.
A general mesh class.
Definition: mesh.h:67
void add_boundary_node(const unsigned &b, Node *const &node_pt)
Add a (pointer to) a node to the b-th boundary.
Definition: mesh.cc:243
Node *& external_halo_node_pt(const unsigned &p, const unsigned &j)
Access fct to the j-th external halo node in this Mesh whose non-halo external counterpart is held on...
Definition: mesh.h:2377
unsigned nexternal_haloed_element()
Total number of external haloed elements in this Mesh.
Definition: mesh.h:2267
unsigned add_external_haloed_node_pt(const unsigned &p, Node *&nod_pt)
Add external haloed node whose halo (external) counterpart is held on processor p to the storage sche...
Definition: mesh.cc:9516
GeneralisedElement *& external_haloed_element_pt(const unsigned &p, const unsigned &e)
Access fct to the e-th external haloed element in this Mesh whose non-halo counterpart is held on pro...
Definition: mesh.h:2296
FiniteElement * finite_element_pt(const unsigned &e) const
Upcast (downcast?) to FiniteElement (needed to access FiniteElement member functions).
Definition: mesh.h:473
unsigned nboundary() const
Return number of boundaries.
Definition: mesh.h:827
unsigned add_external_haloed_element_pt(const unsigned &p, GeneralisedElement *&el_pt)
Add external haloed element whose non-halo counterpart is held on processor p to the storage scheme f...
Definition: mesh.cc:9475
void add_external_halo_node_pt(const unsigned &p, Node *&nod_pt)
Add external halo node whose non-halo (external) counterpart is held on processor p to the storage sc...
Definition: mesh.h:2368
void get_shared_node_pt(const unsigned &p, Vector< Node * > &shared_node_pt)
Get vector of pointers to shared nodes with processor p. Required for faster search in Missing_master...
Definition: mesh.h:2119
OomphCommunicator * communicator_pt() const
Read-only access fct to communicator (Null if mesh is not distributed, i.e. if we don't have mpi).
Definition: mesh.h:1600
unsigned nexternal_haloed_node()
Total number of external haloed nodes in this Mesh.
Definition: mesh.h:2412
Vector< GeneralisedElement * > haloed_element_pt(const unsigned &p)
Return vector of haloed elements in this Mesh whose haloing counterpart is held on processor p.
Definition: mesh.h:1779
Node * shared_node_pt(const unsigned &p, const unsigned &j)
Access fct to the j-th shared node in this Mesh who has a counterpart on processor p.
Definition: mesh.h:2110
Nodes are derived from Data, but, in addition, have a definite (Eulerian) position in a space of a gi...
Definition: nodes.h:906
virtual bool is_on_boundary() const
Test whether the Node lies on a boundary. The "bulk" Node cannot lie on a boundary,...
Definition: nodes.h:1373
unsigned ndim() const
Return (Eulerian) spatial dimension of the node.
Definition: nodes.h:1054
double & x(const unsigned &i)
Return the i-th nodal coordinate.
Definition: nodes.h:1060
void resize(const unsigned &n_value)
Resize the number of equations.
Definition: nodes.cc:2167
unsigned nposition_type() const
Number of coordinate types needed in the mapping between local and global coordinates.
Definition: nodes.h:1016
bool is_hanging() const
Test whether the node is geometrically hanging.
Definition: nodes.h:1285
double value(const unsigned &i) const
Return i-th value (dofs or pinned) at this node either directly or via hanging node representation....
Definition: nodes.cc:2408
HangInfo *const & hanging_pt() const
Return pointer to hanging node data (this refers to the geometric hanging node status) (const version...
Definition: nodes.h:1228
An OomphLibError object which should be thrown when an run-time error is encountered....
/////////////////////////////////////////////////////////////////// /////////////////////////////////...
unsigned & p_order()
Access function to P_order.
/////////////////////////////////////////////////////////////////// /////////////////////////////////...
Definition: Qelements.h:91
double & s_macro_ur(const unsigned &i)
Access fct to the i-th coordinate of the element's "upper right" vertex in the associated MacroElemen...
Definition: Qelements.h:202
double & s_macro_ll(const unsigned &i)
Access fct to the i-th coordinate of the element's "lower left" vertex in the associated MacroElement...
Definition: Qelements.h:186
A Class for nodes that deform elastically (i.e. position is an unknown in the problem)....
Definition: nodes.h:1686
Data *const & variable_position_pt() const
Pointer to variable_position data (const version)
Definition: nodes.h:1765
unsigned nlagrangian_type() const
Number of types of Lagrangian coordinates used to interpolate the Lagrangian coordinates within the e...
Definition: nodes.h:1877
unsigned nlagrangian() const
Return number of lagrangian coordinates.
Definition: nodes.h:1870
////////////////////////////////////////////////////////////////////// //////////////////////////////...
Definition: timesteppers.h:231
unsigned ntstorage() const
Return the number of doubles required to represent history (one for steady)
Definition: timesteppers.h:601
A slight extension to the standard template vector class so that we can include "graceful" array rang...
Definition: Vector.h:58
bool Doc_full_stats
Boolean to indicate whether to output further info during setup_multi_domain_interaction() routines.
void add_external_halo_node_helper(Node *&new_nod_pt, Mesh *const &mesh_pt, unsigned &loc_p, unsigned &node_index, FiniteElement *const &new_el_pt, int &n_cont_inter_values, unsigned &counter_for_recv_unsigneds, Vector< unsigned > &recv_unsigneds, unsigned &counter_for_recv_doubles, Vector< double > &recv_doubles)
Helper functiono to add external halo node that is not a master.
void get_required_master_nodal_information_helper(int &iproc, Node *master_nod_pt, Mesh *const &mesh_pt, int &n_cont_inter_values, Vector< unsigned > &send_unsigneds, Vector< double > &send_doubles)
Helper function to get the required master nodal information from an external haloed master node so t...
void add_external_haloed_node_helper(int &iproc, Node *nod_pt, Mesh *const &mesh_pt, int &n_cont_inter_values, Vector< unsigned > &send_unsigneds, Vector< double > &send_doubles)
Helper to add external haloed node that is not a master.
bool Doc_stats
Boolean to indicate whether to output basic info during setup_multi_domain_interaction() routines.
void add_external_haloed_node_to_storage(int &iproc, Node *nod_pt, Mesh *const &mesh_pt, int &n_cont_inter_values, Vector< unsigned > &send_unsigneds, Vector< double > &send_doubles)
Helper function to add external haloed nodes, including any masters.
void recursively_add_masters_of_external_haloed_node(int &iproc, Node *nod_pt, Mesh *const &mesh_pt, int &n_cont_inter_values, Vector< unsigned > &send_unsigneds, Vector< double > &send_doubles)
Recursively add any master nodes (and their master nodes etc) of external nodes.
void get_required_nodal_information_helper(int &iproc, Node *nod_pt, Mesh *const &mesh_pt, int &n_cont_inter_values, Vector< unsigned > &send_unsigneds, Vector< double > &send_doubles)
Helper function to get the required nodal information from an external haloed node so that a fully-fu...
void add_external_haloed_master_node_helper(int &iproc, Node *master_nod_pt, Mesh *const &mesh_pt, int &n_cont_inter_values, Vector< unsigned > &send_unsigneds, Vector< double > &send_doubles)
Helper function to add external haloed node that is a master.
bool Doc_timings
Boolean to indicate whether to doc timings or not.
void construct_new_external_halo_node_helper(Node *&new_nod_pt, unsigned &loc_p, unsigned &node_index, FiniteElement *const &new_el_pt, Mesh *const &mesh_pt, unsigned &counter_for_recv_unsigneds, Vector< unsigned > &recv_unsigneds, unsigned &counter_for_recv_doubles, Vector< double > &recv_doubles)
Helper function which constructs a new external halo node (on new element) with the required informat...
Vector< std::string > Flat_packed_unsigneds_string
//////////////////////////////////////////////////////////////////// ////////////////////////////////...
OomphInfo oomph_info
Single (global) instantiation of the OomphInfo object – this is used throughout the library as a "rep...