problem.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// Version 1.0; svn revision $LastChangedRevision$
7 //LIC//
8 //LIC// $LastChangedDate$
9 //LIC//
10 //LIC// Copyright (C) 2006-2016 Matthias Heil and Andrew Hazel
11 //LIC//
12 //LIC// This library is free software; you can redistribute it and/or
13 //LIC// modify it under the terms of the GNU Lesser General Public
14 //LIC// License as published by the Free Software Foundation; either
15 //LIC// version 2.1 of the License, or (at your option) any later version.
16 //LIC//
17 //LIC// This library is distributed in the hope that it will be useful,
18 //LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 //LIC// Lesser General Public License for more details.
21 //LIC//
22 //LIC// You should have received a copy of the GNU Lesser General Public
23 //LIC// License along with this library; if not, write to the Free Software
24 //LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 //LIC// 02110-1301 USA.
26 //LIC//
27 //LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
28 //LIC//
29 //LIC//====================================================================
30 
31 #ifdef OOMPH_HAS_MPI
32 #include "mpi.h"
33 #endif
34 
35 #include<list>
36 #include<algorithm>
37 #include<string>
38 
39 #include "oomph_utilities.h"
40 #include "problem.h"
41 #include "timesteppers.h"
42 #include "explicit_timesteppers.h"
44 #include "refineable_mesh.h"
45 #include "triangle_mesh.h"
46 #include "linear_solver.h"
47 #include "eigen_solver.h"
48 #include "assembly_handler.h"
49 #include "dg_elements.h"
50 #include "partitioning.h"
51 #include "spines.h"
52 
53 //Include to fill in additional_setup_shared_node_scheme() function
55 
56 
57 namespace oomph
58 {
59 
60 
61 //////////////////////////////////////////////////////////////////
62 //Non-inline functions for the problem class
63 //////////////////////////////////////////////////////////////////
64 
65 //=================================================================
66 ///The continuation timestepper object
67 //=================================================================
68 ContinuationStorageScheme Problem::Continuation_time_stepper;
69 
70 //================================================================
71 /// Constructor: Allocate space for one time stepper
72 /// and set all pointers to NULL and set defaults for all
73 /// parameters.
74 //===============================================================
76  Mesh_pt(0), Time_pt(0), Explicit_time_stepper_pt(0), Saved_dof_pt(0),
77  Default_set_initial_condition_called(false),
78  Use_globally_convergent_newton_method(false),
79  Empty_actions_before_read_unstructured_meshes_has_been_called(false),
80  Empty_actions_after_read_unstructured_meshes_has_been_called(false),
81  Store_local_dof_pt_in_elements(false),
82  Calculate_hessian_products_analytic(false),
83 #ifdef OOMPH_HAS_MPI
84  Doc_imbalance_in_parallel_assembly(false),
85  Use_default_partition_in_load_balance(false),
86  Must_recompute_load_balance_for_assembly(true),
87  Halo_scheme_pt(0),
88 #endif
89  Relaxation_factor(1.0),
90  Newton_solver_tolerance(1.0e-8),
92  Nnewton_iter_taken(0),
93  Max_residuals(10.0),
94  Time_adaptive_newton_crash_on_solve_fail(false),
95  Jacobian_reuse_is_enabled(false), Jacobian_has_been_computed(false),
96  Problem_is_nonlinear(true),
97  Pause_at_end_of_sparse_assembly(false),
98  Doc_time_in_distribute(false),
99  Sparse_assembly_method(Perform_assembly_using_vectors_of_pairs),
100  Sparse_assemble_with_arrays_initial_allocation(400),
101  Sparse_assemble_with_arrays_allocation_increment(150),
102  Numerical_zero_for_sparse_assembly(0.0),
103  FD_step_used_in_get_hessian_vector_products(1.0e-8),
104  Mass_matrix_reuse_is_enabled(false), Mass_matrix_has_been_computed(false),
105  Discontinuous_element_formulation(false),
106  Minimum_dt(1.0e-12), Maximum_dt(1.0e12),
107  DTSF_max_increase(4.0),
108  DTSF_min_decrease(0.8),
109  Minimum_dt_but_still_proceed(-1.0),
110  Scale_arc_length(true), Desired_proportion_of_arc_length(0.5),
111  Theta_squared(1.0), Sign_of_jacobian(0), Continuation_direction(1.0),
112  Parameter_derivative(1.0), Parameter_current(0.0),
113  Use_continuation_timestepper(false),
114  Dof_derivative_offset(1),
115  Dof_current_offset(2),
116  Ds_current(0.0), Desired_newton_iterations_ds(5),
117  Minimum_ds(1.0e-10), Bifurcation_detection(false),
118  Bisect_to_find_bifurcation(false),
119  First_jacobian_sign_change(false),Arc_length_step_taken(false),
120  Use_finite_differences_for_continuation_derivatives(false),
121 #ifdef OOMPH_HAS_MPI
122  Dist_problem_matrix_distribution(Uniform_matrix_distribution),
123  Parallel_sparse_assemble_previous_allocation(0),
124  Problem_has_been_distributed(false),
125  Bypass_increase_in_dof_check_during_pruning(false),
126  Max_permitted_error_for_halo_check(1.0e-14),
127 #endif
128  Shut_up_in_newton_solve(false),
129  Always_take_one_newton_step(false),
130  Timestep_reduction_factor_after_nonconvergence(0.5),
131  Keep_temporal_error_below_tolerance(true)
132  {
134 
135  /// Setup terminate helper
137 
138  // By default no submeshes:
139  Sub_mesh_pt.resize(0);
140  // No timesteppers
141  Time_stepper_pt.resize(0);
142 
143  //Set the linear solvers, eigensolver and assembly handler
146 
148 
150 
151  // setup the communicator
152 #ifdef OOMPH_HAS_MPI
154  {
156  }
157  else
158  {
160  }
161 #else
163 #endif
164 
165  // just create an empty linear algebra distribution for the
166  // DOFs
167  // this is setup when assign_eqn_numbers(...) is called.
169  }
170 
171 //================================================================
172 /// Destructor to clean up memory
173 //================================================================
175  {
176 
177  // Delete the memory assigned for the global time
178  // (it's created on the fly in Problem::add_time_stepper_pt()
179  // so we are entitled to delete it.
180  if (Time_pt!=0)
181  {
182  delete Time_pt;
183  Time_pt = 0;
184  }
185 
186  // We're not using the default linear solver,
187  // somebody else must have built it, so that person
188  // must be in charge of killing it.
189  // We can safely delete the defaults, however
191 
194  delete Communicator_pt;
195  delete Dof_distribution_pt;
196 
197  // Delete any copies of the problem that have been created for
198  // use in adaptive bifurcation tracking.
199  // ALH: This will eventually go
200  unsigned n_copies = Copy_of_problem_pt.size();
201  for(unsigned c=0;c<n_copies;c++)
202  {
203  delete Copy_of_problem_pt[c];
204  }
205 
206  // if this problem has sub meshes then we must delete the Mesh_pt
207  if (Sub_mesh_pt.size() != 0)
208  {
210  delete Mesh_pt;
211  }
212  }
213 
214  //=================================================================
215  ///Setup the count vector that records how many elements contribute
216  ///to each degree of freedom. Returns the total number of elements
217  ///in the problem
218  //=================================================================
220  {
221  //Now set the element counter to have the current Dof distribution
223  //We need to use the halo scheme (assuming it has been setup)
224 #ifdef OOMPH_HAS_MPI
226 #endif
227 
228  //Loop over the elements and count the entries
229  //and number of (non-halo) elements
230  const unsigned n_element = this->mesh_pt()->nelement();
231  unsigned n_non_halo_element_local=0;
232  for(unsigned e=0;e<n_element;e++)
233  {
234  GeneralisedElement* elem_pt = this->mesh_pt()->element_pt(e);
235 #ifdef OOMPH_HAS_MPI
236  //Ignore halo elements
237  if(!elem_pt->is_halo())
238  {
239 #endif
240  //Increment the number of non halo elements
241  ++n_non_halo_element_local;
242  //Now count the number of times the element contributes to a value
243  //using the current assembly handler
244  unsigned n_var = this->Assembly_handler_pt->ndof(elem_pt);
245  for(unsigned n=0;n<n_var;n++)
246  {
248  this->Assembly_handler_pt->eqn_number(elem_pt,n));
249  }
250 #ifdef OOMPH_HAS_MPI
251  }
252 #endif
253  }
254 
255  //Storage for the total number of elements
256  unsigned Nelement=0;
257 
258  //Add together all the counts if we are in parallel
259 #ifdef OOMPH_HAS_MPI
261 
262  //If distributed, find the total number of elements in the problem
264  {
265  //Need to gather the total number of non halo elements
266  MPI_Allreduce(&n_non_halo_element_local,&Nelement,1,MPI_UNSIGNED,MPI_SUM,
267  this->communicator_pt()->mpi_comm());
268  }
269  //Otherwise the total number is the same on each processor
270  else
271 #endif
272  {
273  Nelement = n_non_halo_element_local;
274  }
275 
276  return Nelement;
277  }
278 
279 
280 #ifdef OOMPH_HAS_MPI
281 
282  //==================================================================
283  /// Setup the halo scheme for the degrees of freedom
284  //==================================================================
286  {
287  //Find the number of elements stored on this processor
288  const unsigned n_element = this->mesh_pt()->nelement();
289 
290  //Work out the all global equations to which this processor
291  //contributes
292  Vector<unsigned> my_eqns;
293  this->get_my_eqns(this->Assembly_handler_pt,0,n_element-1,my_eqns);
294 
295  //Build the halo scheme, based on the equations to which this
296  //processor contributes
298  new DoubleVectorHaloScheme(this->Dof_distribution_pt,my_eqns);
299 
300  //Find pointers to all the halo dofs
301  //There may be more of these than required by my_eqns
302  //(but should not be less)
303  std::map<unsigned,double*> halo_data_pt;
304  this->get_all_halo_data(halo_data_pt);
305 
306  //Now setup the Halo_dofs
307  Halo_scheme_pt->setup_halo_dofs(halo_data_pt,this->Halo_dof_pt);
308  }
309 
310  //==================================================================
311  /// Distribute the problem without doc; report stats if required.
312  /// Returns actual partitioning used, e.g. for restart.
313  //==================================================================
314  Vector<unsigned> Problem::distribute(const bool& report_stats)
315  {
316  // Set dummy doc paramemters
317  DocInfo doc_info;
318  doc_info.disable_doc();
319 
320  // Set the sizes of the input and output vectors
321  unsigned n_element=mesh_pt()->nelement();
322  Vector<unsigned> element_partition(n_element,0);
323 
324  // Distribute and return partitioning
325  return distribute(element_partition,doc_info,report_stats);
326  }
327 
328  //==================================================================
329  /// Distribute the problem according to specified partition.
330  /// If all entries in partitioning vector are zero we use METIS
331  /// to do the partitioning after all.
332  /// Returns actual partitioning used, e.g. for restart.
333  //==================================================================
335  (const Vector<unsigned>& element_partition, const bool& report_stats)
336  {
337 #ifdef PARANOID
338  bool has_non_zero_entry=false;
339  unsigned n=element_partition.size();
340  for (unsigned i=0;i<n;i++)
341  {
342  if (element_partition[i]!=0)
343  {
344  has_non_zero_entry=true;
345  break;
346  }
347  }
348  if (!has_non_zero_entry)
349  {
350  std::ostringstream warn_message;
351  warn_message << "WARNING: All entries in specified partitioning vector \n"
352  << " are zero -- will ignore this and use METIS\n"
353  << " to perform the partitioning\n";
354  OomphLibWarning(warn_message.str(),
355  "Problem::distribute()",
356  OOMPH_EXCEPTION_LOCATION);
357  }
358 #endif
359  // Set dummy doc paramemters
360  DocInfo doc_info;
361  doc_info.disable_doc();
362 
363  // Distribute and return partitioning
364  return distribute(element_partition,doc_info,report_stats);
365  }
366 
367  //==================================================================
368  /// Distribute the problem and doc to specified DocInfo.
369  /// Returns actual partitioning used, e.g. for restart.
370  //==================================================================
372  const bool& report_stats)
373  {
374  // Set the sizes of the input and output vectors
375  unsigned n_element=mesh_pt()->nelement();
376 
377  // Dummy input vector
378  Vector<unsigned> element_partition(n_element,0);
379 
380  // Distribute and return partitioning
381  return distribute(element_partition,doc_info,report_stats);
382  }
383 
384  //==================================================================
385  /// Distribute the problem according to specified partition.
386  /// (If all entries in partitioning vector are zero we use METIS
387  /// to do the partitioning after all) and doc.
388  /// Returns actual partitioning used, e.g. for restart.
389  //==================================================================
391  (const Vector<unsigned>& element_partition,
392  DocInfo& doc_info, const bool& report_stats)
393  {
394  // Storage for number of processors and number of elements in global mesh
395  int n_proc=this->communicator_pt()->nproc();
396  int my_rank=this->communicator_pt()->my_rank();
397  int n_element=mesh_pt()->nelement();
398 
399  // Vector to be returned
400  Vector<unsigned> return_element_domain;
401 
402  // Buffer extreme cases
403  if (n_proc==1) // single-process job - don't do anything
404  {
405  if (report_stats)
406  {
407  std::ostringstream warn_message;
408  warn_message << "WARNING: You've tried to distribute a problem over\n"
409  << "only one processor: this would make METIS crash.\n"
410  << "Ignoring your request for distribution.\n";
411  OomphLibWarning(warn_message.str(),
412  "Problem::distribute()",
413  OOMPH_EXCEPTION_LOCATION);
414  }
415  }
416  else if (n_proc>n_element) // more processors than elements
417  {
418  // Throw an error
419  std::ostringstream error_stream;
420  error_stream << "You have tried to distribute a problem\n"
421  << "but there are less elements than processors.\n"
422  << "Please re-run with more elements!\n"
423  << "Please also ensure that actions_before_distribute().\n"
424  << "and actions_after_distribute() are correctly set up.\n"
425  << std::endl;
426  throw OomphLibError(error_stream.str(),
427  OOMPH_CURRENT_FUNCTION,
428  OOMPH_EXCEPTION_LOCATION);
429  }
430  else
431  {
432  // We only distribute uniformly-refined meshes; buffer the case where
433  // either mesh is not uniformly refined
434  bool a_mesh_is_not_uniformly_refined=false;
435  unsigned n_mesh=nsub_mesh();
436  if (n_mesh==0)
437  {
438  // Check refinement levels
439  if (TreeBasedRefineableMeshBase* mmesh_pt =
440  dynamic_cast<TreeBasedRefineableMeshBase*>(mesh_pt(0)))
441  {
442  unsigned min_ref_level=0;
443  unsigned max_ref_level=0;
444  mmesh_pt->get_refinement_levels(min_ref_level,max_ref_level);
445  // If they are not the same
446  if (max_ref_level!=min_ref_level)
447  {
448  a_mesh_is_not_uniformly_refined=true;
449  }
450  }
451  }
452  else
453  {
454  for (unsigned i_mesh=0;i_mesh<n_mesh;i_mesh++)
455  {
456  // Check refinement levels for each mesh individually
457  // (one mesh is allowed to be "more uniformly refined" than another)
458  if (TreeBasedRefineableMeshBase* mmesh_pt =
459  dynamic_cast<TreeBasedRefineableMeshBase*>(mesh_pt(i_mesh)))
460  {
461  unsigned min_ref_level=0;
462  unsigned max_ref_level=0;
463  mmesh_pt->get_refinement_levels(min_ref_level,max_ref_level);
464  // If they are not the same
465  if (max_ref_level!=min_ref_level)
466  {
467  a_mesh_is_not_uniformly_refined=true;
468  }
469  }
470  }
471  }
472 
473  // If any mesh is not uniformly refined
474  if (a_mesh_is_not_uniformly_refined)
475  {
476  // Again it may make more sense to throw an error here as the user
477  // will probably not be running a problem that is small enough to
478  // fit the whole of on each processor
479  std::ostringstream error_stream;
480  error_stream << "You have tried to distribute a problem\n"
481  << "but at least one of your meshes is no longer\n"
482  << "uniformly refined. In order to preserve the Tree\n"
483  << "and TreeForest structure, Problem::distribute() can\n"
484  << "only be called while meshes are uniformly refined.\n"
485  << std::endl;
486  throw OomphLibError(error_stream.str(),
487  OOMPH_CURRENT_FUNCTION,
488  OOMPH_EXCEPTION_LOCATION);
489  }
490  else
491  {
492  // Is there any global data? If so, distributing the problem won't work
493  if (nglobal_data() > 0)
494  {
495  std::ostringstream error_stream;
496  error_stream << "You have tried to distribute a problem\n"
497  << "and there is some global data.\n"
498  << "This is not likely to work...\n"
499  << std::endl;
500  throw OomphLibError(error_stream.str(),
501  OOMPH_CURRENT_FUNCTION,
502  OOMPH_EXCEPTION_LOCATION);
503  }
504 
505  double t_start = 0;
507  {
508  t_start=TimingHelpers::timer();
509  }
510 
511 
512 #ifdef PARANOID
513  unsigned old_ndof=ndof();
514 #endif
515 
516  // Need to partition the global mesh before distributing
517  Mesh* global_mesh_pt = mesh_pt();
518 
519  // Vector listing the affiliation of each element
520  unsigned nelem=global_mesh_pt->nelement();
521  Vector<unsigned> element_domain(nelem);
522 
523  // Number of elements that I'm in charge of, based on any
524  // incoming partitioning
525  unsigned n_my_elements=0;
526 
527  // Have we used the pre-set partitioning
528  bool used_preset_partitioning=false;
529 
530  // Partition the mesh, unless the partition has already been passed in
531  // If it hasn't then the sum of all the entries of the vector should be 0
532  unsigned sum_element_partition=0;
533  unsigned n_part=element_partition.size();
534  for (unsigned e=0;e<n_part;e++)
535  {
536  // ... another one for me.
537  if (int(element_partition[e])==my_rank) n_my_elements++;
538 
539  sum_element_partition+=element_partition[e];
540  }
541  if (sum_element_partition==0)
542  {
543  oomph_info << "INFO: using METIS to partition elements"
544  << std::endl;
545  partition_global_mesh(global_mesh_pt,doc_info,element_domain);
546  used_preset_partitioning=false;
547  }
548  else
549  {
550  oomph_info << "INFO: using pre-set partition of elements"
551  << std::endl;
552  used_preset_partitioning=true;
553  element_domain=element_partition;
554  }
555 
556  // Set the GLOBAL Mesh as being distributed
557  global_mesh_pt->set_communicator_pt(this->communicator_pt());
558 
559  double t_end = 0.0;
561  {
562  t_end=TimingHelpers::timer();
563  oomph_info << "Time for partitioning of global mesh: "
564  << t_end-t_start << std::endl;
565  t_start = TimingHelpers::timer();
566  }
567 
568  // Store how many elements we had in the various sub-meshes
569  // before actions_before_distribute() (which may empty some of
570  // them).
571  Vector<unsigned> n_element_in_old_submesh(n_mesh);
572  if (n_mesh!=0)
573  {
574  for (unsigned i_mesh=0;i_mesh<n_mesh;i_mesh++)
575  {
576  unsigned nsub_elem=mesh_pt(i_mesh)->nelement();
577  n_element_in_old_submesh[i_mesh]=nsub_elem;
578  }
579  }
580 
581  // Partitioning complete; call actions before distribute
583 
585  {
586  t_end = TimingHelpers::timer();
587  oomph_info << "Time for actions before distribute: "
588  << t_end-t_start << std::endl;
589  }
590 
591  // This next bit is cheap -- omit timing
592  // t_start = TimingHelpers::timer();
593 
594  // Number of submeshes (NB: some may have been deleted in
595  // actions_after_distribute())
596  n_mesh=nsub_mesh();
597 
598 
599  // Prepare vector of vectors for submesh element domains
600  Vector<Vector<unsigned> > submesh_element_domain(n_mesh);
601 
602  // The submeshes need to know their own element domains.
603  // Also if any meshes have been emptied we ignore their
604  // partitioning in the vector that we return from here
605  return_element_domain.reserve(element_domain.size());
606  if (n_mesh!=0)
607  {
608  unsigned count=0;
609  for (unsigned i_mesh=0;i_mesh<n_mesh;i_mesh++)
610  {
611  unsigned nsub_elem=mesh_pt(i_mesh)->nelement();
612  submesh_element_domain[i_mesh].resize(nsub_elem);
613  unsigned nsub_elem_old=n_element_in_old_submesh[i_mesh];
614  for (unsigned e=0; e<nsub_elem_old; e++)
615  {
616  if (nsub_elem_old==nsub_elem)
617  {
618  submesh_element_domain[i_mesh][e]=element_domain[count];
619  return_element_domain.push_back(element_domain[count]);
620  }
621  //return_element_domain.push_back(element_domain[count]);
622  count++;
623  }
624  }
625  }
626  else
627  {
628  return_element_domain=element_domain;
629  }
630 
632  {
633  t_start = TimingHelpers::timer();
634  }
635 
636  // Setup the map between "root" element and number in global mesh
637  // (currently used in the load_balance() routines)
638 
639  // This map is only established for structured meshes, then we
640  // need to check here the type of mesh
641  if (n_mesh==0)
642  {
643  // Check if the only one mesh is an structured mesh
644  bool structured_mesh = true;
645  TriangleMeshBase* tri_mesh_pt =
646  dynamic_cast<TriangleMeshBase*>(mesh_pt(0));
647  if (tri_mesh_pt != 0)
648  {
649  structured_mesh = false;
650  } // if (tri_mesh_pt != 0)
651  if (structured_mesh)
652  {
653  const unsigned n_ele = global_mesh_pt->nelement();
654  Base_mesh_element_pt.resize(n_ele);
656  for (unsigned e=0;e<n_ele;e++)
657  {
658  GeneralisedElement* el_pt=global_mesh_pt->element_pt(e);
660  Base_mesh_element_pt[e]=el_pt;
661  } // for (e<n_ele)
662  } // A TreeBaseMesh mesh
663  } // if (n_mesh==0)
664  else
665  {
666  // If we have submeshes then we only add those elements that
667  // belong to structured meshes, but first compute the number
668  // of total elements in the structured meshes
669  unsigned nglobal_element = 0;
670  // Store which submeshes are structured
671  std::vector<bool> is_structured_mesh(n_mesh);
672  for (unsigned i_mesh=0;i_mesh<n_mesh;i_mesh++)
673  {
674  TriangleMeshBase* tri_mesh_pt =
675  dynamic_cast<TriangleMeshBase*>(mesh_pt(i_mesh));
676  if (tri_mesh_pt != 0)
677  {
678  // Set the flags to indicate this is not an structured
679  // mesh
680  is_structured_mesh[i_mesh] = false;
681  } // if (tri_mesh_pt != 0)
682  else
683  {
684  // Set the flags to indicate this is an structured
685  // mesh
686  is_structured_mesh[i_mesh] = true;
687  } // else if (tri_mesh_pt != 0)
688  // Check if mesh is an structured mesh
689  if (is_structured_mesh[i_mesh])
690  {
691  nglobal_element+=mesh_pt(i_mesh)->nelement();
692  } // A TreeBaseMesh mesh
693  } // for (i_mesh<n_mesh)
694 
695  // Once computed the number of elements, then resize the
696  // structure
697  Base_mesh_element_pt.resize(nglobal_element);
699  unsigned counter = 0;
700  for (unsigned i_mesh=0;i_mesh<n_mesh;i_mesh++)
701  {
702  // Check if mesh is an structured mesh
703  if (is_structured_mesh[i_mesh])
704  {
705  const unsigned n_ele = mesh_pt(i_mesh)->nelement();
706  for (unsigned e=0;e<n_ele;e++)
707  {
708  GeneralisedElement* el_pt=mesh_pt(i_mesh)->element_pt(e);
709  Base_mesh_element_number_plus_one[el_pt]=counter+1;
710  Base_mesh_element_pt[counter]=el_pt;
711  // Inrease the global element number
712  counter++;
713  } // for (e<n_ele)
714  } // An structured mesh
715  } // for (i_mesh<n_mesh)
716 
717 #ifdef PARANOID
718  if (counter != nglobal_element)
719  {
720  std::ostringstream error_stream;
721  error_stream
722  << "The number of global elements ("<<nglobal_element
723  <<") is not the sameas the number of\nadded elements ("
724  << counter <<") to the Base_mesh_element_pt data "
725  << "structure!!!\n\n";
726  throw OomphLibError(error_stream.str(),
727  "Problem::distribute()",
728  OOMPH_EXCEPTION_LOCATION);
729  } // if (counter != nglobal_element)
730 #endif // #ifdef PARANOID
731 
732  } // else if (n_mesh==0)
733 
734  // Wipe everything if a pre-determined partitioning
735  // didn't specify ANY elements for this processor
736  // (typically happens during restarts with larger number
737  // of processors -- in this case we really want an empty
738  // processor rather than one with any "kept" halo elements)
739  bool overrule_keep_as_halo_element_status=false;
740  if ((n_my_elements==0)&&(used_preset_partitioning))
741  {
742  oomph_info << "INFO: We're over-ruling the \"keep as halo element\"\n"
743  << " status because the preset partitioning\n"
744  << " didn't place ANY elements on this processor,\n"
745  << " probably because of a restart on a larger \n"
746  << " number of processors\n";
747  overrule_keep_as_halo_element_status=true;
748  }
749 
750 
751  // Distribute the (sub)meshes (i.e. sort out their halo lookup schemes)
752  Vector<GeneralisedElement*> deleted_element_pt;
753  if (n_mesh==0)
754  {
755  global_mesh_pt->distribute(this->communicator_pt(),
756  element_domain,
757  deleted_element_pt,
758  doc_info,report_stats,
759  overrule_keep_as_halo_element_status);
760  }
761  else // There are submeshes, "distribute" each one separately
762  {
763  for (unsigned i_mesh=0; i_mesh<n_mesh; i_mesh++)
764  {
765  if (report_stats)
766  {
767  oomph_info << "Distributing submesh " << i_mesh << std::endl
768  << "--------------------" << std::endl;
769  }
770  // Set the doc_info number to reflect the submesh
771  doc_info.number()=i_mesh;
772  mesh_pt(i_mesh)->distribute(this->communicator_pt(),
773  submesh_element_domain[i_mesh],
774  deleted_element_pt,
775  doc_info,report_stats,
776  overrule_keep_as_halo_element_status);
777  }
778  // Rebuild the global mesh
780  }
781 
782  // Null out information associated with deleted elements
783  unsigned n_del=deleted_element_pt.size();
784  for (unsigned e=0;e<n_del;e++)
785  {
786  GeneralisedElement* el_pt=deleted_element_pt[e];
787  unsigned old_el_number=Base_mesh_element_number_plus_one[el_pt]-1;
789  Base_mesh_element_pt[old_el_number]=0;
790  }
791 
793  {
794  t_end = TimingHelpers::timer();
795  oomph_info << "Time for mesh-level distribution: "
796  << t_end-t_start << std::endl;
797  t_start = TimingHelpers::timer();
798  }
799 
800  // Now the problem has been distributed
802 
803  // Call actions after distribute
805 
807  {
808  t_end = TimingHelpers::timer();
809  oomph_info << "Time for actions after distribute: "
810  << t_end-t_start << std::endl;
811  t_start = TimingHelpers::timer();
812  }
813 
814  // Re-assign the equation numbers (incl synchronisation if reqd)
815  unsigned n_dof=assign_eqn_numbers();
816  oomph_info << "Number of equations: " << n_dof
817  << std::endl;
818 
820  {
821  t_end = TimingHelpers::timer();
822  oomph_info << "Time for re-assigning eqn numbers (in distribute): "
823  << t_end-t_start << std::endl;
824  }
825 
826 
827 #ifdef PARANOID
828  if (n_dof!=old_ndof)
829  {
830  std::ostringstream error_stream;
831  error_stream
832  << "Number of dofs in distribute() has changed "
833  << "from " << old_ndof << " to " << n_dof << "\n"
834  <<"Check that you've implemented any necessary actions_before/after\n"
835  << "distribute functions, e.g. to pin redundant pressure dofs"
836  << " etc.\n";
837  throw OomphLibError(error_stream.str(),
838  OOMPH_CURRENT_FUNCTION,
839  OOMPH_EXCEPTION_LOCATION);
840  }
841 #endif
842 
843  } // end if to check for uniformly refined mesh(es)
844 
845  } // end if to check number of processors vs. number of elements etc.
846 
847 
848  // Force re-analysis of time spent on assembly each
849  // elemental Jacobian
851  Elemental_assembly_time.clear();
852 
853  // Return the partition vector used in the distribution
854  return return_element_domain;
855 
856  }
857 
858  //==================================================================
859  /// Partition the global mesh, return vector specifying the processor
860  /// number for each element. Virtual so that it can be overloaded by
861  /// any user; the default is to use METIS to perform the partitioning
862  /// (with a bit of cleaning up afterwards to sort out "special cases").
863  //==================================================================
864  void Problem::partition_global_mesh(Mesh* &global_mesh_pt, DocInfo& doc_info,
865  Vector<unsigned>& element_domain,
866  const bool& report_stats)
867  {
868  // Storage for number of processors and current processor
869  int n_proc=this->communicator_pt()->nproc();
870  int rank=this->communicator_pt()->my_rank();
871 
872  std::ostringstream filename;
873  std::ofstream some_file;
874 
875  // Doc the original mesh on proc 0
876  //--------------------------------
877  if (doc_info.is_doc_enabled())
878  {
879  if (rank==0)
880  {
881  filename << doc_info.directory() << "/complete_mesh"
882  << doc_info.number() << ".dat";
883  global_mesh_pt->output(filename.str().c_str(),5);
884  }
885  }
886 
887  // Partition the mesh
888  //-------------------
889  // METIS Objective (0: minimise edge cut; 1: minimise total comm volume)
890  unsigned objective=0;
891 
892  // Do the partitioning
893  unsigned nelem=0;
894  if (this->communicator_pt()->my_rank()==0)
895  {
896  METIS::partition_mesh(this,n_proc,objective,element_domain);
897  nelem=element_domain.size();
898  }
899  MPI_Bcast(&nelem,1,MPI_UNSIGNED,0,this->communicator_pt()->mpi_comm());
900  element_domain.resize(nelem);
901  MPI_Bcast(&element_domain[0],nelem,MPI_UNSIGNED,0,
902  this->communicator_pt()->mpi_comm());
903 
904  // On very coarse meshes with larger numbers of processors, METIS
905  // occasionally returns an element_domain Vector for which a particular
906  // processor has no elements affiliated to it; the following fixes this
907 
908  // Convert element_domain to integer storage
909  Vector<int> int_element_domain(nelem);
910  for (unsigned e=0;e<nelem;e++)
911  {
912  int_element_domain[e]=element_domain[e];
913  }
914 
915  // Global storage for number of elements on each process
916  int my_number_of_elements=0;
917  Vector<int> number_of_elements(n_proc,0);
918 
919  for (unsigned e=0;e<nelem;e++)
920  {
921  if (int_element_domain[e]==rank)
922  {
923  my_number_of_elements++;
924  }
925  }
926 
927  // Communicate the correct value for each single process into
928  // the global storage vector
929  MPI_Allgather(&my_number_of_elements,1,MPI_INT,
930  &number_of_elements[0],1,MPI_INT,
931  this->communicator_pt()->mpi_comm());
932 
933  // If a process has no elements then switch an element with the
934  // process with the largest number of elements, assuming
935  // that it still has enough elements left to share
936  int max_number_of_elements=0;
937  int process_with_max_elements=0;
938  for (int d=0;d<n_proc;d++)
939  {
940  if (number_of_elements[d]==0)
941  {
942  // Find the process with maximum number of elements
943  if (max_number_of_elements<=1)
944  {
945  for (int dd=0;dd<n_proc;dd++)
946  {
947  if (number_of_elements[dd]>max_number_of_elements)
948  {
949  max_number_of_elements=number_of_elements[dd];
950  process_with_max_elements=dd;
951  }
952  }
953  }
954 
955  // Check that this number of elements is okay for sharing
956  if (max_number_of_elements<=1)
957  {
958  // Throw an error if elements can't be shared
959  std::ostringstream error_stream;
960  error_stream << "No process has more than 1 element, and\n"
961  << "at least one process has no elements!\n"
962  << "Suggest rerunning with more refinement.\n"
963  << std::endl;
964  throw OomphLibError(error_stream.str(),
965  OOMPH_CURRENT_FUNCTION,
966  OOMPH_EXCEPTION_LOCATION);
967 
968  }
969 
970  // Loop over the element domain vector and switch
971  // one value for process "process_with_max_elements" with d
972  for (unsigned e=0;e<nelem;e++)
973  {
974  if (int_element_domain[e]==process_with_max_elements)
975  {
976  int_element_domain[e]=d;
977  // Change the numbers associated with these processes
978  number_of_elements[d]++;
979  number_of_elements[process_with_max_elements]--;
980  // Reduce the number of elements available on "max" process
981  max_number_of_elements--;
982  // Inform the user that a switch has taken place
983  if (report_stats)
984  {
985  oomph_info << "INFO: Switched element domain at position " << e
986  << std::endl
987  << "from process " << process_with_max_elements
988  << " to process " << d
989  << std::endl
990  << "which was given no elements by METIS partition"
991  << std::endl;
992  }
993  // Only need to do this once for this element loop, otherwise
994  // this will take all the elements from "max" process and put them
995  // in process d, thus leaving essentially the same problem!
996  break;
997  }
998  }
999  }
1000 
1001  }
1002 
1003  // Reassign new values to the element_domain vector
1004  for (unsigned e=0;e<nelem;e++)
1005  {
1006  element_domain[e]=int_element_domain[e];
1007  }
1008 
1009  unsigned count_elements=0;
1010  for (unsigned e=0; e<nelem; e++)
1011  {
1012  if(int(element_domain[e])==rank)
1013  {
1014  count_elements++;
1015  }
1016  }
1017 
1018  if (report_stats)
1019  {
1020  oomph_info << "I have " << count_elements
1021  << " elements from this partition" << std::endl << std::endl;
1022  }
1023  }
1024 
1025  //==================================================================
1026  /// (Irreversibly) prune halo(ed) elements and nodes, usually
1027  /// after another round of refinement, to get rid of
1028  /// excessively wide halo layers. Note that the current
1029  /// mesh will be now regarded as the base mesh and no unrefinement
1030  /// relative to it will be possible once this function
1031  /// has been called.
1032  //==================================================================
1034  const bool& report_stats)
1035  {
1036 
1037  // Storage for number of processors and current processor
1038  int n_proc=this->communicator_pt()->nproc();
1039 
1040  // Has the problem been distributed yet?
1042  {
1043  oomph_info
1044  << "WARNING: Problem::prune_halo_elements_and_nodes() was called on a "
1045  << "non-distributed Problem!" << std::endl;
1046  oomph_info << "Ignoring your request..." << std::endl;
1047  }
1048  else
1049  {
1050  // There are no halo layers to prune if it's a single-process job
1051  if (n_proc==1)
1052  {
1053  oomph_info << "WARNING: You've tried to prune halo layers on a problem\n"
1054  << "with only one processor: this is unnecessary.\n"
1055  << "Ignoring your request."
1056  << std::endl << std::endl;
1057  }
1058  else
1059  {
1060 
1061 #ifdef PARANOID
1062  unsigned old_ndof=ndof();
1063 #endif
1064 
1065  double t_start = 0.0;
1067  {
1068  t_start=TimingHelpers::timer();
1069  }
1070 
1071  // Call actions before distribute
1073 
1074  double t_end = 0.0;
1076  {
1077  t_end=TimingHelpers::timer();
1078  oomph_info
1079  << "Time for actions_before_distribute() in "
1080  << "Problem::prune_halo_elements_and_nodes(): "
1081  << t_end-t_start << std::endl;
1082  t_start = TimingHelpers::timer();
1083  }
1084 
1085  // Associate all elements with root in current Base mesh
1086  unsigned nel=Base_mesh_element_pt.size();
1087  std::map<GeneralisedElement*,unsigned> old_base_element_number_plus_one;
1088  std::vector<bool> old_root_is_halo_or_non_existent(nel,true);
1089  for (unsigned e=0;e<nel;e++)
1090  {
1091  // Get the base element
1093 
1094  // Does it exist locally?
1095  if (base_el_pt!=0)
1096  {
1097  // Check if it's a halo element
1098  if (!base_el_pt->is_halo())
1099  {
1100  old_root_is_halo_or_non_existent[e]=false;
1101  }
1102 
1103  // Not refineable: It's only the element iself
1104  RefineableElement* ref_el_pt=0;
1105  ref_el_pt=dynamic_cast<RefineableElement*>(base_el_pt);
1106  if (ref_el_pt==0)
1107  {
1108  old_base_element_number_plus_one[base_el_pt]=e+1;
1109  }
1110  // Refineable: Get entire tree of elements
1111  else
1112  {
1113  Vector<Tree*> tree_pt;
1114  ref_el_pt->tree_pt()->stick_all_tree_nodes_into_vector(tree_pt);
1115  unsigned ntree=tree_pt.size();
1116  for (unsigned t=0;t<ntree;t++)
1117  {
1118  old_base_element_number_plus_one[tree_pt[t]->object_pt()]=e+1;
1119  }
1120  }
1121  }
1122  }
1123 
1124 
1125 
1127  {
1128  t_end=TimingHelpers::timer();
1129  oomph_info
1130  << "Time for setup old root elements in "
1131  << "Problem::prune_halo_elements_and_nodes(): "
1132  << t_end-t_start << std::endl;
1133  t_start = TimingHelpers::timer();
1134  }
1135 
1136 
1137  // Now remember the old number of base elements
1138  unsigned nel_base_old=nel;
1139 
1140 
1141  // Prune the halo elements and nodes of the mesh(es)
1142  Vector<GeneralisedElement*> deleted_element_pt;
1143  unsigned n_mesh=nsub_mesh();
1144  if (n_mesh==0)
1145  {
1146  // Prune halo elements and nodes for the (single) global mesh
1147  mesh_pt()->prune_halo_elements_and_nodes(deleted_element_pt,
1148  doc_info,report_stats);
1149  }
1150  else
1151  {
1152  // Loop over individual submeshes and prune separately
1153  for (unsigned i_mesh=0; i_mesh<n_mesh; i_mesh++)
1154  {
1155  mesh_pt(i_mesh)->prune_halo_elements_and_nodes(deleted_element_pt,
1156  doc_info,report_stats);
1157  }
1158 
1159  // Rebuild the global mesh
1161  }
1162 
1164  {
1165  t_end=TimingHelpers::timer();
1166  oomph_info
1167  << "Total time for all mesh-level prunes in "
1168  << "Problem::prune_halo_elements_and_nodes(): "
1169  << t_end-t_start << std::endl;
1170  t_start = TimingHelpers::timer();
1171  }
1172 
1173  // Loop over all elements in newly rebuilt mesh (which contains
1174  // all element in "tree order"), find the roots
1175  // (which are either non-refineable elements or refineable elements
1176  // whose tree representations are TreeRoots)
1177  std::map<FiniteElement*,bool> root_el_done;
1178 
1179  // Vector storing vectors of pointers to new base elements associated
1180  // with the same old base element
1182  new_base_element_associated_with_old_base_element(nel_base_old);
1183 
1184  unsigned n_meshes = n_mesh;
1185  // Change the value for the number of submeshes if there is only
1186  // one mesh so that the loop below works if we have only one
1187  // mesh
1188  if (n_meshes == 0)
1189  {n_meshes = 1;}
1190 
1191  // Store which submeshes, if there are some are structured
1192  // meshes
1193  std::vector<bool> is_structured_mesh(n_meshes);
1194 
1195  // Loop over all elements in the rebuilt mesh, but make sure
1196  // that we are only looping over the structured meshes
1197  nel = 0;
1198  for (unsigned i_mesh = 0; i_mesh < n_meshes; i_mesh++)
1199  {
1200  TriangleMeshBase* tri_mesh_pt =
1201  dynamic_cast<TriangleMeshBase*>(mesh_pt(i_mesh));
1202  if (!(tri_mesh_pt!=0))
1203  {
1204  // Mark the mesh as structured mesh
1205  is_structured_mesh[i_mesh] = true;
1206  // Add the number of elements
1207  nel+=mesh_pt(i_mesh)->nelement();
1208  } // if (!(tri_mesh_pt!=0))
1209  else
1210  {
1211  // Mark the mesh as nonstructured mesh
1212  is_structured_mesh[i_mesh] = false;
1213  } // else if (!(tri_mesh_pt!=0))
1214  } // for (i_mesh < n_mesh)
1215 
1216  // Go for all the meshes (if there are submeshes)
1217  for (unsigned i_mesh = 0; i_mesh < n_meshes; i_mesh++)
1218  {
1219  // Only work with the elements in the mesh if it is an
1220  // structured mesh
1221  if (is_structured_mesh[i_mesh])
1222  {
1223  // Get the number of elements in the submesh
1224  const unsigned nele_submesh = mesh_pt(i_mesh)->nelement();
1225  for (unsigned e = 0; e < nele_submesh; e++)
1226  {
1227  // Get the element
1228  GeneralisedElement* el_pt = mesh_pt(i_mesh)->element_pt(e);
1229 
1230  // Not refineable: It's definitely a new base element
1231  RefineableElement* ref_el_pt=0;
1232  ref_el_pt=dynamic_cast<RefineableElement*>(el_pt);
1233  if (ref_el_pt==0)
1234  {
1235  unsigned old_base_el_no =
1236  old_base_element_number_plus_one[el_pt]-1;
1237  new_base_element_associated_with_old_base_element
1238  [old_base_el_no].push_back(el_pt);
1239  }
1240  // Refineable
1241  else
1242  {
1243  // Is it a tree root (after pruning)? In that case it's
1244  // a new base element
1245  if (dynamic_cast<TreeRoot*>(ref_el_pt->tree_pt()))
1246  {
1247  unsigned old_base_el_no =
1248  old_base_element_number_plus_one[el_pt]-1;
1249  new_base_element_associated_with_old_base_element
1250  [old_base_el_no].push_back(el_pt);
1251  }
1252  else
1253  {
1254  // Get associated root element
1255  FiniteElement* root_el_pt=
1256  ref_el_pt->tree_pt()->root_pt()->object_pt();
1257 
1258  if (!root_el_done[root_el_pt])
1259  {
1260  root_el_done[root_el_pt]=true;
1261  unsigned old_base_el_no=
1262  old_base_element_number_plus_one[el_pt]-1;
1263  new_base_element_associated_with_old_base_element
1264  [old_base_el_no].push_back(root_el_pt);
1265  }
1266  }
1267  }
1268  } // for (e < nele_submesh)
1269  } // if (is_structured_mesh[i_mesh])
1270  } // for (i_mesh < n_mesh)
1271 
1272  // Create a vector that stores how many new root/base elements
1273  // got spawned from each old root/base element in the global mesh
1274  Vector<unsigned> local_n_new_root(nel_base_old);
1275 #ifdef PARANOID
1276  Vector<unsigned> n_new_root_back(nel_base_old);
1277 #endif
1278  for (unsigned e=0;e<nel_base_old;e++)
1279  {
1280  local_n_new_root[e]=
1281  new_base_element_associated_with_old_base_element[e].size();
1282 
1283 #ifdef PARANOID
1284  // Backup so we can check that halo data was consistent
1285  n_new_root_back[e]=local_n_new_root[e];
1286 #endif
1287  }
1288 
1290  {
1291  t_end=TimingHelpers::timer();
1292  oomph_info
1293  << "Time for setup of new base elements in "
1294  << "Problem::prune_halo_elements_and_nodes(): "
1295  << t_end-t_start << std::endl;
1296  t_start = TimingHelpers::timer();
1297  }
1298 
1299  // Now do reduce operation to get information for all
1300  // old root/base elements -- the pruned (halo!) base elements contain
1301  // fewer associated new roots.
1302  Vector<unsigned> n_new_root(nel_base_old);
1303  MPI_Allreduce(&local_n_new_root[0],&n_new_root[0],nel_base_old,
1304  MPI_UNSIGNED,MPI_MAX,this->communicator_pt()->mpi_comm());
1305 
1306 
1308  {
1309  t_end=TimingHelpers::timer();
1310  oomph_info
1311  << "Time for allreduce in "
1312  << "Problem::prune_halo_elements_and_nodes(): "
1313  << t_end-t_start << std::endl;
1314  t_start = TimingHelpers::timer();
1315  }
1316 
1317  // Find out total number of base elements
1318  unsigned nel_base_new=0;
1319  for (unsigned e=0;e<nel_base_old;e++)
1320  {
1321  // Increment
1322  nel_base_new+=n_new_root[e];
1323 
1324 #ifdef PARANOID
1325  // If we already had data for this root previously then
1326  // the data ought to be consistent afterwards (since taking
1327  // the max of consistent numbers shouldn't change things -- this
1328  // deals with halo/haloed elements)
1329  if (!old_root_is_halo_or_non_existent[e])
1330  {
1331  if (n_new_root_back[e]!=0)
1332  {
1333  if (n_new_root_back[e]!=n_new_root[e])
1334  {
1335  std::ostringstream error_stream;
1336  error_stream
1337  << "Number of new root elements spawned from old root "
1338  << e << ": " << n_new_root[e] << "\nis not consistent"
1339  << " with previous value: " << n_new_root_back[e] << std::endl;
1340  throw OomphLibError(
1341  error_stream.str(),
1342  OOMPH_CURRENT_FUNCTION,
1343  OOMPH_EXCEPTION_LOCATION);
1344  }
1345  }
1346  }
1347 
1348 #endif
1349  }
1350 
1351  // Reset base_mesh information
1352  Base_mesh_element_pt.clear();
1353  Base_mesh_element_pt.resize(nel_base_new,0);
1355 
1356  // Now enumerate the new base/root elements consistently
1357  unsigned count=0;
1358  for (unsigned e=0;e<nel_base_old;e++)
1359  {
1360  // Old root is non-halo: Just add the new roots into the
1361  // new lookup scheme consecutively
1362  if (!old_root_is_halo_or_non_existent[e])
1363  {
1364  // Loop over new root/base element
1365  unsigned n_new_root=
1366  new_base_element_associated_with_old_base_element[e].size();
1367  for (unsigned j=0;j<n_new_root;j++)
1368  {
1369  // Store new root/base element
1370  GeneralisedElement* el_pt=
1371  new_base_element_associated_with_old_base_element[e][j];
1372  Base_mesh_element_pt[count]=el_pt;
1373  Base_mesh_element_number_plus_one[el_pt]=count+1;
1374 
1375  // Bump counter
1376  count++;
1377  }
1378  }
1379  // Old root element is halo so skip insertion (i.e. leave
1380  // entries in lookup schemes nulled) but increase counter to
1381  // ensure consistency between processors
1382  else
1383  {
1384  unsigned nskip=n_new_root[e];
1385  count+=nskip;
1386  }
1387  }
1388 
1389  // Re-setup the map between "root" element and number in global mesh
1390  // (used in the load_balance() routines)
1392 
1393 
1395  {
1396  t_end=TimingHelpers::timer();
1397  oomph_info
1398  << "Time for finishing off base mesh info "
1399  << "Problem::prune_halo_elements_and_nodes(): "
1400  << t_end-t_start << std::endl;
1401  t_start = TimingHelpers::timer();
1402  }
1403 
1404 
1405  // Call actions after distribute
1407 
1408 
1410  {
1411  t_end=TimingHelpers::timer();
1412  oomph_info
1413  << "Time for actions_after_distribute() "
1414  << "Problem::prune_halo_elements_and_nodes(): "
1415  << t_end-t_start << std::endl;
1416  t_start = TimingHelpers::timer();
1417  }
1418 
1419 
1420  // Re-assign the equation numbers (incl synchronisation if reqd)
1421 #ifdef PARANOID
1422  unsigned n_dof=assign_eqn_numbers();
1423 #else
1425 #endif
1426 
1427 
1429  {
1430  t_end=TimingHelpers::timer();
1431  oomph_info
1432  << "Time for assign_eqn_numbers() "
1433  << "Problem::prune_halo_elements_and_nodes(): "
1434  << t_end-t_start << std::endl;
1435  t_start = TimingHelpers::timer();
1436  }
1437 
1438 
1439 #ifdef PARANOID
1441  {
1442  if (n_dof!=old_ndof)
1443  {
1444  std::ostringstream error_stream;
1445  error_stream
1446  << "Number of dofs in prune_halo_elements_and_nodes() has changed "
1447  << "from " << old_ndof << " to " << n_dof << "\n"
1448  <<"Check that you've implemented any necessary actions_before/after"
1449  << "\nadapt/distribute functions, e.g. to pin redundant pressure"
1450  << " dofs etc.\n";
1451  throw OomphLibError(error_stream.str(),
1452  OOMPH_CURRENT_FUNCTION,
1453  OOMPH_EXCEPTION_LOCATION);
1454  }
1455  }
1456 #endif
1457 
1458 
1459  }
1460  }
1461 
1462  }
1463 
1464 
1465 #endif
1466 
1467 
1468 //===================================================================
1469 /// Build a single (global) mesh from a number
1470 /// of submeshes which are passed as a vector of pointers to the
1471 /// submeshes. The ordering is not necessarily optimal.
1472 //==============================================================
1474  {
1475 #ifdef PARANOID
1476  //Has a global mesh already been built
1477  if(Mesh_pt!=0)
1478  {
1479  std::string error_message =
1480  "Problem::build_global_mesh() called,\n";
1481  error_message += " but a global mesh has already been built:\n";
1482  error_message += "Problem::Mesh_pt is not zero!\n";
1483 
1484  throw OomphLibError(error_message,
1485  OOMPH_CURRENT_FUNCTION,
1486  OOMPH_EXCEPTION_LOCATION);
1487  }
1488  //Check that there are submeshes
1489  if(Sub_mesh_pt.size()==0)
1490  {
1491  std::string error_message =
1492  "Problem::build_global_mesh() called,\n";
1493  error_message += " but there are no submeshes:\n";
1494  error_message += "Problem::Sub_mesh_pt has no entries\n";
1495 
1496  throw OomphLibError(error_message,
1497  OOMPH_CURRENT_FUNCTION,
1498  OOMPH_EXCEPTION_LOCATION);
1499  }
1500 #endif
1501 
1502  //Create an empty mesh
1503  Mesh_pt = new Mesh();
1504 
1505  //Call the rebuild function to construct the mesh
1507  }
1508 
1509 //====================================================================
1510 /// If one of the submeshes has changed (e.g. by
1511 /// mesh adaptation) we need to update the global mesh.
1512 /// \b Note: The nodes boundary information refers to the
1513 /// boundary numbers within the submesh!
1514 /// N.B. This is essentially the same function as the Mesh constructor
1515 /// that assembles a single global mesh from submeshes
1516 //=====================================================================
1518  {
1519  // Use the function in mesh to merge the submeshes into this one
1521  }
1522 
1523 
1524 //================================================================
1525 /// Add a timestepper to the problem. The function will automatically
1526 /// create or resize the Time object so that it contains the appropriate
1527 /// number of levels of storage.
1528 //================================================================
1530  {
1531  //Add the timestepper to the vector
1532  Time_stepper_pt.push_back(time_stepper_pt);
1533 
1534  //Find the number of timesteps required by the timestepper
1535  unsigned ndt = time_stepper_pt->ndt();
1536 
1537  //If time has not been allocated, create time object with the
1538  //required number of time steps
1539  if(Time_pt==0)
1540  {
1541  Time_pt = new Time(ndt);
1542  oomph_info << "Created Time with " << ndt << " timesteps" << std::endl;
1543  }
1544  else
1545  {
1546  //If the required number of time steps is greater than currently stored
1547  //resize the time storage
1548  if(ndt > Time_pt->ndt())
1549  {
1550  Time_pt->resize(ndt);
1551  oomph_info << "Resized Time to include " << ndt << " timesteps"
1552  << std::endl;
1553  }
1554  //Otherwise report that we are OK
1555  else
1556  {
1557  oomph_info << "Time object already has storage for " << ndt
1558  << " timesteps" << std::endl;
1559  }
1560  }
1561 
1562  //Pass the pointer to time to the timestepper
1563  time_stepper_pt->time_pt() = Time_pt;
1564  }
1565 
1566 //================================================================
1567 /// Set the explicit time stepper for the problem and also
1568 /// ensure that a time object has been created.
1569 //================================================================
1572  {
1573  //Set the explicit time stepper
1575 
1576  //If time has not been allocated, create time object with the
1577  //required number of time steps
1578  if(Time_pt==0)
1579  {
1580  Time_pt = new Time(0);
1581  oomph_info << "Created Time with storage for no previous timestep"
1582  << std::endl;
1583  }
1584  else
1585  {
1586  oomph_info << "Time object already exists " << std::endl;
1587  }
1588 
1589  }
1590 
1591 
1592 #ifdef OOMPH_HAS_MPI
1593 
1594 //================================================================
1595 /// Set default first and last elements for parallel assembly
1596 /// of non-distributed problem.
1597 //================================================================
1599  {
1601  {
1602 
1603  // Minimum number of elements per processor if there are fewer elements
1604  // than processors
1605  unsigned min_el=10;
1606 
1607  // Resize and make default assignments
1608  int n_proc=this->communicator_pt()->nproc();
1609  unsigned n_elements=Mesh_pt->nelement();
1610  First_el_for_assembly.resize(n_proc,0);
1611  Last_el_plus_one_for_assembly.resize(n_proc,0);
1612 
1613  // In the absence of any better knowledge distribute work evenly
1614  // over elements
1615  unsigned range=0;
1616  unsigned lo_proc=0;
1617  unsigned hi_proc=n_proc-1;
1618  if (int(n_elements)>=n_proc)
1619  {
1620  range=unsigned(double(n_elements)/double(n_proc));
1621  }
1622  else
1623  {
1624  range=min_el;
1625  lo_proc=0;
1626  hi_proc=unsigned(double(n_elements)/double(min_el));
1627  }
1628 
1629  for (int p=lo_proc;p<=int(hi_proc);p++)
1630  {
1631  First_el_for_assembly[p] = p*range;
1632 
1633  unsigned last_el_plus_one=(p+1)*range;
1634  if (last_el_plus_one>n_elements) last_el_plus_one=n_elements;
1635  Last_el_plus_one_for_assembly[p] = last_el_plus_one;
1636  }
1637 
1638  // Last one needs to incorporate any dangling elements
1639  if (int(n_elements)>=n_proc)
1640  {
1641  Last_el_plus_one_for_assembly[n_proc-1] = n_elements;
1642  }
1643 
1644  // Doc
1645  if (n_proc>1)
1646  {
1647 
1649  {
1650  oomph_info << "Problem is not distributed. Parallel assembly of "
1651  << "Jacobian uses default partitioning: "<< std::endl;
1652  for (int p=0;p<n_proc;p++)
1653  {
1655  {
1656  oomph_info << "Proc " << p << " assembles from element "
1657  << First_el_for_assembly[p] << " to "
1658  << Last_el_plus_one_for_assembly[p]-1 << " \n";
1659  }
1660  else
1661  {
1662  oomph_info << "Proc " << p << " assembles no elements\n";
1663  }
1664  }
1665  }
1666  }
1667  }
1668 
1669  }
1670 
1671 
1672 //=======================================================================
1673 /// Helper function to re-assign the first and last elements to be
1674 /// assembled by each processor during parallel assembly for
1675 /// non-distributed problem.
1676 //=======================================================================
1678  {
1679  // Wait until all processes have completed/timed their assembly
1680  MPI_Barrier(this->communicator_pt()->mpi_comm());
1681 
1682  // Storage for number of processors and current processor
1683  int n_proc=this->communicator_pt()->nproc();
1684  int rank=this->communicator_pt()->my_rank();
1685 
1686  // Don't bother to update if we've got fewer elements than
1687  // processors
1688  unsigned nel=Elemental_assembly_time.size();
1689  if (int(nel)<n_proc)
1690  {
1691  oomph_info << "Not re-computing distribution of elemental assembly\n"
1692  << "because there are fewer elements than processors\n";
1693  return;
1694  }
1695 
1696  // Setup vectors storing the number of element timings to be sent
1697  // and the offset in the final vector
1698  Vector<int> receive_count(n_proc);
1699  Vector<int> displacement(n_proc);
1700  int offset=0;
1701  for (int p=0;p<n_proc;p++)
1702  {
1703  // Default distribution of labour
1704  unsigned el_lo = First_el_for_assembly[p];
1705  unsigned el_hi = Last_el_plus_one_for_assembly[p]-1;
1706 
1707  // Number of timings to be sent and offset from start in
1708  // final vector
1709  receive_count[p]=el_hi-el_lo+1;
1710  displacement[p]=offset;
1711  offset+=el_hi-el_lo+1;
1712  }
1713 
1714  // Make temporary c-style array to avoid over-writing in Gatherv below
1715  double* el_ass_time = new double[nel];
1716  for (unsigned e=0;e<nel;e++)
1717  {
1718  el_ass_time[e]=Elemental_assembly_time[e];
1719  }
1720 
1721  // Gather timings on root processor
1722  unsigned nel_local =Last_el_plus_one_for_assembly[rank]-1
1723  -First_el_for_assembly[rank]+1;
1724  MPI_Gatherv(
1725  &el_ass_time[First_el_for_assembly[rank]],nel_local,MPI_DOUBLE,
1726  &Elemental_assembly_time[0],&receive_count[0],&displacement[0],
1727  MPI_DOUBLE,0,this->communicator_pt()->mpi_comm());
1728  delete[] el_ass_time;
1729 
1730  // Vector of first and last elements for each processor
1731  Vector<Vector <int> > first_and_last_element(n_proc);
1732  for(int p=0;p<n_proc;p++) {first_and_last_element[p].resize(2);}
1733 
1734  // Re-distribute work
1735  if (rank==0)
1736  {
1738  {
1739  oomph_info
1740  << std::endl
1741  << "Re-assigning distribution of element assembly over processors:"
1742  << std::endl;
1743  }
1744 
1745  // Get total assembly time
1746  double total=0.0;
1747  unsigned n_elements=Mesh_pt->nelement();
1748  for (unsigned e=0;e<n_elements;e++)
1749  {
1750  total+=Elemental_assembly_time[e];
1751  }
1752 
1753  // Target load per processor
1754  double target_load=total/double(n_proc);
1755 
1756  // We're on the root processor: Always start with the first element
1757  int proc=0;
1758  first_and_last_element[0][0] = 0;
1759 
1760  // Highest element we can help ourselves to if we want to leave
1761  // at least one element for all subsequent processors
1762  unsigned max_el_avail=n_elements-n_proc;
1763 
1764  // Initialise total work allocated
1765  total=0.0;
1766  for (unsigned e=0;e<n_elements;e++)
1767  {
1768  total+=Elemental_assembly_time[e];
1769 
1770  //Once we have reached the target load or we've used up practically
1771  // all the elements...
1772  if ((total>target_load)||(e==max_el_avail))
1773  {
1774 
1775  // Last element for current processor
1776  first_and_last_element[proc][1]=e;
1777 
1778  //Provided that we are not on the last processor
1779  if (proc<(n_proc-1))
1780  {
1781  // Set first element for next one
1782  first_and_last_element[proc+1][0]=e+1;
1783 
1784  // Move on to the next processor
1785  proc++;
1786  }
1787 
1788  // Can have one more...
1789  max_el_avail++;
1790 
1791  // Re-initialise the time
1792  total=0.0;
1793  } // end of test for "total exceeds target"
1794  }
1795 
1796 
1797  // Last element for last processor
1798  first_and_last_element[n_proc-1][1]=n_elements-1;
1799 
1800 
1801  // The following block should probably be paranoidified away
1802  // but we've screwed the logic up so many times that I feel
1803  // it's safer to keep it...
1804  bool wrong=false;
1805  std::ostringstream error_stream;
1806  for (int p=0;p<n_proc-1;p++)
1807  {
1808  unsigned first_of_current=first_and_last_element[p][0];
1809  unsigned last_of_current=first_and_last_element[p][1];
1810  if (first_of_current>last_of_current)
1811  {
1812  wrong=true;
1813  error_stream << "Error: First/last element of proc " << p << ": "
1814  << first_of_current << " " << last_of_current
1815  << std::endl;
1816  }
1817  unsigned first_of_next=first_and_last_element[p+1][0];
1818  if (first_of_next!=(last_of_current+1))
1819  {
1820  wrong=true;
1821  error_stream << "Error: First element of proc " << p+1 << ": "
1822  << first_of_next << " and last element of proc "
1823  << p << ": " << last_of_current
1824  << std::endl;
1825  }
1826  }
1827  if (wrong)
1828  {
1829  throw OomphLibError(error_stream.str(),
1830  OOMPH_CURRENT_FUNCTION,
1831  OOMPH_EXCEPTION_LOCATION);
1832  }
1833 
1834 
1835  // THIS TIDY UP SHOULD NO LONGER BE REQUIRED AND CAN GO AT SOME POINT
1836 
1837  // //If we haven't got to the end of the processor list then
1838  // //need to shift things about slightly because the processors
1839  // //at the end will be empty.
1840  // //This can occur when you have very fast assembly times and the
1841  // //rounding errors mean that the targets are achieved before all processors
1842  // //have been visited.
1843  // //Happens a lot when you massively oversubscribe the CPUs (which was
1844  // //only ever for testing!)
1845  // if (proc!=n_proc-1)
1846  // {
1847  // oomph_info
1848  // << "First pass did not allocate elements on every processor\n";
1849  // oomph_info <<
1850  // "Moving elements so that each processor has at least one\n";
1851 
1852  // //Work out number of empty processos
1853  // unsigned n_empty_processors = n_proc - proc + 1;
1854 
1855  // //Loop over the processors that do have elements
1856  // //and work out how many we need to steal elements from
1857  // unsigned n_element_on_processors=0;
1858  // do
1859  // {
1860  // //Step down the processors
1861  // --proc;
1862  // //Add the current processor to the number of empty processors
1863  // //because the elements have to be shared between processors
1864  // //including the one(s) on which they are currently stored.
1865  // ++n_empty_processors;
1866  // n_element_on_processors +=
1867  // (first_and_last_element[proc][1] -
1868  // first_and_last_element[proc][0] + 1);
1869  // }
1870  // while(n_element_on_processors < n_empty_processors);
1871 
1872  // //Should now be able to put one element on each processor
1873  // //Start from the end and do so
1874  // unsigned current_element = n_elements-1;
1875  // for(int p=n_proc-1;p>proc;p--)
1876  // {
1877  // first_and_last_element[p][1] = current_element;
1878  // first_and_last_element[p][0] = --current_element;
1879  // }
1880 
1881  // //Now for the last processor we touched, just adjust the final value
1882  // first_and_last_element[proc][1] = current_element;
1883  // }
1884  // //Otherwise just put the rest of the elements on the final
1885  // //processor
1886  // else
1887  // {
1888  // // Last one
1889  // first_and_last_element[n_proc-1][1]=n_elements-1;
1890  // }
1891 
1892 
1893  // END PRESUMED-TO-BE-UNNECESSARY BLOCK...
1894 
1895 
1896 
1897  //Now communicate the information
1898 
1899  //Set local informationt for this (root) processor
1900  First_el_for_assembly[0]= first_and_last_element[0][0];
1901  Last_el_plus_one_for_assembly[0] = first_and_last_element[0][1] + 1;
1902 
1904  {
1905  oomph_info
1906  << "Processor " << 0 << " assembles Jacobians"
1907  << " from elements " << first_and_last_element[0][0] << " to "
1908  << first_and_last_element[0][1] << " "
1909  << std::endl;
1910  }
1911 
1912  //Only now can we send the information to the other processors
1913  for(int p=1;p<n_proc;++p)
1914  {
1915 
1916  MPI_Send(&first_and_last_element[p][0],2,MPI_INT,p,
1917  0,this->communicator_pt()->mpi_comm());
1918 
1919 
1921  {
1922  oomph_info
1923  << "Processor " << p << " assembles Jacobians"
1924  << " from elements " << first_and_last_element[p][0] << " to "
1925  << first_and_last_element[p][1] << " "
1926  << std::endl;
1927  }
1928  }
1929  }
1930  // Receive first and last element from root on non-master processors
1931  else
1932  {
1933  Vector<int> aux(2);
1934  MPI_Status status;
1935  MPI_Recv(&aux[0],2,MPI_INT,0,0,
1936  this->communicator_pt()->mpi_comm(),&status);
1937  First_el_for_assembly[rank]=aux[0];
1938  Last_el_plus_one_for_assembly[rank]=aux[1]+1;
1939  }
1940 
1941  // Wipe all others
1942  for (int p=0;p<n_proc;p++)
1943  {
1944  if (p!=rank)
1945  {
1946  First_el_for_assembly[p]=0;
1948  }
1949  }
1950 
1951  // The equations assembled by this processor may have changed so
1952  // we must resize the sparse assemble with arrays previous allocation
1954  }
1955 
1956 #endif
1957 
1958 //================================================================
1959 /// Assign all equation numbers for problem: Deals with global
1960 /// data (= data that isn't attached to any elements) and then
1961 /// does the equation numbering for the elements. Bool argument
1962 /// can be set to false to ignore assigning local equation numbers
1963 /// (necessary in the parallel implementation of locate_zeta
1964 /// between multiple meshes).
1965 //================================================================
1966  unsigned long Problem::assign_eqn_numbers(const bool& assign_local_eqn_numbers)
1967  {
1968  //Check that the global mesh has been build
1969 #ifdef PARANOID
1970  if(Mesh_pt==0)
1971  {
1972  std::ostringstream error_stream;
1973  error_stream <<
1974  "Global mesh does not exist, so equation numbers cannot be assigned.\n";
1975  //Check for sub meshes
1976  if(nsub_mesh()==0)
1977  {
1978  error_stream << "There aren't even any sub-meshes in the Problem.\n"
1979  << "You can set the global mesh directly by using\n"
1980  << "Problem::mesh_pt() = my_mesh_pt;\n"
1981  << "OR you can use Problem::add_sub_mesh(mesh_pt); "
1982  << "to add a sub mesh.\n";
1983  }
1984  else
1985  {
1986  error_stream << "There are " << nsub_mesh() << " sub-meshes.\n";
1987  }
1988  error_stream <<
1989  "You need to call Problem::build_global_mesh() to create a global mesh\n"
1990  << "from the sub-meshes.\n\n";
1991 
1992  throw OomphLibError(error_stream.str(),
1993  OOMPH_CURRENT_FUNCTION,
1994  OOMPH_EXCEPTION_LOCATION);
1995  }
1996 #endif
1997 
1998  // Number of submeshes
1999  unsigned n_sub_mesh=Sub_mesh_pt.size();
2000 
2001 #ifdef OOMPH_HAS_MPI
2002 
2003  // Storage for number of processors
2004  int n_proc=this->communicator_pt()->nproc();
2005 
2006 
2007  if (n_proc>1)
2008  {
2009  // Force re-analysis of time spent on assembly each
2010  // elemental Jacobian
2012  Elemental_assembly_time.clear();
2013  }
2014  else
2015  {
2017  }
2018 
2019  // Re-distribution of elements over processors during assembly
2020  // must be recomputed
2022  {
2023  // Set default first and last elements for parallel assembly
2024  // of non-distributed problem.
2026  }
2027 
2028 #endif
2029 
2030 
2031  double t_start = 0.0;
2033  {
2034  t_start=TimingHelpers::timer();
2035  }
2036 
2037  // Loop over all elements in the mesh and set up any additional
2038  // dependencies that they may have (e.g. storing the geometric
2039  // Data, i.e. Data that affects an element's shape in elements
2040  // with algebraic node-update functions
2041  unsigned nel=Mesh_pt->nelement();
2042  for (unsigned e=0;e<nel;e++)
2043  {
2045  }
2046 
2047 #ifdef OOMPH_HAS_MPI
2048  // Complete setup of dependencies for external halo elements too
2049  unsigned n_mesh=this->nsub_mesh();
2050  for (unsigned i_mesh=0;i_mesh<n_mesh;i_mesh++)
2051  {
2052  for (int iproc=0;iproc<n_proc;iproc++)
2053  {
2054  unsigned n_ext_halo_el=mesh_pt(i_mesh)->nexternal_halo_element(iproc);
2055  for (unsigned e=0;e<n_ext_halo_el;e++)
2056  {
2057  mesh_pt(i_mesh)->external_halo_element_pt(iproc,e)
2059  }
2060  }
2061  }
2062 #endif
2063 
2064 
2065 
2066 
2067  double t_end = 0.0;
2069  {
2070  t_end = TimingHelpers::timer();
2071  oomph_info
2072  << "Time for complete setup of dependencies in assign_eqn_numbers: "
2073  << t_end-t_start << std::endl;
2074  }
2075 
2076 
2077  // Initialise number of dofs for reserve below
2078  unsigned n_dof=0;
2079 
2080  // Potentially loop over remainder of routine, possible re-visiting all those
2081  // parts that must be redone, following the removal of duplicate
2082  // external halo data.
2083  for (unsigned loop_count=0;loop_count<2;loop_count++)
2084  {
2085  //(Re)-set the dof pointer to zero length because entries are
2086  //pushed back onto it -- if it's not reset here then we get into
2087  //trouble during mesh refinement when we reassign all dofs
2088  Dof_pt.resize(0);
2089 
2090  // Reserve from previous allocation if we're going around again
2091  Dof_pt.reserve(n_dof);
2092 
2093  //Reset the equation number
2094  unsigned long equation_number=0;
2095 
2096  //Now set equation numbers for the global Data
2097  unsigned Nglobal_data = nglobal_data();
2098  for(unsigned i=0;i<Nglobal_data;i++)
2099  {Global_data_pt[i]->assign_eqn_numbers(equation_number,Dof_pt);}
2100 
2102  {
2103  t_start = TimingHelpers::timer();
2104  }
2105 
2106  //Call assign equation numbers on the global mesh
2108 
2109  // Deal with the spine meshes additional numbering
2110  //If there is only one mesh
2111  if(n_sub_mesh==0)
2112  {
2113  if(SpineMesh* const spine_mesh_pt = dynamic_cast<SpineMesh*>(Mesh_pt))
2114  {
2115  n_dof = spine_mesh_pt->assign_global_spine_eqn_numbers(Dof_pt);
2116  }
2117  }
2118  //Otherwise loop over the sub meshes
2119  else
2120  {
2121  //Assign global equation numbers first
2122  for(unsigned i=0;i<n_sub_mesh;i++)
2123  {
2124  if(SpineMesh* const spine_mesh_pt =
2125  dynamic_cast<SpineMesh*>(Sub_mesh_pt[i]))
2126  {
2127  n_dof = spine_mesh_pt->assign_global_spine_eqn_numbers(Dof_pt);
2128  }
2129  }
2130  }
2131 
2133  {
2134  t_end = TimingHelpers::timer();
2135  oomph_info
2136  << "Time for assign_global_eqn_numbers in assign_eqn_numbers: "
2137  << t_end-t_start << std::endl;
2138  t_start = TimingHelpers::timer();
2139  }
2140 
2141 
2142 #ifdef OOMPH_HAS_MPI
2143 
2144  // reset previous allocation
2146 
2147  // Only synchronise if the problem has actually been
2148  // distributed.
2150  {
2151  // Synchronise the equation numbers and return the total
2152  // number of degrees of freedom in the overall problem
2153  // Do not assign local equation numbers -- we're doing this
2154  // below.
2155  n_dof=synchronise_eqn_numbers(false);
2156  }
2157  // ..else just setup the Dof_distribution_pt
2158  // NOTE: this is setup by synchronise_eqn_numbers(...)
2159  // if Problem_has_been_distributed
2160  else
2161 #endif
2162  {
2164  }
2165 
2167  {
2168  t_end = TimingHelpers::timer();
2169  oomph_info
2170  << "Time for Problem::synchronise_eqn_numbers in "
2171  << "Problem::assign_eqn_numbers: "
2172  << t_end-t_start << std::endl;
2173  }
2174 
2175 
2176 #ifdef OOMPH_HAS_MPI
2177 
2178 
2179  // Now remove duplicate data in external halo elements
2181  {
2182 
2184  {
2185  t_start = TimingHelpers::timer();
2186  }
2187 
2188  // Monitor if we've actually changed anything
2189  bool actually_removed_some_data=false;
2190 
2191  // Only do it once!
2192  if (loop_count==0)
2193  {
2194  if (n_sub_mesh==0)
2195  {
2196  remove_duplicate_data(Mesh_pt,actually_removed_some_data);
2197  }
2198  else
2199  {
2200  for (unsigned i=0;i<n_sub_mesh;i++)
2201  {
2202  bool tmp_actually_removed_some_data=false;
2204  tmp_actually_removed_some_data);
2205  if (tmp_actually_removed_some_data) actually_removed_some_data=true;
2206  }
2207  }
2208  }
2209 
2210 
2212  {
2213  t_end = TimingHelpers::timer();
2214  std::stringstream tmp;
2215  tmp << "Time for calls to Problem::remove_duplicate_data in "
2216  << "Problem::assign_eqn_numbers: "
2217  << t_end-t_start << " ; have ";
2218  if (!actually_removed_some_data)
2219  {
2220  tmp << " not ";
2221  }
2222  tmp << " removed some/any data.\n";
2223  oomph_info << tmp.str();
2224  t_start=TimingHelpers::timer();
2225  }
2226 
2227  // Break out of the loop if we haven't done anything here.
2228  unsigned status=0;
2229  if (actually_removed_some_data) status=1;
2230 
2231  // Allreduce to check if anyone has removed any data
2232  unsigned overall_status=0;
2233  MPI_Allreduce(&status,&overall_status,1,
2234  MPI_UNSIGNED,MPI_MAX,this->communicator_pt()->mpi_comm());
2235 
2236 
2238  {
2239  t_end = TimingHelpers::timer();
2240  std::stringstream tmp;
2241  tmp << "Time for MPI_Allreduce after Problem::remove_duplicate_data in "
2242  << "Problem::assign_eqn_numbers: "
2243  << t_end-t_start << std::endl;
2244  oomph_info << tmp.str();
2245  t_start=TimingHelpers::timer();
2246  }
2247 
2248  // Bail out if we haven't done anything here
2249  if (overall_status!=1)
2250  {
2251  break;
2252  }
2253 
2254  // Big tidy up: Remove null pointers from halo/haloed node storage
2255  // for all meshes (this involves comms and therefore must be
2256  // performed outside loop over meshes so the all-to-all is only
2257  // done once)
2259 
2260  // Time it...
2262  {
2263  double t_end = TimingHelpers::timer();
2264  oomph_info
2265  << "Total time for "
2266  << "Problem::remove_null_pointers_from_external_halo_node_storage(): "
2267  << t_end-t_start << std::endl;
2268  }
2269 
2270  }
2271  else
2272  {
2273  // Problem not distributed; no need for another loop
2274  break;
2275  }
2276 
2277 #else
2278 
2279  // Serial run: Again no need for a second loop
2280  break;
2281 
2282 #endif
2283 
2284  } // end of loop over fcts that need to be re-executed if
2285  // we've removed duplicate external data
2286 
2287 
2288  // Resize the sparse assemble with arrays previous allocation
2290 
2291 
2293  {
2294  t_start = TimingHelpers::timer();
2295  }
2296 
2297  // Finally assign local equations
2298  if (assign_local_eqn_numbers)
2299  {
2300  if (n_sub_mesh==0)
2301  {
2303  }
2304  else
2305  {
2306  for (unsigned i=0;i<n_sub_mesh;i++)
2307  {
2308  Sub_mesh_pt[i]->
2309  assign_local_eqn_numbers(Store_local_dof_pt_in_elements);
2310  }
2311  }
2312  }
2313 
2315  {
2316  t_end = TimingHelpers::timer();
2317  oomph_info
2318  << "Total time for all Mesh::assign_local_eqn_numbers in "
2319  << "Problem::assign_eqn_numbers: "
2320  << t_end-t_start << std::endl;
2321  }
2322 
2323 
2324  // and return the total number of DOFs
2325  return n_dof;
2326 
2327  }
2328 //================================================================
2329 /// \short Function to describe the dofs in terms of the global
2330 /// equation number, i.e. what type of value (nodal value of
2331 /// a Node; value in a Data object; value of internal Data in an
2332 /// element; etc) is the unknown with a certain global equation number.
2333 /// Output stream defaults to oomph_info.
2334 //================================================================
2335  void Problem::describe_dofs(std::ostream& out) const
2336  {
2337  //Check that the global mesh has been build
2338 #ifdef PARANOID
2339  if(Mesh_pt==0)
2340  {
2341  std::ostringstream error_stream;
2342  error_stream <<
2343  "Global mesh does not exist, so equation numbers cannot be found.\n";
2344  //Check for sub meshes
2345  if(nsub_mesh()==0)
2346  {
2347  error_stream << "There aren't even any sub-meshes in the Problem.\n"
2348  << "You can set the global mesh directly by using\n"
2349  << "Problem::mesh_pt() = my_mesh_pt;\n"
2350  << "OR you can use Problem::add_sub_mesh(mesh_pt); "
2351  << "to add a sub mesh.\n";
2352  }
2353  else
2354  {
2355  error_stream << "There are " << nsub_mesh() << " sub-meshes.\n";
2356  }
2357  error_stream <<
2358  "You need to call Problem::build_global_mesh() to create a global mesh\n"
2359  << "from the sub-meshes.\n\n";
2360 
2361  throw OomphLibError(error_stream.str(),
2362  OOMPH_CURRENT_FUNCTION,
2363  OOMPH_EXCEPTION_LOCATION);
2364  }
2365 #endif
2366 
2367  out << "Although this program will describe the degrees of freedom in the \n"
2368  << "problem, it will do so using the typedef for the elements. This is \n"
2369  << "not neccesarily human readable, but there is a solution.\n"
2370  << "Pipe your program's output through c++filt, with the argument -t.\n"
2371  << "e.g. \"./two_d_multi_poisson | c++filt -t > ReadableOutput.txt\".\n "
2372  << "(Disregarding the quotes)\n\n\n";
2373 
2374  out << "Classifying Global Equation Numbers" << std::endl;
2375  out << std::string(80,'-') << std::endl;
2376 
2377  // Number of submeshes
2378  unsigned n_sub_mesh=Sub_mesh_pt.size();
2379 
2380  // Classify Global dofs
2381  unsigned Nglobal_data = nglobal_data();
2382  for(unsigned i=0;i<Nglobal_data;i++)
2383  {
2384  std::stringstream conversion;
2385  conversion <<" in Global Data "<<i<<".";
2386  std::string in(conversion.str());
2387  Global_data_pt[i]->describe_dofs(out,in);
2388  }
2389 
2390  // Put string in limiting scope.
2391  {
2392  // Descend into assignment for mesh.
2393  std::string in(" in Problem's Only Mesh.");
2394  Mesh_pt->describe_dofs(out,in);
2395  }
2396 
2397  // Deal with the spine meshes additional numbering:
2398  // If there is only one mesh:
2399  if(n_sub_mesh==0)
2400  {
2401  if(SpineMesh* const spine_mesh_pt = dynamic_cast<SpineMesh*>(Mesh_pt))
2402  {
2403  std::string in(" in Problem's Only SpineMesh.");
2404  spine_mesh_pt->describe_spine_dofs(out,in);
2405  }
2406  }
2407  //Otherwise loop over the sub meshes
2408  else
2409  {
2410  //Assign global equation numbers first
2411  for(unsigned i=0;i<n_sub_mesh;i++)
2412  {
2413  if(SpineMesh* const spine_mesh_pt =
2414  dynamic_cast<SpineMesh*>(Sub_mesh_pt[i]))
2415  {
2416  std::stringstream conversion;
2417  conversion <<" in Sub-SpineMesh "<<i<<".";
2418  std::string in(conversion.str());
2419  spine_mesh_pt->describe_spine_dofs(out,in);
2420  }// end if.
2421  }// end for.
2422  }// end else.
2423 
2424 
2425  out << std::string(80,'\\') << std::endl;
2426  out << std::string(80,'\\') << std::endl;
2427  out << std::string(80,'\\') << std::endl;
2428  out << "Classifying global eqn numbers in terms of elements." << std::endl;
2429  out << std::string(80,'-') << std::endl;
2430  out << "Eqns | Source" << std::endl;
2431  out << std::string(80,'-') << std::endl;
2432 
2433  if (n_sub_mesh==0)
2434  {
2435  std::string in(" in Problem's Only Mesh.");
2436  Mesh_pt->describe_local_dofs(out,in);
2437  }
2438  else
2439  {
2440  for (unsigned i=0;i<n_sub_mesh;i++)
2441  {
2442  std::stringstream conversion;
2443  conversion <<" in Sub-Mesh "<<i<<".";
2444  std::string in(conversion.str());
2445  Sub_mesh_pt[i]->describe_local_dofs(out,in);
2446  }// End for
2447  }// End else
2448  } // End problem::describe_dofs(...)
2449 
2450 
2451 //================================================================
2452 /// Get the vector of dofs, i.e. a vector containing the current
2453 /// values of all unknowns.
2454 //================================================================
2456  {
2457  //Find number of dofs
2458  const unsigned long n_dof = ndof();
2459 
2460  //Resize the vector
2461  dofs.build(Dof_distribution_pt,0.0);
2462 
2463  //Copy dofs into vector
2464  for(unsigned long l=0;l<n_dof;l++)
2465  {
2466  dofs[l] = *Dof_pt[l];
2467  }
2468  }
2469 
2470  /// Get history values of dofs in a double vector.
2471  void Problem::get_dofs(const unsigned& t, DoubleVector& dofs) const
2472  {
2473 #ifdef PARANOID
2474  if(distributed())
2475  {
2476  throw OomphLibError("Not designed for distributed problems",
2477  OOMPH_EXCEPTION_LOCATION,
2478  OOMPH_CURRENT_FUNCTION);
2479  // might work, not sure
2480  }
2481 #endif
2482 
2483  // Resize the vector
2484  dofs.build(Dof_distribution_pt, 0.0);
2485 
2486  // First deal with global data
2487  unsigned Nglobal_data = nglobal_data();
2488  for(unsigned i=0; i<Nglobal_data; i++)
2489  {
2490  for(unsigned j=0, nj=Global_data_pt[i]->nvalue(); j<nj; j++)
2491  {
2492  // For each data get the equation number and copy out the value.
2493  int eqn_number = Global_data_pt[i]->eqn_number(j);
2494  if(eqn_number >= 0)
2495  {
2496  dofs[eqn_number] = Global_data_pt[i]->value(t, j);
2497  }
2498  }
2499  }
2500 
2501  // Next element internal data
2502  for(unsigned i=0, ni=mesh_pt()->nelement(); i<ni; i++)
2503  {
2504 
2505  GeneralisedElement* ele_pt = mesh_pt()->element_pt(i);
2506  for(unsigned j=0, nj=ele_pt->ninternal_data(); j<nj; j++)
2507  {
2508 
2509  Data* d_pt = ele_pt->internal_data_pt(j);
2510  for(unsigned k=0, nk=d_pt->nvalue(); k<nk; k++)
2511  {
2512 
2513  int eqn_number = d_pt->eqn_number(k);
2514  if(eqn_number >= 0)
2515  {
2516  dofs[eqn_number] = d_pt->value(t, k);
2517  }
2518  }
2519  }
2520  }
2521 
2522  // Now the nodes
2523  for(unsigned i=0, ni=mesh_pt()->nnode(); i<ni; i++)
2524  {
2525  Node* node_pt = mesh_pt()->node_pt(i);
2526  for(unsigned j=0, nj=node_pt->nvalue(); j<nj; j++)
2527  {
2528  // For each node get the equation number and copy out the value.
2529  int eqn_number = node_pt->eqn_number(j);
2530  if(eqn_number >= 0)
2531  {
2532  dofs[eqn_number] = node_pt->value(t, j);
2533 
2534  }
2535  }
2536  }
2537  }
2538 
2539 
2540 #ifdef OOMPH_HAS_MPI
2541 
2542 //=======================================================================
2543 /// Private helper function to remove repeated data
2544 /// in external haloed elements associated with specified mesh.
2545 /// Bool is true if some data was removed -- this usually requires
2546 /// re-running through certain parts of the equation numbering procedure.
2547 //======================================================================
2549  bool& actually_removed_some_data)
2550  {
2551 
2552 // // // Taken out again by MH -- clutters up output
2553 // // Doc timings if required
2554 // double t_start=0.0;
2555 // if (Global_timings::Doc_comprehensive_timings)
2556 // {
2557 // t_start=TimingHelpers::timer();
2558 // }
2559 
2560  int n_proc=this->communicator_pt()->nproc();
2561  int my_rank=this->communicator_pt()->my_rank();
2562 
2563  // Initialise
2564  actually_removed_some_data=false;
2565 
2566  // Each individual container of external halo nodes has unique
2567  // nodes/equation numbers, but there may be some duplication between
2568  // two or more different containers; the following code checks for this
2569  // and removes the duplication by overwriting any data point with an already
2570  // existing eqn number with the original data point which had the eqn no.
2571 
2572  // // Storage for existing nodes, enumerated by first non-negative
2573  // // global equation number
2574  // unsigned n_dof=ndof();
2575 
2576  // Note: This used to be
2577  // Vector<Node*> global_node_pt(n_dof,0);
2578  // but this is a total killer! Memory allocation is extremely
2579  // costly and only relatively few entries are used so use
2580  // map:
2581  std::map<unsigned,Node*> global_node_pt;
2582 
2583  // Only do each retained node once
2584  std::map<Node*,bool> node_done;
2585 
2586  // Loop over existing "normal" elements in mesh
2587  unsigned n_element=mesh_pt->nelement();
2588  for (unsigned e=0;e<n_element;e++)
2589  {
2590  FiniteElement* el_pt = dynamic_cast<FiniteElement*>(
2591  mesh_pt->element_pt(e));
2592  if (el_pt!=0)
2593  {
2594  // Loop over nodes
2595  unsigned n_node=el_pt->nnode();
2596  for (unsigned j=0;j<n_node;j++)
2597  {
2598  Node* nod_pt=el_pt->node_pt(j);
2599 
2600  // Have we already done the node?
2601  if (!node_done[nod_pt])
2602  {
2603  node_done[nod_pt]=true;
2604 
2605  // Loop over values stored at node (if any) to find
2606  // the first non-negative eqn number
2607  unsigned first_non_negative_eqn_number_plus_one=0;
2608  unsigned n_val=nod_pt->nvalue();
2609  for (unsigned i_val=0;i_val<n_val;i_val++)
2610  {
2611  int eqn_no=nod_pt->eqn_number(i_val);
2612  if (eqn_no>=0)
2613  {
2614  first_non_negative_eqn_number_plus_one=eqn_no+1;
2615  break;
2616  }
2617  }
2618 
2619  // If we haven't found a non-negative eqn number check
2620  // eqn numbers associated with solid data (if any)
2621  if (first_non_negative_eqn_number_plus_one==0)
2622  {
2623  // Is it a solid node?
2624  SolidNode* solid_nod_pt=dynamic_cast<SolidNode*>(nod_pt);
2625  if (solid_nod_pt!=0)
2626  {
2627  // Loop over values stored at node (if any) to find
2628  // the first non-negative eqn number
2629  unsigned n_val=solid_nod_pt->variable_position_pt()->nvalue();
2630  for (unsigned i_val=0;i_val<n_val;i_val++)
2631  {
2632  int eqn_no=solid_nod_pt->
2633  variable_position_pt()->eqn_number(i_val);
2634  if (eqn_no>=0)
2635  {
2636  first_non_negative_eqn_number_plus_one=eqn_no+1;
2637  break;
2638  }
2639  }
2640  }
2641  }
2642 
2643  // Associate node with first non negative global eqn number
2644  if (first_non_negative_eqn_number_plus_one>0)
2645  {
2646  global_node_pt[first_non_negative_eqn_number_plus_one-1]=nod_pt;
2647  }
2648 
2649 
2650  // Take into account master nodes too
2651  if (dynamic_cast<RefineableElement*>(el_pt)!=0)
2652  {
2653  int n_cont_int_values=dynamic_cast<RefineableElement*>
2654  (el_pt)->ncont_interpolated_values();
2655  for (int i_cont=-1;i_cont<n_cont_int_values;i_cont++)
2656  {
2657  if (nod_pt->is_hanging(i_cont))
2658  {
2659  HangInfo* hang_pt=nod_pt->hanging_pt(i_cont);
2660  unsigned n_master=hang_pt->nmaster();
2661  for (unsigned m=0;m<n_master;m++)
2662  {
2663  Node* master_nod_pt=hang_pt->master_node_pt(m);
2664  if (!node_done[master_nod_pt])
2665  {
2666  node_done[master_nod_pt]=true;
2667 
2668  // Loop over values stored at node (if any) to find
2669  // the first non-negative eqn number
2670  unsigned first_non_negative_eqn_number_plus_one=0;
2671  unsigned n_val=master_nod_pt->nvalue();
2672  for (unsigned i_val=0;i_val<n_val;i_val++)
2673  {
2674  int eqn_no=master_nod_pt->eqn_number(i_val);
2675  if (eqn_no>=0)
2676  {
2677  first_non_negative_eqn_number_plus_one=eqn_no+1;
2678  break;
2679  }
2680  }
2681 
2682  // If we haven't found a non-negative eqn number check
2683  // eqn numbers associated with solid data (if any)
2684  if (first_non_negative_eqn_number_plus_one==0)
2685  {
2686  // If this master is a SolidNode then add its extra
2687  // eqn numbers
2688  SolidNode* master_solid_nod_pt=dynamic_cast<SolidNode*>
2689  (master_nod_pt);
2690  if (master_solid_nod_pt!=0)
2691  {
2692  // Loop over values stored at node (if any) to find
2693  // the first non-negative eqn number
2694  unsigned n_val=master_solid_nod_pt->
2695  variable_position_pt()->nvalue();
2696  for (unsigned i_val=0;i_val<n_val;i_val++)
2697  {
2698  int eqn_no=master_solid_nod_pt->
2699  variable_position_pt()->eqn_number(i_val);
2700  if (eqn_no>=0)
2701  {
2702  first_non_negative_eqn_number_plus_one=eqn_no+1;
2703  break;
2704  }
2705  }
2706  }
2707  }
2708  // Associate node with first non negative global
2709  // eqn number
2710  if (first_non_negative_eqn_number_plus_one>0)
2711  {
2712  global_node_pt[first_non_negative_eqn_number_plus_one-1]
2713  =master_nod_pt;
2714  }
2715 
2716  } // End of not-yet-done hang node
2717  }
2718  }
2719  }
2720  }
2721  } // endif for node already done
2722  }// End of loop over nodes
2723  } //End of FiniteElement
2724 
2725  // Internal data equation numbers do not need to be added since
2726  // internal data cannot be shared between distinct elements, so
2727  // internal data on locally-stored elements can never be halo.
2728  }
2729 
2730  // Set to record duplicate nodes scheduled to be killed
2731  std::set<Node*> killed_nodes;
2732 
2733  // Now loop over the other processors from highest to lowest
2734  // (i.e. if there is a duplicate between these containers
2735  // then this will use the node on the highest numbered processor)
2736  for (int iproc=n_proc-1;iproc>=0;iproc--)
2737  {
2738  // Don't have external halo elements with yourself!
2739  if (iproc!=my_rank)
2740  {
2741  // Loop over external halo elements with iproc
2742  // to remove the duplicates
2743  unsigned n_element=mesh_pt->nexternal_halo_element(iproc);
2744  for (unsigned e_ext=0;e_ext<n_element;e_ext++)
2745  {
2746  FiniteElement* finite_ext_el_pt =
2747  dynamic_cast<FiniteElement*>(mesh_pt->
2748  external_halo_element_pt(iproc,e_ext));
2749  if(finite_ext_el_pt!=0)
2750  {
2751  // Loop over nodes
2752  unsigned n_node=finite_ext_el_pt->nnode();
2753  for (unsigned j=0;j<n_node;j++)
2754  {
2755  Node* nod_pt=finite_ext_el_pt->node_pt(j);
2756 
2757  // Loop over values stored at node (if any) to find
2758  // the first non-negative eqn number
2759  unsigned first_non_negative_eqn_number_plus_one=0;
2760  unsigned n_val=nod_pt->nvalue();
2761  for (unsigned i_val=0;i_val<n_val;i_val++)
2762  {
2763  int eqn_no=nod_pt->eqn_number(i_val);
2764  if (eqn_no>=0)
2765  {
2766  first_non_negative_eqn_number_plus_one=eqn_no+1;
2767  break;
2768  }
2769  }
2770 
2771  // If we haven't found a non-negative eqn number check
2772  // eqn numbers associated with solid data (if any)
2773  if (first_non_negative_eqn_number_plus_one==0)
2774  {
2775  // Is it a solid node?
2776  SolidNode* solid_nod_pt=dynamic_cast<SolidNode*>(nod_pt);
2777  if (solid_nod_pt!=0)
2778  {
2779  // Loop over values stored at node (if any) to find
2780  // the first non-negative eqn number
2781  unsigned n_val=solid_nod_pt->variable_position_pt()->nvalue();
2782  for (unsigned i_val=0;i_val<n_val;i_val++)
2783  {
2784  int eqn_no=solid_nod_pt->
2785  variable_position_pt()->eqn_number(i_val);
2786  if (eqn_no>=0)
2787  {
2788  first_non_negative_eqn_number_plus_one=eqn_no+1;
2789  break;
2790  }
2791  }
2792  }
2793  }
2794 
2795  // Identified which node we're dealing with via first non-negative
2796  // global eqn number (if there is none, everything is pinned
2797  // and we don't give a damn...)
2798  if (first_non_negative_eqn_number_plus_one>0)
2799  {
2800  Node* existing_node_pt=
2801  global_node_pt[first_non_negative_eqn_number_plus_one-1];
2802 
2803  // Does this node already exist?
2804  if (existing_node_pt!=0)
2805  {
2806  // Record that we're about to cull one
2807  actually_removed_some_data=true;
2808 
2809  // It's a duplicate, so store the duplicated one for
2810  // later killing...
2811  Node* duplicated_node_pt=nod_pt;
2812  if (!node_done[duplicated_node_pt])
2813  {
2814 
2815  // Remove node from all boundaries
2816  std::set<unsigned>* boundaries_pt;
2817  duplicated_node_pt->
2818  get_boundaries_pt(boundaries_pt);
2819  if (boundaries_pt!=0)
2820  {
2821  Vector<unsigned> bound;
2822  unsigned nb=(*boundaries_pt).size();
2823  bound.reserve(nb);
2824  for (std::set<unsigned>::iterator it=
2825  (*boundaries_pt).begin(); it!=
2826  (*boundaries_pt).end(); it++)
2827  {
2828  bound.push_back((*it));
2829  }
2830  for (unsigned i=0;i<nb;i++)
2831  {
2832  mesh_pt->remove_boundary_node(bound[i],
2833  duplicated_node_pt);
2834  }
2835  }
2836 
2837  // Get ready to kill it
2838  killed_nodes.insert(duplicated_node_pt);
2839  unsigned i_proc=unsigned(iproc);
2840  mesh_pt->null_external_halo_node(i_proc,
2841  duplicated_node_pt);
2842  }
2843 
2844 
2845  // Note: For now we're leaving the "dangling" (no longer
2846  // accessed masters where they are; they get cleaned
2847  // up next time we delete all the external storage
2848  // for the meshes so it's a temporary "leak" only...
2849  // At some point we should probably delete them properly too
2850 
2851 #ifdef PARANOID
2852 
2853  // Check that hang status of exiting and replacement node
2854  // matches
2855  if (dynamic_cast<RefineableElement*>(finite_ext_el_pt)!=0)
2856  {
2857  int n_cont_inter_values=dynamic_cast<RefineableElement*>
2858  (finite_ext_el_pt)->ncont_interpolated_values();
2859  for (int i_cont=-1;i_cont<n_cont_inter_values;i_cont++)
2860  {
2861  unsigned n_master_orig=0;
2862  if (finite_ext_el_pt->node_pt(j)->is_hanging(i_cont))
2863  {
2864  n_master_orig=finite_ext_el_pt->node_pt(j)->
2865  hanging_pt(i_cont)->nmaster();
2866 
2867  // Temporary leak: Resolve like this:
2868  // loop over all external halo nodes and identify the
2869  // the ones that are still reached by any of the external
2870  // elements. Kill the dangling ones.
2871  }
2872  unsigned n_master_replace=0;
2873  if (existing_node_pt->is_hanging(i_cont))
2874  {
2875  n_master_replace=existing_node_pt->
2876  hanging_pt(i_cont)->nmaster();
2877  }
2878 
2879  if (n_master_orig!=n_master_replace)
2880  {
2881  std::ostringstream error_stream;
2882  error_stream << "Number of master nodes for node to be replaced, "
2883  << n_master_orig << ", doesn't match"
2884  << "those of replacement node, "
2885  << n_master_replace << " for i_cont="
2886  << i_cont << std::endl;
2887  {
2888  error_stream << "Nodal coordinates of replacement node:";
2889  unsigned ndim=existing_node_pt->ndim();
2890  for (unsigned i=0;i<ndim;i++)
2891  {
2892  error_stream << existing_node_pt->x(i) << " ";
2893  }
2894  error_stream << "\n";
2895  error_stream << "The coordinates of its "
2896  << n_master_replace << " master nodes are: \n";
2897  for (unsigned k=0;k<n_master_replace;k++)
2898  {
2899  Node* master_nod_pt=existing_node_pt->
2900  hanging_pt(i_cont)->master_node_pt(k);
2901  unsigned ndim=master_nod_pt->ndim();
2902  for (unsigned i=0;i<ndim;i++)
2903  {
2904  error_stream << master_nod_pt->x(i) << " ";
2905  }
2906  error_stream << "\n";
2907  }
2908  }
2909 
2910  {
2911  error_stream << "Nodal coordinates of node to be replaced:";
2912  unsigned ndim=finite_ext_el_pt->node_pt(j)->ndim();
2913  for (unsigned i=0;i<ndim;i++)
2914  {
2915  error_stream << finite_ext_el_pt->node_pt(j)->x(i) << " ";
2916  }
2917  error_stream << "\n";
2918  error_stream << "The coordinates of its "
2919  << n_master_orig << " master nodes are: \n";
2920  for (unsigned k=0;k<n_master_orig;k++)
2921  {
2922  Node* master_nod_pt=finite_ext_el_pt->node_pt(j)->
2923  hanging_pt(i_cont)->master_node_pt(k);
2924  unsigned ndim=master_nod_pt->ndim();
2925  for (unsigned i=0;i<ndim;i++)
2926  {
2927  error_stream << master_nod_pt->x(i) << " ";
2928  }
2929  error_stream << "\n";
2930  }
2931  }
2932 
2933 
2934  throw OomphLibError(error_stream.str(),
2935  OOMPH_CURRENT_FUNCTION,
2936  OOMPH_EXCEPTION_LOCATION);
2937  }
2938  }
2939  }
2940 #endif
2941  // ...and point to the existing one
2942  finite_ext_el_pt->node_pt(j)=existing_node_pt;
2943  }
2944  // If it doesn't add it to the list of existing ones
2945  else
2946  {
2947  global_node_pt[first_non_negative_eqn_number_plus_one-1]=
2948  nod_pt;
2949  node_done[nod_pt]=true;
2950  }
2951  }
2952 
2953 
2954  // Do the same for any master nodes of that (possibly replaced) node
2955  if (dynamic_cast<RefineableElement*>(finite_ext_el_pt)!=0)
2956  {
2957  int n_cont_inter_values=dynamic_cast<RefineableElement*>
2958  (finite_ext_el_pt)->ncont_interpolated_values();
2959  for (int i_cont=-1;i_cont<n_cont_inter_values;i_cont++)
2960  {
2961  if (finite_ext_el_pt->node_pt(j)->is_hanging(i_cont))
2962  {
2963  HangInfo* hang_pt=finite_ext_el_pt->node_pt(j)->hanging_pt(i_cont);
2964  unsigned n_master=hang_pt->nmaster();
2965  for (unsigned m=0;m<n_master;m++)
2966  {
2967  Node* master_nod_pt=hang_pt->master_node_pt(m);
2968  unsigned n_val=master_nod_pt->nvalue();
2969  unsigned first_non_negative_eqn_number_plus_one=0;
2970  for (unsigned i_val=0;i_val<n_val;i_val++)
2971  {
2972  int eqn_no=master_nod_pt->eqn_number(i_val);
2973  if (eqn_no>=0)
2974  {
2975  first_non_negative_eqn_number_plus_one=eqn_no+1;
2976  break;
2977  }
2978  }
2979 
2980  // If we haven't found a non-negative eqn number check
2981  // eqn numbers associated with solid data (if any)
2982  if (first_non_negative_eqn_number_plus_one==0)
2983  {
2984  SolidNode* solid_master_nod_pt=dynamic_cast<SolidNode*>
2985  (master_nod_pt);
2986  if (solid_master_nod_pt!=0)
2987  {
2988  // Loop over values stored at node (if any) to find
2989  // the first non-negative eqn number
2990  unsigned n_val=solid_master_nod_pt->
2991  variable_position_pt()->nvalue();
2992  for (unsigned i_val=0;i_val<n_val;i_val++)
2993  {
2994  int eqn_no=solid_master_nod_pt->
2995  variable_position_pt()->eqn_number(i_val);
2996  if (eqn_no>=0)
2997  {
2998  first_non_negative_eqn_number_plus_one=eqn_no+1;
2999  break;
3000  }
3001  }
3002  }
3003  }
3004 
3005  // Identified which node we're dealing with via
3006  // first non-negative global eqn number (if there
3007  // is none, everything is pinned and we don't give a
3008  // damn...)
3009  if (first_non_negative_eqn_number_plus_one>0)
3010  {
3011  Node* existing_node_pt=
3012  global_node_pt[
3013  first_non_negative_eqn_number_plus_one-1];
3014 
3015  // Does this node already exist?
3016  if (existing_node_pt!=0)
3017  {
3018  // Record that we're about to cull one
3019  actually_removed_some_data=true;
3020 
3021  // It's a duplicate, so store the duplicated one for
3022  // later killing...
3023  Node* duplicated_node_pt=master_nod_pt;
3024 
3025  if (!node_done[duplicated_node_pt])
3026  {
3027  // Remove node from all boundaries
3028  std::set<unsigned>* boundaries_pt;
3029  duplicated_node_pt->
3030  get_boundaries_pt(boundaries_pt);
3031  if (boundaries_pt!=0)
3032  {
3033  for (std::set<unsigned>::iterator it=
3034  (*boundaries_pt).begin(); it!=
3035  (*boundaries_pt).end(); it++)
3036  {
3037  mesh_pt->remove_boundary_node(
3038  (*it),duplicated_node_pt);
3039  }
3040  }
3041 
3042  killed_nodes.insert(duplicated_node_pt);
3043  unsigned i_proc=unsigned(iproc);
3044  mesh_pt->null_external_halo_node(i_proc,
3045  duplicated_node_pt);
3046  }
3047 
3048  // Weight of the original node
3049  double m_weight=hang_pt->master_weight(m);
3050 
3051 
3052 #ifdef PARANOID
3053  // Sanity check: setting replacement master
3054  // node for non-hanging node? Sign of really
3055  // f***ed up code.
3056  Node* tmp_nod_pt=finite_ext_el_pt->node_pt(j);
3057  if (!tmp_nod_pt->is_hanging(i_cont))
3058  {
3059  std::ostringstream error_stream;
3060  error_stream
3061  << "About to re-set master for i_cont= "
3062  << i_cont << " for external node (with proc "
3063  << iproc << " )"
3064  << tmp_nod_pt << " at ";
3065  unsigned n=tmp_nod_pt->ndim();
3066  for (unsigned jj=0;jj<n;jj++)
3067  {
3068  error_stream << tmp_nod_pt->x(jj) << " ";
3069  }
3070  error_stream
3071  << " which is not hanging --> About to die!"
3072  << "Outputting offending element into oomph-info "
3073  << "stream. \n\n";
3074  oomph_info << "\n\n";
3075  finite_ext_el_pt->output(*(oomph_info.stream_pt()));
3076  oomph_info << "\n\n";
3077  oomph_info.stream_pt()->flush();
3078  throw OomphLibError(
3079  error_stream.str(),
3080  OOMPH_CURRENT_FUNCTION,
3081  OOMPH_EXCEPTION_LOCATION);
3082  }
3083 #endif
3084 
3085 
3086  // And re-set master
3087  finite_ext_el_pt->node_pt(j)->
3088  hanging_pt(i_cont)->
3089  set_master_node_pt(m,existing_node_pt,
3090  m_weight);
3091  }
3092  // If it doesn't, add it to the list of existing ones
3093  else
3094  {
3095  global_node_pt[
3096  first_non_negative_eqn_number_plus_one-1]=
3097  master_nod_pt;
3098  node_done[master_nod_pt]=true;
3099  }
3100  }
3101  } // End of loop over master nodes
3102  } // end of hanging
3103  } // end of loop over continously interpolated variables
3104  } // end refineable element (with potentially hanging node
3105 
3106  } // end loop over nodes on external halo elements
3107 
3108  } //End of check for finite element
3109 
3110  } // end loop over external halo elements
3111  }
3112  } // end loop over processors
3113 
3114 
3115  // Now kill all the deleted nodes
3116  for (std::set<Node*>::iterator it=killed_nodes.begin();
3117  it!=killed_nodes.end();it++)
3118  {
3119  delete (*it);
3120  }
3121 
3122 
3123 // oomph_info << "Number of nonzero entries in global_node_pt: "
3124 // << global_node_pt.size() << std::endl;
3125 
3126 // // Time it...
3127 // // Taken out again by MH -- clutters up output
3128 // if (Global_timings::Doc_comprehensive_timings)
3129 // {
3130 // double t_end = TimingHelpers::timer();
3131 // oomph_info
3132 // << "Total time for Problem::remove_duplicate_data: "
3133 // << t_end-t_start << std::endl;
3134 // }
3135 
3136 }
3137 
3138 
3139 //========================================================================
3140 /// Consolidate external halo node storage by removing nulled out
3141 /// pointers in external halo and haloed schemes for all meshes.
3142 //========================================================================
3144  {
3145 
3146  //Do we have submeshes?
3147  unsigned n_mesh_loop=1;
3148  unsigned nmesh=nsub_mesh();
3149  if (nmesh>0)
3150  {
3151  n_mesh_loop=nmesh;
3152  }
3153 
3154  //Storage for number of processors and current processor
3155  int n_proc=this->communicator_pt()->nproc();
3156  int my_rank=this->communicator_pt()->my_rank();
3157 
3158  //If only one processor then return
3159  if(n_proc==1) {return;}
3160 
3161  //Loop over all (other) processors and store index of any nulled-out
3162  //external halo nodes in storage scheme.
3163 
3164  //Data to be sent to each processor
3165  Vector<int> send_n(n_proc,0);
3166 
3167  //Storage for all values to be sent to all processors
3168  Vector<int> send_data;
3169 
3170  //Start location within send_data for data to be sent to each processor
3171  Vector<int> send_displacement(n_proc,0);
3172 
3173  //Check missing ones
3174  for (int domain=0;domain<n_proc;domain++)
3175  {
3176  //Set the offset for the current processor
3177  send_displacement[domain] = send_data.size();
3178 
3179  //Don't bother to do anything if the processor in the loop is the
3180  //current processor
3181  if(domain!=my_rank)
3182  {
3183 
3184  //Deal with sub-meshes one-by-one if required
3185  Mesh* my_mesh_pt=0;
3186 
3187  //Loop over submeshes
3188  for (unsigned imesh=0;imesh<n_mesh_loop;imesh++)
3189  {
3190  if (nmesh==0)
3191  {
3192  my_mesh_pt=mesh_pt();
3193  }
3194  else
3195  {
3196  my_mesh_pt=mesh_pt(imesh);
3197  }
3198 
3199  //Make backup of external halo node pointers with this domain
3200  Vector<Node*> backup_pt(my_mesh_pt->external_halo_node_pt(domain));
3201 
3202  //How many do we have currently?
3203  unsigned nnod=backup_pt.size();
3204 
3205  //Prepare storage for updated halo nodes
3206  Vector<Node*> new_external_halo_node_pt;
3207  new_external_halo_node_pt.reserve(nnod);
3208 
3209  //Loop over external halo nodes with this domain
3210  for (unsigned j=0;j<nnod;j++)
3211  {
3212  //Get pointer to node
3213  Node* nod_pt=backup_pt[j];
3214 
3215  //Has it been nulled out?
3216  if (nod_pt==0)
3217  {
3218  //Save index of nulled out one
3219  send_data.push_back(j);
3220  }
3221  else
3222  {
3223  //Still alive: Copy across
3224  new_external_halo_node_pt.push_back(nod_pt);
3225  }
3226  }
3227 
3228  //Set new external halo node vector
3229  my_mesh_pt->set_external_halo_node_pt(domain,new_external_halo_node_pt);
3230 
3231  //End of data for this mesh
3232  send_data.push_back(-1);
3233 
3234  } // end of loop over meshes
3235 
3236  } // end skip own domain
3237 
3238  // Find the number of data added to the vector
3239  send_n[domain] = send_data.size() - send_displacement[domain];
3240 
3241  }
3242 
3243  //Storage for the number of data to be received from each processor
3244  Vector<int> receive_n(n_proc,0);
3245 
3246  //Now send numbers of data to be sent between all processors
3247  MPI_Alltoall(&send_n[0],1,MPI_INT,&receive_n[0],1,MPI_INT,
3248  this->communicator_pt()->mpi_comm());
3249 
3250 
3251  //We now prepare the data to be received
3252  //by working out the displacements from the received data
3253  Vector<int> receive_displacement(n_proc,0);
3254  int receive_data_count=0;
3255  for(int rank=0;rank<n_proc;++rank)
3256  {
3257  //Displacement is number of data received so far
3258  receive_displacement[rank] = receive_data_count;
3259  receive_data_count += receive_n[rank];
3260  }
3261 
3262  //Now resize the receive buffer for all data from all processors
3263  //Make sure that it has a size of at least one
3264  if(receive_data_count==0) {++receive_data_count;}
3265  Vector<int> receive_data(receive_data_count);
3266 
3267  //Make sure that the send buffer has size at least one
3268  //so that we don't get a segmentation fault
3269  if(send_data.size()==0) {send_data.resize(1);}
3270 
3271  //Now send the data between all the processors
3272  MPI_Alltoallv(&send_data[0],&send_n[0],&send_displacement[0],
3273  MPI_INT,
3274  &receive_data[0],&receive_n[0],
3275  &receive_displacement[0],
3276  MPI_INT,
3277  this->communicator_pt()->mpi_comm());
3278 
3279  //Now use the received data
3280  for (int send_rank=0;send_rank<n_proc;send_rank++)
3281  {
3282  //Don't bother to do anything for the processor corresponding to the
3283  //current processor or if no data were received from this processor
3284  if((send_rank != my_rank) && (receive_n[send_rank] != 0))
3285  {
3286  //Counter for the data within the large array
3287  unsigned count=receive_displacement[send_rank];
3288 
3289  //Deal with sub-meshes one-by-one if required
3290  Mesh* my_mesh_pt=0;
3291 
3292  // Loop over submeshes
3293  for (unsigned imesh=0;imesh<n_mesh_loop;imesh++)
3294  {
3295  if (nmesh==0)
3296  {
3297  my_mesh_pt=mesh_pt();
3298  }
3299  else
3300  {
3301  my_mesh_pt=mesh_pt(imesh);
3302  }
3303 
3304  //Make backup of external haloed node pointers with this domain
3305  Vector<Node*> backup_pt=my_mesh_pt->external_haloed_node_pt(send_rank);
3306 
3307  //Unpack until we reach "end of data" indicator (-1) for this mesh
3308  while (true)
3309  {
3310  //Read next entry
3311  int next_one=receive_data[count++];
3312 
3313  if (next_one==-1)
3314  {
3315  break;
3316  }
3317  else
3318  {
3319  //Null out the entry
3320  backup_pt[next_one]=0;
3321  }
3322  }
3323 
3324  //How many do we have currently?
3325  unsigned nnod=backup_pt.size();
3326 
3327  //Prepare storage for updated haloed nodes
3328  Vector<Node*> new_external_haloed_node_pt;
3329  new_external_haloed_node_pt.reserve(nnod);
3330 
3331  //Loop over external haloed nodes with this domain
3332  for (unsigned j=0;j<nnod;j++)
3333  {
3334  //Get pointer to node
3335  Node* nod_pt=backup_pt[j];
3336 
3337  //Has it been nulled out?
3338  if (nod_pt!=0)
3339  {
3340  //Still alive: Copy across
3341  new_external_haloed_node_pt.push_back(nod_pt);
3342  }
3343  }
3344 
3345  //Set new external haloed node vector
3346  my_mesh_pt->set_external_haloed_node_pt(send_rank,
3347  new_external_haloed_node_pt);
3348 
3349  }
3350  }
3351 
3352  } //End of data is received
3353  }
3354 
3355 #endif
3356 
3357 
3358 
3359 //=======================================================================
3360 /// Function that sets the values of the dofs in the object
3361 //======================================================================
3363  {
3364  const unsigned long n_dof = this->ndof();
3365 #ifdef PARANOID
3366  if(n_dof != dofs.nrow())
3367  {
3368  std::ostringstream error_stream;
3369  error_stream << "Number of degrees of freedom in vector argument "
3370  << dofs.nrow() << "\n"
3371  << "does not equal number of degrees of freedom in problem "
3372  << n_dof;
3373  throw OomphLibError(error_stream.str(),
3374  OOMPH_CURRENT_FUNCTION,
3375  OOMPH_EXCEPTION_LOCATION);
3376  }
3377 #endif
3378  for(unsigned long l=0;l<n_dof;l++)
3379  {
3380  *Dof_pt[l] = dofs[l];
3381  }
3382  }
3383 
3384  /// Set history values of dofs
3385  void Problem::set_dofs(const unsigned& t, DoubleVector& dofs)
3386  {
3387 #ifdef PARANOID
3388  if(distributed())
3389  {
3390  throw OomphLibError("Not designed for distributed problems",
3391  OOMPH_EXCEPTION_LOCATION,
3392  OOMPH_CURRENT_FUNCTION);
3393  // might work if the dofs vector is distributed in the right way...
3394  }
3395 #endif
3396 
3397  // First deal with global data
3398  unsigned Nglobal_data = nglobal_data();
3399  for(unsigned i=0; i<Nglobal_data; i++)
3400  {
3401  for(unsigned j=0, nj=Global_data_pt[i]->nvalue(); j<nj; j++)
3402  {
3403  // For each data get the equation number and copy out the value.
3404  int eqn_number = Global_data_pt[i]->eqn_number(j);
3405  if(eqn_number >= 0)
3406  {
3407  Global_data_pt[i]->set_value(t, j, dofs[eqn_number]);
3408  }
3409  }
3410  }
3411 
3412  // Next element internal data
3413  for(unsigned i=0, ni=mesh_pt()->nelement(); i<ni; i++)
3414  {
3415 
3416  GeneralisedElement* ele_pt = mesh_pt()->element_pt(i);
3417  for(unsigned j=0, nj=ele_pt->ninternal_data(); j<nj; j++)
3418  {
3419 
3420  Data* d_pt = ele_pt->internal_data_pt(j);
3421  for(unsigned k=0, nk=d_pt->nvalue(); k<nk; k++)
3422  {
3423 
3424  int eqn_number = d_pt->eqn_number(k);
3425  if(eqn_number >= 0)
3426  {
3427  d_pt->set_value(t, k, dofs[eqn_number]);
3428  }
3429  }
3430  }
3431  }
3432 
3433  // Now the nodes
3434  for(unsigned i=0, ni=mesh_pt()->nnode(); i<ni; i++)
3435  {
3436  Node* node_pt = mesh_pt()->node_pt(i);
3437  for(unsigned j=0, nj=node_pt->nvalue(); j<nj; j++)
3438  {
3439  // For each node get the equation number and copy out the value.
3440  int eqn_number = node_pt->eqn_number(j);
3441  if(eqn_number >= 0)
3442  {
3443  node_pt->set_value(t, j, dofs[eqn_number]);
3444  }
3445  }
3446  }
3447  }
3448 
3449 
3450  /// Set history values of dofs from the type of vector stored in
3451  /// problem::Dof_pt.
3452  void Problem::set_dofs(const unsigned& t, Vector<double*>& dof_pt)
3453  {
3454 #ifdef PARANOID
3455  if(distributed())
3456  {
3457  throw OomphLibError("Not implemented for distributed problems!",
3458  OOMPH_EXCEPTION_LOCATION,
3459  OOMPH_CURRENT_FUNCTION);
3460  }
3461 #endif
3462 
3463  // If we have any spine meshes I think there might be more degrees
3464  // of freedom there. I don't use them though so I'll let someone who
3465  // knows what they are doing handle it. --David Shepherd
3466 
3467  // First deal with global data
3468  unsigned Nglobal_data = nglobal_data();
3469  for(unsigned i=0; i<Nglobal_data; i++)
3470  {
3471  for(unsigned j=0, nj=Global_data_pt[i]->nvalue(); j<nj; j++)
3472  {
3473  // For each data get the equation number and copy in the value.
3474  int eqn_number = Global_data_pt[i]->eqn_number(j);
3475  if(eqn_number >= 0)
3476  {
3477  Global_data_pt[i]->set_value(t, j, *(dof_pt[eqn_number]));
3478  }
3479  }
3480  }
3481 
3482  // Now the mesh data
3483  // nodes
3484  for(unsigned i=0, ni=mesh_pt()->nnode(); i<ni; i++)
3485  {
3486  Node* node_pt = mesh_pt()->node_pt(i);
3487  for(unsigned j=0, nj=node_pt->nvalue(); j<nj; j++)
3488  {
3489  // For each node get the equation number and copy in the value.
3490  int eqn_number = node_pt->eqn_number(j);
3491  if(eqn_number >= 0)
3492  {
3493  node_pt->set_value(t, j, *(dof_pt[eqn_number]));
3494  }
3495  }
3496  }
3497 
3498  // and non-nodal data inside elements
3499  for(unsigned i=0, ni=mesh_pt()->nelement(); i<ni; i++)
3500  {
3501  GeneralisedElement* ele_pt = mesh_pt()->element_pt(i);
3502  for(unsigned j=0, nj=ele_pt->ninternal_data(); j<nj; j++)
3503  {
3504  Data* data_pt = ele_pt->internal_data_pt(j);
3505  // For each node get the equation number and copy in the value.
3506  int eqn_number = data_pt->eqn_number(j);
3507  if(eqn_number >= 0)
3508  {
3509  data_pt->set_value(t, j, *(dof_pt[eqn_number]));
3510  }
3511  }
3512  }
3513 
3514  }
3515 
3516 
3517 //===================================================================
3518 ///Function that adds the values to the dofs
3519 //==================================================================
3520  void Problem::add_to_dofs(const double &lambda,
3521  const DoubleVector &increment_dofs)
3522  {
3523  const unsigned long n_dof = this->ndof();
3524  for(unsigned long l=0;l<n_dof;l++)
3525  {
3526  *Dof_pt[l] += lambda*increment_dofs[l];
3527  }
3528  }
3529 
3530 
3531 
3532 //=========================================================================
3533 ///Return the residual vector multiplied by the inverse mass matrix
3534 ///Virtual so that it can be overloaded for mpi problems
3535 //=========================================================================
3537  {
3538  //This function does not make sense for assembly handlers other than the
3539  //default, so complain if we try to call it with another handler
3540 
3541 #ifdef PARANOID
3542  //If we are not the default, then complain
3544  {
3545  std::ostringstream error_stream;
3546  error_stream
3547  <<
3548  "The function get_inverse_mass_matrix_times_residuals() can only be\n"
3549  <<
3550  "used with the default assembly handler\n\n";
3551  throw OomphLibError(error_stream.str(),
3552  OOMPH_CURRENT_FUNCTION,
3553  OOMPH_EXCEPTION_LOCATION);
3554  }
3555 #endif
3556 
3557  //Find the number of degrees of freedom in the problem
3558  const unsigned n_dof = this->ndof();
3559 
3560  //Resize the vector
3561  LinearAlgebraDistribution dist(this->communicator_pt(),n_dof,false);
3562  Mres.build(&dist,0.0);
3563 
3564  //If we have discontinuous formulation
3565  //We can invert the mass matrix element by element
3567  {
3568  //Loop over the elements and get their residuals
3569  const unsigned n_element = Problem::mesh_pt()->nelement();
3570  Vector<double> element_Mres;
3571  for(unsigned e=0;e<n_element;e++)
3572  {
3573  //Cache the element
3574  DGElement* const elem_pt =
3575  dynamic_cast<DGElement*>(Problem::mesh_pt()->element_pt(e));
3576 
3577  //Find the elemental inverse mass matrix times residuals
3578  const unsigned n_el_dofs = elem_pt->ndof();
3579  elem_pt->get_inverse_mass_matrix_times_residuals(element_Mres);
3580 
3581  //Add contribution to global matrix
3582  for(unsigned i=0;i<n_el_dofs;i++)
3583  {
3584  Mres[elem_pt->eqn_number(i)] = element_Mres[i];
3585  }
3586  }
3587  }
3588  //Otherwise it's continous and we must invert the full
3589  //mass matrix via a global linear solve.
3590  else
3591  {
3592  //Now do the linear solve -- recycling Mass matrix if requested
3593  //If we already have the factorised mass matrix, then resolve
3595  {
3597  {
3598  oomph_info << "Not recomputing Mass Matrix " << std::endl;
3599  }
3600 
3601  //Get the residuals
3602  DoubleVector residuals(&dist,0.0);
3603  this->get_residuals(residuals);
3604 
3605  // Resolve the linear system
3607  }
3608  //Otherwise solve for the first time
3609  else
3610  {
3611  //If we wish to reuse the mass matrix, then enable resolve
3613  {
3615  {
3616  oomph_info << "Enabling resolve in explicit timestep" << std::endl;
3617  }
3619  }
3620 
3621  //Use a custom assembly handler to assemble and invert the mass matrix
3622 
3623  //Store the old assembly handler
3624  AssemblyHandler* old_assembly_handler_pt = this->assembly_handler_pt();
3625  //Set the assembly handler to the explicit timestep handler
3627 
3628  //Solve the linear system
3630  //The mass matrix has now been computed
3632 
3633  //Delete the Explicit Timestep handler
3634  delete this->assembly_handler_pt();
3635  //Reset the assembly handler to the original handler
3636  this->assembly_handler_pt() = old_assembly_handler_pt;
3637  }
3638  }
3639  }
3640 
3642  {
3643  // Loop over timesteppers: make them (temporarily) steady and store their
3644  // is_steady status.
3645  unsigned n_time_steppers = this->ntime_stepper();
3646  std::vector<bool> was_steady(n_time_steppers);
3647  for(unsigned i=0;i<n_time_steppers;i++)
3648  {
3649  was_steady[i]=time_stepper_pt(i)->is_steady();
3651  }
3652 
3653  // Calculate f using the residual/jacobian machinary.
3655 
3656  // Reset the is_steady status of all timesteppers that weren't already
3657  // steady when we came in here and reset their weights
3658  for(unsigned i=0;i<n_time_steppers;i++)
3659  {
3660  if (!was_steady[i])
3661  {
3663  }
3664  }
3665  }
3666 
3667 
3668 
3669 //================================================================
3670 /// Get the total residuals Vector for the problem
3671 //================================================================
3673  {
3674  // Three different cases; if MPI_Helpers::MPI_has_been_initialised=true
3675  // this means MPI_Helpers::init() has been called. This could happen on a
3676  // code compiled with MPI but run serially; in this instance the
3677  // get_residuals function still works on one processor.
3678  //
3679  // Secondly, if a code has been compiled with MPI, but MPI_Helpers::init()
3680  // has not been called, then MPI_Helpers::MPI_has_been_initialised=false
3681  // and the code calls...
3682  //
3683  // Thirdly, the serial version (compiled by all, but only run when compiled
3684  // with MPI if MPI_Helpers::MPI_has_been_initialised=false
3685 
3686  //Check that the residuals has the correct number of rows if it has been
3687  //setup
3688 #ifdef PARANOID
3689  if(residuals.built())
3690  {
3691  if(residuals.distribution_pt()->nrow() != this->ndof())
3692  {
3693  std::ostringstream error_stream;
3694  error_stream <<
3695  "The distribution of the residuals vector does not have the correct\n"
3696  <<
3697  "number of global rows\n";
3698 
3699  throw OomphLibError(error_stream.str(),
3700  OOMPH_CURRENT_FUNCTION,
3701  OOMPH_EXCEPTION_LOCATION);
3702  }
3703  }
3704 #endif
3705 
3706  //Find the number of rows
3707  const unsigned nrow = this->ndof();
3708 
3709  // Determine the distribution for the residuals vector
3710  // IF the vector has distribution setup then use that
3711  // ELSE determine the distribution based on the
3712  // distributed_matrix_distribution enum
3713  LinearAlgebraDistribution* dist_pt=0;
3714  if (residuals.built())
3715  {
3716  dist_pt = new LinearAlgebraDistribution(residuals.distribution_pt());
3717  }
3718  else
3719  {
3720 #ifdef OOMPH_HAS_MPI
3721  // if problem is only one one processor assemble non-distributed
3722  // distribution
3723  if (Communicator_pt->nproc() == 1)
3724  {
3725  dist_pt = new LinearAlgebraDistribution(Communicator_pt,nrow,false);
3726  }
3727  // if the problem is not distributed then assemble the jacobian with
3728  // a uniform distributed distribution
3729  else if (!Problem_has_been_distributed)
3730  {
3731  dist_pt = new LinearAlgebraDistribution(Communicator_pt,nrow,true);
3732  }
3733  // otherwise the problem is a distributed problem
3734  else
3735  {
3737  {
3739  dist_pt = new LinearAlgebraDistribution(Communicator_pt,nrow,true);
3740  break;
3743  break;
3745  LinearAlgebraDistribution* uniform_dist_pt =
3747  bool use_problem_dist = true;
3748  unsigned nproc = Communicator_pt->nproc();
3749  for (unsigned p = 0; p < nproc; p++)
3750  {
3751  if ((double)Dof_distribution_pt->nrow_local(p) >
3752  ((double)uniform_dist_pt->nrow_local(p))*1.1)
3753  {
3754  use_problem_dist = false;
3755  }
3756  }
3757  if (use_problem_dist)
3758  {
3760  }
3761  else
3762  {
3763  dist_pt = new LinearAlgebraDistribution(uniform_dist_pt);
3764  }
3765  delete uniform_dist_pt;
3766  break;
3767  }
3768  }
3769 #else
3770  dist_pt = new LinearAlgebraDistribution(Communicator_pt,nrow,false);
3771 #endif
3772  }
3773 
3774  //Locally cache pointer to assembly handler
3776 
3777  //Build and zero the residuals
3778  residuals.build(dist_pt,0.0);
3779 
3780  //Serial (or one processor case)
3781 #ifdef OOMPH_HAS_MPI
3782  if(this->communicator_pt()->nproc() == 1)
3783  {
3784 #endif // OOMPH_HAS_MPI
3785  //Loop over all the elements
3786  unsigned long Element_pt_range = Mesh_pt->nelement();
3787  for(unsigned long e=0;e<Element_pt_range;e++)
3788  {
3789  //Get the pointer to the element
3790  GeneralisedElement* elem_pt = Mesh_pt->element_pt(e);
3791  //Find number of dofs in the element
3792  unsigned n_element_dofs = assembly_handler_pt->ndof(elem_pt);
3793  //Set up an array
3794  Vector<double> element_residuals(n_element_dofs);
3795  //Fill the array
3796  assembly_handler_pt->get_residuals(elem_pt,element_residuals);
3797  //Now loop over the dofs and assign values to global Vector
3798  for(unsigned l=0;l<n_element_dofs;l++)
3799  {
3800  residuals[assembly_handler_pt->eqn_number(elem_pt,l)]
3801  += element_residuals[l];
3802  }
3803  }
3804  //Otherwise parallel case
3805 #ifdef OOMPH_HAS_MPI
3806  }
3807 else
3808  {
3809  //Store the current assembly handler
3810  AssemblyHandler* const old_assembly_handler_pt = Assembly_handler_pt;
3811  //Create a new assembly handler that only assembles the residuals
3812  Assembly_handler_pt = new ParallelResidualsHandler(old_assembly_handler_pt);
3813 
3814  //Setup memory for parallel sparse assemble
3815  //No matrix so all size zero
3816  Vector<int*> column_index;
3817  Vector<int* > row_start;
3818  Vector<double* > value;
3819  Vector<unsigned> nnz;
3820  //One set of residuals of sizer one
3821  Vector<double*> res(1);
3822 
3823  //Call the parallel sparse assemble, that should only assemble residuals
3824  parallel_sparse_assemble(dist_pt,
3825  column_index,
3826  row_start,
3827  value,
3828  nnz,
3829  res);
3830  //Fill in the residuals data
3831  residuals.set_external_values(res[0],true);
3832 
3833  //Delete new assembly handler
3834  delete Assembly_handler_pt;
3835  //Reset the assembly handler to the original
3836  Assembly_handler_pt = old_assembly_handler_pt;
3837  }
3838 #endif
3839 
3840  //Delete the distribution
3841  delete dist_pt;
3842  }
3843 
3844 //=============================================================================
3845 /// Get the fully assembled residual vector and Jacobian matrix
3846 /// in dense storage. The DoubleVector residuals returned will be
3847 /// non-distributed. If on calling this method the DoubleVector residuals is
3848 /// setup then it must be non-distributed and of the correct length.
3849 /// The matrix type DenseDoubleMatrix is not distributable and therefore
3850 /// the residual vector is also assumed to be non distributable.
3851 //=============================================================================
3853  DenseDoubleMatrix& jacobian)
3854  {
3855  // get the number of degrees of freedom
3856  unsigned n_dof=ndof();
3857 
3858 #ifdef PARANOID
3859  // PARANOID checks : if the distribution of residuals is setup then it must
3860  // must not be distributed, have the right number of rows, and the same
3861  // communicator as the problem
3862  if (residuals.built())
3863  {
3864  if (residuals.distribution_pt()->distributed())
3865  {
3866  std::ostringstream error_stream;
3867  error_stream
3868  << "If the DoubleVector residuals is setup then it must not "
3869  << "be distributed.";
3870  throw OomphLibError(error_stream.str(),
3871  OOMPH_CURRENT_FUNCTION,
3872  OOMPH_EXCEPTION_LOCATION);
3873  }
3874  if (residuals.distribution_pt()->nrow() != n_dof)
3875  {
3876  std::ostringstream error_stream;
3877  error_stream
3878  << "If the DoubleVector residuals is setup then it must have"
3879  << " the correct number of rows";
3880  throw OomphLibError(error_stream.str(),
3881  OOMPH_CURRENT_FUNCTION,
3882  OOMPH_EXCEPTION_LOCATION);
3883  }
3884  if (!(*Communicator_pt == *residuals.distribution_pt()->communicator_pt()))
3885  {
3886  std::ostringstream error_stream;
3887  error_stream
3888  << "If the DoubleVector residuals is setup then it must have"
3889  << " the same communicator as the problem.";
3890  throw OomphLibError(error_stream.str(),
3891  OOMPH_CURRENT_FUNCTION,
3892  OOMPH_EXCEPTION_LOCATION);
3893  }
3894  }
3895 #endif
3896 
3897  // set the residuals distribution if it is not setup
3898  if (!residuals.built())
3899  {
3900  LinearAlgebraDistribution dist(Communicator_pt,n_dof,false);
3901  residuals.build(&dist,0.0);
3902  }
3903  // else just zero the residuals
3904  else
3905  {
3906  residuals.initialise(0.0);
3907  }
3908 
3909  // Resize the matrices -- this cannot always be done externally
3910  // because get_jacobian exists in many different versions for
3911  // different storage formats -- resizing a CC or CR matrix doesn't
3912  // make sense.
3913 
3914  // resize the jacobian
3915  jacobian.resize(n_dof,n_dof);
3916  jacobian.initialise(0.0);
3917 
3918  //Locally cache pointer to assembly handler
3920 
3921  //Loop over all the elements
3922  unsigned long n_element = Mesh_pt->nelement();
3923  for(unsigned long e=0;e<n_element;e++)
3924  {
3925  //Get the pointer to the element
3926  GeneralisedElement* elem_pt = Mesh_pt->element_pt(e);
3927  //Find number of dofs in the element
3928  unsigned n_element_dofs = assembly_handler_pt->ndof(elem_pt);
3929  //Set up an array
3930  Vector<double> element_residuals(n_element_dofs);
3931  //Set up a matrix
3932  DenseMatrix<double> element_jacobian(n_element_dofs);
3933  //Fill the array
3934  assembly_handler_pt->get_jacobian(elem_pt,
3935  element_residuals,element_jacobian);
3936  //Now loop over the dofs and assign values to global Vector
3937  for(unsigned l=0;l<n_element_dofs;l++)
3938  {
3939  unsigned long eqn_number = assembly_handler_pt->eqn_number(elem_pt,l);
3940  residuals[eqn_number] += element_residuals[l];
3941  for(unsigned l2=0;l2<n_element_dofs;l2++)
3942  {
3943  jacobian(eqn_number ,
3944  assembly_handler_pt->eqn_number(elem_pt,l2)) +=
3945  element_jacobian(l,l2);
3946  }
3947  }
3948  }
3949  }
3950 
3951 //=============================================================================
3952 /// Return the fully-assembled Jacobian and residuals for the problem,
3953 /// in the case where the Jacobian matrix is in a distributable
3954 /// row compressed storage format.
3955 /// 1. If the distribution of the jacobian and residuals is setup then, they
3956 /// will be returned with that distribution.
3957 /// Note. the jacobian and residuals must have the same distribution.
3958 /// 2. If the distribution of the jacobian and residuals are not setup then
3959 /// their distribution will computed based on:
3960 /// Distributed_problem_matrix_distribution.
3961 //=============================================================================
3963  {
3964 
3965  // Three different cases; if MPI_Helpers::MPI_has_been_initialised=true
3966  // this means MPI_Helpers::setup() has been called. This could happen on a
3967  // code compiled with MPI but run serially; in this instance the
3968  // get_residuals function still works on one processor.
3969  //
3970  // Secondly, if a code has been compiled with MPI, but MPI_Helpers::setup()
3971  // has not been called, then MPI_Helpers::MPI_has_been_initialised=false
3972  // and the code calls...
3973  //
3974  // Thirdly, the serial version (compiled by all, but only run when compiled
3975  // with MPI if MPI_Helpers::MPI_has_been_initialised=false
3976  //
3977  // The only case where an MPI code cannot run serially at present
3978  // is one where the distribute function is used (i.e. METIS is called)
3979 
3980  //Allocate storage for the matrix entries
3981  //The generalised Vector<Vector<>> structure is required
3982  //for the most general interface to sparse_assemble() which allows
3983  //the assembly of multiple matrices at once.
3984  Vector<int* > column_index(1);
3985  Vector<int* > row_start(1);
3986  Vector<double* > value(1);
3987  Vector<unsigned> nnz(1);
3988 
3989 #ifdef PARANOID
3990  // PARANOID checks that the distribution of the jacobian matches that of the
3991  // residuals (if they are setup) and that they have the right number of rows
3992  if (residuals.built() &&
3993  jacobian.distribution_built())
3994  {
3995  if (!(*residuals.distribution_pt() == *jacobian.distribution_pt()))
3996  {
3997  std::ostringstream error_stream;
3998  error_stream << "The distribution of the residuals must "
3999  << "be the same as the distribution of the jacobian.";
4000  throw OomphLibError(error_stream.str(),
4001  OOMPH_CURRENT_FUNCTION,
4002  OOMPH_EXCEPTION_LOCATION);
4003  }
4004  if (jacobian.distribution_pt()->nrow() != this->ndof())
4005  {
4006  std::ostringstream error_stream;
4007  error_stream << "The distribution of the jacobian and residuals does not"
4008  << "have the correct number of global rows.";
4009  throw OomphLibError(error_stream.str(),
4010  OOMPH_CURRENT_FUNCTION,
4011  OOMPH_EXCEPTION_LOCATION);
4012  }
4013  }
4014  else if (residuals.built() !=
4015  jacobian.distribution_built())
4016  {
4017  std::ostringstream error_stream;
4018  error_stream << "The distribution of the jacobian and residuals must "
4019  << "both be setup or both not setup";
4020  throw OomphLibError(error_stream.str(),
4021  OOMPH_CURRENT_FUNCTION,
4022  OOMPH_EXCEPTION_LOCATION);
4023  }
4024 #endif
4025 
4026 
4027  //Allocate generalised storage format for passing to sparse_assemble()
4028  Vector<double* > res(1);
4029 
4030  // number of rows
4031  unsigned nrow = this->ndof();
4032 
4033  // determine the distribution for the jacobian.
4034  // IF the jacobian has distribution setup then use that
4035  // ELSE determine the distribution based on the
4036  // distributed_matrix_distribution enum
4037  LinearAlgebraDistribution* dist_pt=0;
4038  if (jacobian.distribution_built())
4039  {
4040  dist_pt = new LinearAlgebraDistribution(jacobian.distribution_pt());
4041  }
4042  else
4043  {
4044 #ifdef OOMPH_HAS_MPI
4045  // if problem is only one one processor
4046  if (Communicator_pt->nproc() == 1)
4047  {
4048  dist_pt = new LinearAlgebraDistribution(Communicator_pt,nrow,false);
4049  }
4050  // if the problem is not distributed then assemble the jacobian with
4051  // a uniform distributed distribution
4052  else if (!Problem_has_been_distributed)
4053  {
4054  dist_pt = new LinearAlgebraDistribution(Communicator_pt,nrow,true);
4055  }
4056  // otherwise the problem is a distributed problem
4057  else
4058  {
4060  {
4062  dist_pt = new LinearAlgebraDistribution(Communicator_pt,nrow,true);
4063  break;
4066  break;
4068  LinearAlgebraDistribution* uniform_dist_pt =
4070  bool use_problem_dist = true;
4071  unsigned nproc = Communicator_pt->nproc();
4072  for (unsigned p = 0; p < nproc; p++)
4073  {
4074  if ((double)Dof_distribution_pt->nrow_local(p) >
4075  ((double)uniform_dist_pt->nrow_local(p))*1.1)
4076  {
4077  use_problem_dist = false;
4078  }
4079  }
4080  if (use_problem_dist)
4081  {
4083  }
4084  else
4085  {
4086  dist_pt = new LinearAlgebraDistribution(uniform_dist_pt);
4087  }
4088  delete uniform_dist_pt;
4089  break;
4090  }
4091  }
4092 #else
4093  dist_pt = new LinearAlgebraDistribution(Communicator_pt,nrow,false);
4094 #endif
4095  }
4096 
4097 
4098  //The matrix is in compressed row format
4099  bool compressed_row_flag=true;
4100 
4101 #ifdef OOMPH_HAS_MPI
4102  //
4103  if (Communicator_pt->nproc() == 1)
4104  {
4105 #endif
4107  row_start,
4108  value,
4109  nnz,
4110  res,
4111  compressed_row_flag);
4112  jacobian.build(dist_pt);
4113  jacobian.build_without_copy(dist_pt->nrow(),nnz[0],
4114  value[0],column_index[0],row_start[0]);
4115  residuals.build(dist_pt,0.0);
4116  residuals.set_external_values(res[0],true);
4117 #ifdef OOMPH_HAS_MPI
4118  }
4119  else
4120  {
4121  if (dist_pt->distributed())
4122  {
4123  parallel_sparse_assemble(dist_pt,
4124  column_index,
4125  row_start,
4126  value,
4127  nnz,
4128  res);
4129  jacobian.build(dist_pt);
4130  jacobian.build_without_copy(dist_pt->nrow(),nnz[0],
4131  value[0],column_index[0],
4132  row_start[0]);
4133  residuals.build(dist_pt,0.0);
4134  residuals.set_external_values(res[0],true);
4135  }
4136  else
4137  {
4138  LinearAlgebraDistribution* temp_dist_pt =
4139  new LinearAlgebraDistribution(Communicator_pt,dist_pt->nrow(),true);
4140  parallel_sparse_assemble(temp_dist_pt,
4141  column_index,
4142  row_start,
4143  value,
4144  nnz,
4145  res);
4146  jacobian.build(temp_dist_pt);
4147  jacobian.build_without_copy(dist_pt->nrow(),nnz[0],
4148  value[0],column_index[0],
4149  row_start[0]);
4150  jacobian.redistribute(dist_pt);
4151  residuals.build(temp_dist_pt,0.0);
4152  residuals.set_external_values(res[0],true);
4153  residuals.redistribute(dist_pt);
4154  delete temp_dist_pt;
4155  }
4156  }
4157 #endif
4158 
4159  // clean up dist_pt and residuals_vector pt
4160  delete dist_pt;
4161  }
4162 
4163 //=============================================================================
4164 /// Return the fully-assembled Jacobian and residuals for the problem,
4165 /// in the case when the jacobian matrix is in column-compressed storage
4166 /// format.
4167 //=============================================================================
4169  {
4170  // Three different cases; if MPI_Helpers::MPI_has_been_initialised=true
4171  // this means MPI_Helpers::setup() has been called. This could happen on a
4172  // code compiled with MPI but run serially; in this instance the
4173  // get_residuals function still works on one processor.
4174  //
4175  // Secondly, if a code has been compiled with MPI, but MPI_Helpers::setup()
4176  // has not been called, then MPI_Helpers::MPI_has_been_initialised=false
4177  // and the code calls...
4178  //
4179  // Thirdly, the serial version (compiled by all, but only run when compiled
4180  // with MPI if MPI_Helpers::MPI_has_been_5Binitialised=false
4181  //
4182  // The only case where an MPI code cannot run serially at present
4183  // is one where the distribute function is used (i.e. METIS is called)
4184 
4185  // get the number of degrees of freedom
4186  unsigned n_dof=ndof();
4187 
4188 #ifdef PARANOID
4189  // PARANOID checks : if the distribution of residuals is setup then it must
4190  // must not be distributed, have the right number of rows, and the same
4191  // communicator as the problem
4192  if (residuals.built())
4193  {
4194  if (residuals.distribution_pt()->distributed())
4195  {
4196  std::ostringstream error_stream;
4197  error_stream
4198  << "If the DoubleVector residuals is setup then it must not "
4199  << "be distributed.";
4200  throw OomphLibError(error_stream.str(),
4201  OOMPH_CURRENT_FUNCTION,
4202  OOMPH_EXCEPTION_LOCATION);
4203  }
4204  if (residuals.distribution_pt()->nrow() != n_dof)
4205  {
4206  std::ostringstream error_stream;
4207  error_stream
4208  << "If the DoubleVector residuals is setup then it must have"
4209  << " the correct number of rows";
4210  throw OomphLibError(error_stream.str(),
4211  OOMPH_CURRENT_FUNCTION,
4212  OOMPH_EXCEPTION_LOCATION);
4213  }
4214  if (!(*Communicator_pt == *residuals.distribution_pt()->communicator_pt()))
4215  {
4216  std::ostringstream error_stream;
4217  error_stream
4218  << "If the DoubleVector residuals is setup then it must have"
4219  << " the same communicator as the problem.";
4220  throw OomphLibError(error_stream.str(),
4221  OOMPH_CURRENT_FUNCTION,
4222  OOMPH_EXCEPTION_LOCATION);
4223  }
4224  }
4225 #endif
4226 
4227  //Allocate storage for the matrix entries
4228  //The generalised Vector<Vector<>> structure is required
4229  //for the most general interface to sparse_assemble() which allows
4230  //the assembly of multiple matrices at once.
4231  Vector<int* > row_index(1);
4232  Vector<int* > column_start(1);
4233  Vector<double* > value(1);
4234 
4235  //Allocate generalised storage format for passing to sparse_assemble()
4236  Vector<double* > res(1);
4237 
4238  // allocate storage for the number of non-zeros in each matrix
4239  Vector<unsigned> nnz(1);
4240 
4241  //The matrix is in compressed column format
4242  bool compressed_row_flag=false;
4243 
4244  // get the distribution for the residuals
4245  LinearAlgebraDistribution* dist_pt;
4246  if (!residuals.built())
4247  {
4248  dist_pt
4249  = new LinearAlgebraDistribution(Communicator_pt,this->ndof(),false);
4250  }
4251  else
4252  {
4253  dist_pt = new LinearAlgebraDistribution(residuals.distribution_pt());
4254  }
4255 
4256 #ifdef OOMPH_HAS_MPI
4257  if (communicator_pt()->nproc() == 1)
4258  {
4259 #endif
4261  column_start,
4262  value,
4263  nnz,
4264  res,
4265  compressed_row_flag);
4266  jacobian.build_without_copy(value[0],row_index[0],column_start[0],nnz[0],
4267  n_dof,n_dof);
4268  residuals.build(dist_pt,0.0);
4269  residuals.set_external_values(res[0],true);
4270 #ifdef OOMPH_HAS_MPI
4271  }
4272  else
4273  {
4274  std::ostringstream error_stream;
4275  error_stream
4276  << "Cannot assemble a CCDoubleMatrix Jacobian on more "
4277  << "than one processor.";
4278  throw OomphLibError(error_stream.str(),
4279  OOMPH_CURRENT_FUNCTION,
4280  OOMPH_EXCEPTION_LOCATION);
4281  }
4282 #endif
4283 
4284  // clean up
4285  delete dist_pt;
4286  }
4287 
4288 
4289 //===================================================================
4290 /// \short Set all pinned values to zero.
4291 /// Used to set boundary conditions to be homogeneous in the copy
4292 /// of the problem used in adaptive bifurcation tracking
4293 /// (ALH: TEMPORARY HACK, WILL BE FIXED)
4294 //==================================================================
4296 {
4297  //NOTE THIS DOES NOT ZERO ANY SPINE DATA, but otherwise everything else
4298  //should be zeroed
4299 
4300  //Zero any pinned global Data
4301  const unsigned n_global_data = nglobal_data();
4302  for(unsigned i=0;i<n_global_data;i++)
4303  {
4304  Data* const local_data_pt = Global_data_pt[i];
4305  const unsigned n_value = local_data_pt->nvalue();
4306  for(unsigned j=0;j<n_value;j++)
4307  {
4308  //If the data value is pinned set the value to zero
4309  if(local_data_pt->is_pinned(j)) {local_data_pt->set_value(j,0.0);}
4310  }
4311  }
4312 
4313  // Loop over the submeshes:
4314  const unsigned n_sub_mesh=Sub_mesh_pt.size();
4315  if(n_sub_mesh==0)
4316  {
4317  //Loop over the nodes in the element
4318  const unsigned n_node = Mesh_pt->nnode();
4319  for(unsigned n=0;n<n_node;n++)
4320  {
4321  Node* const local_node_pt = Mesh_pt->node_pt(n);
4322  const unsigned n_value = local_node_pt->nvalue();
4323  for(unsigned j=0;j<n_value;j++)
4324  {
4325  //If the data value is pinned set the value to zero
4326  if(local_node_pt->is_pinned(j)) {local_node_pt->set_value(j,0.0);}
4327  }
4328 
4329  //Try to cast to a solid node
4330  SolidNode* const local_solid_node_pt =
4331  dynamic_cast<SolidNode*>(local_node_pt);
4332  //If we are successful
4333  if(local_solid_node_pt)
4334  {
4335  //Find the dimension of the node
4336  const unsigned n_dim = local_solid_node_pt->ndim();
4337  //Find number of positions
4338  const unsigned n_position_type = local_solid_node_pt->nposition_type();
4339 
4340  for(unsigned k=0;k<n_position_type;k++)
4341  {
4342  for(unsigned i=0;i<n_dim;i++)
4343  {
4344  //If the generalised position is pinned,
4345  //set the value to zero
4346  if(local_solid_node_pt->position_is_pinned(k,i))
4347  {
4348  local_solid_node_pt->x_gen(k,i) = 0.0;
4349  }
4350  }
4351  }
4352  }
4353  }
4354 
4355  //Now loop over the element's and zero the internal data
4356  const unsigned n_element = Mesh_pt->nelement();
4357  for(unsigned e=0;e<n_element;e++)
4358  {
4359  GeneralisedElement* const local_element_pt = Mesh_pt->element_pt(e);
4360  const unsigned n_internal = local_element_pt->ninternal_data();
4361  for(unsigned i=0;i<n_internal;i++)
4362  {
4363  Data* const local_data_pt = local_element_pt->internal_data_pt(i);
4364  const unsigned n_value = local_data_pt->nvalue();
4365  for(unsigned j=0;j<n_value;j++)
4366  {
4367  //If the data value is pinned set the value to zero
4368  if(local_data_pt->is_pinned(j)) {local_data_pt->set_value(j,0.0);}
4369  }
4370  }
4371  } //End of loop over elements
4372  }
4373  else
4374  {
4375  //Alternatively loop over all sub meshes
4376  for (unsigned m=0;m<n_sub_mesh;m++)
4377  {
4378  //Loop over the nodes in the element
4379  const unsigned n_node = Sub_mesh_pt[m]->nnode();
4380  for(unsigned n=0;n<n_node;n++)
4381  {
4382  Node* const local_node_pt = Sub_mesh_pt[m]->node_pt(n);
4383  const unsigned n_value = local_node_pt->nvalue();
4384  for(unsigned j=0;j<n_value;j++)
4385  {
4386  //If the data value is pinned set the value to zero
4387  if(local_node_pt->is_pinned(j)) {local_node_pt->set_value(j,0.0);}
4388  }
4389 
4390  //Try to cast to a solid node
4391  SolidNode* const local_solid_node_pt =
4392  dynamic_cast<SolidNode*>(local_node_pt);
4393  //If we are successful
4394  if(local_solid_node_pt)
4395  {
4396  //Find the dimension of the node
4397  const unsigned n_dim = local_solid_node_pt->ndim();
4398  //Find number of positions
4399  const unsigned n_position_type =
4400  local_solid_node_pt->nposition_type();
4401 
4402  for(unsigned k=0;k<n_position_type;k++)
4403  {
4404  for(unsigned i=0;i<n_dim;i++)
4405  {
4406  //If the generalised position is pinned,
4407  //set the value to zero
4408  if(local_solid_node_pt->position_is_pinned(k,i))
4409  {
4410  local_solid_node_pt->x_gen(k,i) = 0.0;
4411  }
4412  }
4413  }
4414  }
4415  }
4416 
4417  //Now loop over the element's and zero the internal data
4418  const unsigned n_element = Sub_mesh_pt[m]->nelement();
4419  for(unsigned e=0;e<n_element;e++)
4420  {
4421  GeneralisedElement* const local_element_pt =
4422  Sub_mesh_pt[m]->element_pt(e);
4423  const unsigned n_internal = local_element_pt->ninternal_data();
4424  for(unsigned i=0;i<n_internal;i++)
4425  {
4426  Data* const local_data_pt = local_element_pt->internal_data_pt(i);
4427  const unsigned n_value = local_data_pt->nvalue();
4428  for(unsigned j=0;j<n_value;j++)
4429  {
4430  //If the data value is pinned set the value to zero
4431  if(local_data_pt->is_pinned(j)) {local_data_pt->set_value(j,0.0);}
4432  }
4433  }
4434  } //End of loop over elements
4435  }
4436  }
4437 }
4438 
4439 
4440 
4441 
4442 //=====================================================================
4443 /// This is a (private) helper function that is used to assemble system
4444 /// matrices in compressed row or column format
4445 /// and compute residual vectors.
4446 /// The default action is to assemble the jacobian matrix and
4447 /// residuals for the Newton method. The action can be
4448 /// overloaded at an elemental level by changing the default
4449 /// behaviour of the function Element::get_all_vectors_and_matrices().
4450 /// column_or_row_index: Column [or row] index of given entry
4451 /// row_or_column_start: Index of first entry for given row [or column]
4452 /// value : Vector of nonzero entries
4453 /// residuals : Residual vector
4454 /// compressed_row_flag: Bool flag to indicate if storage format is
4455 /// compressed row [if false interpretation of
4456 /// arguments is as stated in square brackets].
4457 /// We provide four different assembly methods, each with different
4458 /// memory requirements/execution speeds. The method is set by
4459 /// the public flag Problem::Sparse_assembly_method.
4460 //=====================================================================
4462  Vector<int* > &column_or_row_index,
4463  Vector<int* > &row_or_column_start,
4464  Vector<double* > &value,
4465  Vector<unsigned > &nnz,
4466  Vector<double* > &residuals,
4467  bool compressed_row_flag)
4468 {
4469 
4470  // Choose the actual method
4471  switch(Sparse_assembly_method)
4472  {
4473 
4475 
4477  column_or_row_index,
4478  row_or_column_start,
4479  value,
4480  nnz,
4481  residuals,
4482  compressed_row_flag);
4483 
4484  break;
4485 
4487 
4489  column_or_row_index,
4490  row_or_column_start,
4491  value,
4492  nnz,
4493  residuals,
4494  compressed_row_flag);
4495 
4496  break;
4497 
4499 
4501  column_or_row_index,
4502  row_or_column_start,
4503  value,
4504  nnz,
4505  residuals,
4506  compressed_row_flag);
4507 
4508  break;
4509 
4511 
4513  column_or_row_index,
4514  row_or_column_start,
4515  value,
4516  nnz,
4517  residuals,
4518  compressed_row_flag);
4519 
4520  break;
4521 
4523 
4525  column_or_row_index,
4526  row_or_column_start,
4527  value,
4528  nnz,
4529  residuals,
4530  compressed_row_flag);
4531 
4532  break;
4533 
4534  default:
4535 
4536  std::ostringstream error_stream;
4537  error_stream
4538  << "Error: Incorrect value for Problem::Sparse_assembly_method"
4539  << Sparse_assembly_method << std::endl
4540  << "It should be one of the enumeration Problem::Assembly_method"
4541  << std::endl;
4542  throw OomphLibError(error_stream.str(),
4543  OOMPH_CURRENT_FUNCTION,
4544  OOMPH_EXCEPTION_LOCATION);
4545  }
4546 }
4547 
4548 
4549 
4550 
4551 
4552 //=====================================================================
4553 /// This is a (private) helper function that is used to assemble system
4554 /// matrices in compressed row or column format
4555 /// and compute residual vectors, using maps
4556 /// The default action is to assemble the jacobian matrix and
4557 /// residuals for the Newton method. The action can be
4558 /// overloaded at an elemental level by chaging the default
4559 /// behaviour of the function Element::get_all_vectors_and_matrices().
4560 /// column_or_row_index: Column [or row] index of given entry
4561 /// row_or_column_start: Index of first entry for given row [or column]
4562 /// value : Vector of nonzero entries
4563 /// residuals : Residual vector
4564 /// compressed_row_flag: Bool flag to indicate if storage format is
4565 /// compressed row [if false interpretation of
4566 /// arguments is as stated in square brackets].
4567 //=====================================================================
4569  Vector<int* > &column_or_row_index,
4570  Vector<int* > &row_or_column_start,
4571  Vector<double* > &value,
4572  Vector<unsigned> &nnz,
4573  Vector<double* > &residuals,
4574  bool compressed_row_flag)
4575 {
4576  //Total number of elements
4577  const unsigned long n_elements = mesh_pt()->nelement();
4578 
4579  // Default range of elements for distributed problems
4580  unsigned long el_lo=0;
4581  unsigned long el_hi=n_elements-1;
4582 
4583 #ifdef OOMPH_HAS_MPI
4584  // Otherwise just loop over a fraction of the elements
4585  // (This will either have been initialised in
4586  // Problem::set_default_first_and_last_element_for_assembly() or
4587  // will have been re-assigned during a previous assembly loop
4588  // Note that following the re-assignment only the entries
4589  // for the current processor are relevant.
4591  {
4592  el_lo=First_el_for_assembly[Communicator_pt->my_rank()];
4593  el_hi=Last_el_plus_one_for_assembly[Communicator_pt->my_rank()]-1;
4594  }
4595 #endif
4596 
4597  // number of dofs
4598  unsigned ndof = this->ndof();
4599 
4600  //Find the number of vectors to be assembled
4601  const unsigned n_vector = residuals.size();
4602 
4603  //Find the number of matrices to be assembled
4604  const unsigned n_matrix = column_or_row_index.size();
4605 
4606  //Locally cache pointer to assembly handler
4608 
4609 #ifdef OOMPH_HAS_MPI
4610  bool doing_residuals=false;
4611  if (dynamic_cast<ParallelResidualsHandler*>(Assembly_handler_pt)!=0)
4612  {
4613  doing_residuals=true;
4614  }
4615 #endif
4616 
4617 //Error check dimensions
4618 #ifdef PARANOID
4619  if(row_or_column_start.size() != n_matrix)
4620  {
4621  std::ostringstream error_stream;
4622  error_stream
4623  << "Error: " << std::endl
4624  << "row_or_column_start.size() " << row_or_column_start.size()
4625  << " does not equal "
4626  << "column_or_row_index.size() "
4627  << column_or_row_index.size() << std::endl;
4628  throw OomphLibError(
4629  error_stream.str(),
4630  OOMPH_CURRENT_FUNCTION,
4631  OOMPH_EXCEPTION_LOCATION);
4632  }
4633 
4634  if(value.size() != n_matrix)
4635  {
4636  std::ostringstream error_stream;
4637  error_stream
4638  << "Error in Problem::sparse_assemble_row_or_column_compressed "
4639  << std::endl
4640  << "value.size() " << value.size() << " does not equal "
4641  << "column_or_row_index.size() "
4642  << column_or_row_index.size() << std::endl<< std::endl
4643  << std::endl;
4644  throw OomphLibError(
4645  error_stream.str(),
4646  OOMPH_CURRENT_FUNCTION,
4647  OOMPH_EXCEPTION_LOCATION);
4648  }
4649 #endif
4650 
4651 
4652 //The idea behind this sparse assembly routine is to use a vector of
4653 //maps for the entries in each row or column of the complete matrix.
4654 //The key for each map is the global row or column number and
4655 //the default comparison operator for integers means that each map
4656 //is ordered by the global row or column number. Thus, we need not
4657 //sort the maps, that happens at each insertion of a new entry. The
4658 //price we pay is that for large maps, inseration is not a
4659 //cheap operation. Hash maps can be used to increase the speed, but then
4660 //the ordering is lost and we would have to sort anyway. The solution if
4661 //speed is required is to use lists, see below.
4662 
4663 
4664 //Set up a vector of vectors of maps of entries of each matrix,
4665 //indexed by either the column or row. The entries of the vector for
4666 //each matrix correspond to all the rows or columns of that matrix.
4667 //The use of the map storage
4668 //scheme, with its implicit ordering on the first index, gives
4669 //a sparse ordered list of the entries in the given row or column.
4670  Vector<Vector<std::map<unsigned,double> > > matrix_data_map(n_matrix);
4671  //Loop over the number of matrices being assembled and resize
4672  //each vector of maps to the number of rows or columns of the matrix
4673  for(unsigned m=0;m<n_matrix;m++) {matrix_data_map[m].resize(ndof);}
4674 
4675  //Resize the residuals vectors
4676  for(unsigned v=0;v<n_vector;v++)
4677  {
4678  residuals[v] = new double[ndof];
4679  for (unsigned i = 0; i < ndof; i++)
4680  {
4681  residuals[v][i] = 0;
4682  }
4683  }
4684 
4685 
4686 #ifdef OOMPH_HAS_MPI
4687 
4688 
4689  // Storage for assembly time for elements
4690  double t_assemble_start=0.0;
4691 
4692  // Storage for assembly times
4693  if ((!doing_residuals)&&
4695  {
4696  Elemental_assembly_time.resize(n_elements);
4697  }
4698 
4699 #endif
4700 
4701  //----------------Assemble and populate the maps-------------------------
4702  {
4703  //Allocate local storage for the element's contribution to the
4704  //residuals vectors and system matrices of the size of the maximum
4705  //number of dofs in any element.
4706  //This means that the storage is only allocated (and deleted) once
4707  Vector<Vector<double> > el_residuals(n_vector);
4708  Vector<DenseMatrix<double> > el_jacobian(n_matrix);
4709 
4710  //Loop over the elements for this processor
4711  for(unsigned long e=el_lo;e<=el_hi;e++)
4712  {
4713 
4714 #ifdef OOMPH_HAS_MPI
4715  // Time it?
4716  if ((!doing_residuals)&&
4718  {
4719  t_assemble_start=TimingHelpers::timer();
4720  }
4721 #endif
4722 
4723  //Get the pointer to the element
4724  GeneralisedElement* elem_pt = mesh_pt()->element_pt(e);
4725 
4726 #ifdef OOMPH_HAS_MPI
4727  //Ignore halo elements
4728  if (!elem_pt->is_halo())
4729  {
4730 #endif
4731 
4732  //Find number of degrees of freedom in the element
4733  const unsigned nvar = assembly_handler_pt->ndof(elem_pt);
4734 
4735  //Resize the storage for elemental jacobian and residuals
4736  for(unsigned v=0;v<n_vector;v++) {el_residuals[v].resize(nvar);}
4737  for(unsigned m=0;m<n_matrix;m++) {el_jacobian[m].resize(nvar);}
4738 
4739  //Now get the residuals and jacobian for the element
4740  assembly_handler_pt->
4741  get_all_vectors_and_matrices(elem_pt,el_residuals, el_jacobian);
4742 
4743  //---------------Insert the values into the maps--------------
4744 
4745  //Loop over the first index of local variables
4746  for(unsigned i=0;i<nvar;i++)
4747  {
4748  //Get the local equation number
4749  unsigned eqn_number
4750  = assembly_handler_pt->eqn_number(elem_pt,i);
4751 
4752  //Add the contribution to the residuals
4753  for(unsigned v=0;v<n_vector;v++)
4754  {
4755  //Fill in each residuals vector
4756  residuals[v][eqn_number] += el_residuals[v][i];
4757  }
4758 
4759  //Now loop over the other index
4760  for(unsigned j=0;j<nvar;j++)
4761  {
4762  //Get the number of the unknown
4763  unsigned unknown = assembly_handler_pt->eqn_number(elem_pt,j);
4764 
4765  //Loop over the matrices
4766  for(unsigned m=0;m<n_matrix;m++)
4767  {
4768  //Get the value of the matrix at this point
4769  double value = el_jacobian[m](i,j);
4770  //Only bother to add to the map if it's non-zero
4771  if(std::fabs(value) > Numerical_zero_for_sparse_assembly)
4772  {
4773  //If it's compressed row storage, then our vector of maps
4774  //is indexed by row (equation number)
4775  if(compressed_row_flag)
4776  {
4777  //Add the data into the map using the unknown as the map key
4778  matrix_data_map[m][eqn_number][unknown] += value;
4779  }
4780  //Otherwise it's compressed column storage and our vector is
4781  //indexed by column (the unknown)
4782  else
4783  {
4784  //Add the data into the map using the eqn_numbe as the map key
4785  matrix_data_map[m][unknown][eqn_number] += value;
4786  }
4787  }
4788  } //End of loop over matrices
4789  }
4790  }
4791 
4792 #ifdef OOMPH_HAS_MPI
4793  } // endif halo element
4794 #endif
4795 
4796 
4797 #ifdef OOMPH_HAS_MPI
4798  // Time it?
4799  if ((!doing_residuals)&&
4801  {
4802  Elemental_assembly_time[e]=TimingHelpers::timer()-t_assemble_start;
4803  }
4804 #endif
4805 
4806  } //End of loop over the elements
4807 
4808  } //End of map assembly
4809 
4810 
4811 #ifdef OOMPH_HAS_MPI
4812 
4813  // Postprocess timing information and re-allocate distribution of
4814  // elements during subsequent assemblies.
4815  if ((!doing_residuals)&&
4818  {
4820  }
4821 
4822  // We have determined load balancing for current setup.
4823  // This can remain the same until assign_eqn_numbers() is called
4824  // again -- the flag is re-set to true there.
4825  if ((!doing_residuals)&&
4827  {
4829  }
4830 
4831 #endif
4832 
4833 
4834  //-----------Finally we need to convert the beautiful map storage scheme
4835  //------------------------to the containers required by SuperLU
4836 
4837  //Loop over the number of matrices
4838  for(unsigned m=0;m<n_matrix;m++)
4839  {
4840  //Set the number of rows or columns
4841  row_or_column_start[m] = new int[ndof+1];
4842  //Counter for the total number of entries in the storage scheme
4843  unsigned long entry_count=0;
4844  row_or_column_start[m][0] = entry_count;
4845 
4846  // first we compute the number of non-zeros
4847  nnz[m] = 0;
4848  for(unsigned long i_global=0;i_global<ndof;i_global++)
4849  {
4850  nnz[m] += matrix_data_map[m][i_global].size();
4851  }
4852 
4853  // and then resize the storage
4854  column_or_row_index[m] = new int[nnz[m]];
4855  value[m] = new double[nnz[m]];
4856 
4857  //Now we merely loop over the number of rows or columns
4858  for(unsigned long i_global=0;i_global<ndof;i_global++)
4859  {
4860  //Start index for the present row
4861  row_or_column_start[m][i_global] = entry_count;
4862  //If there are no entries in the map then skip the rest of the loop
4863  if(matrix_data_map[m][i_global].empty()) {continue;}
4864 
4865  //Loop over all the entries in the map corresponding to the given
4866  //row or column. It will be ordered
4867 
4868  for(std::map<unsigned,double>::iterator
4869  it = matrix_data_map[m][i_global].begin();
4870  it!=matrix_data_map[m][i_global].end();++it)
4871  {
4872  //The first value is the column or row index
4873  column_or_row_index[m][entry_count] = it->first;
4874  //The second value is the actual data value
4875  value[m][entry_count] = it->second;
4876  //Increase the value of the counter
4877  entry_count++;
4878  }
4879  }
4880 
4881  //Final entry in the row/column start vector
4882  row_or_column_start[m][ndof] = entry_count;
4883  } //End of the loop over the matrices
4884 
4886  {
4887  oomph_info << "Pausing at end of sparse assembly." << std::endl;
4888  pause("Check memory usage now.");
4889  }
4890 }
4891 
4892 
4893 
4894 
4895 
4896 
4897 //=====================================================================
4898 /// This is a (private) helper function that is used to assemble system
4899 /// matrices in compressed row or column format
4900 /// and compute residual vectors using lists
4901 /// The default action is to assemble the jacobian matrix and
4902 /// residuals for the Newton method. The action can be
4903 /// overloaded at an elemental level by chaging the default
4904 /// behaviour of the function Element::get_all_vectors_and_matrices().
4905 /// column_or_row_index: Column [or row] index of given entry
4906 /// row_or_column_start: Index of first entry for given row [or column]
4907 /// value : Vector of nonzero entries
4908 /// residuals : Residual vector
4909 /// compressed_row_flag: Bool flag to indicate if storage format is
4910 /// compressed row [if false interpretation of
4911 /// arguments is as stated in square brackets].
4912 //=====================================================================
4914  Vector<int* > &column_or_row_index,
4915  Vector<int* > &row_or_column_start,
4916  Vector<double* > &value,
4917  Vector<unsigned> &nnz,
4918  Vector<double* > &residuals,
4919  bool compressed_row_flag)
4920 {
4921  //Total number of elements
4922  const unsigned long n_elements = mesh_pt()->nelement();
4923 
4924  // Default range of elements for distributed problems
4925  unsigned long el_lo=0;
4926  unsigned long el_hi=n_elements-1;
4927 
4928 #ifdef OOMPH_HAS_MPI
4929  // Otherwise just loop over a fraction of the elements
4930  // (This will either have been initialised in
4931  // Problem::set_default_first_and_last_element_for_assembly() or
4932  // will have been re-assigned during a previous assembly loop
4933  // Note that following the re-assignment only the entries
4934  // for the current processor are relevant.
4936  {
4937  el_lo=First_el_for_assembly[Communicator_pt->my_rank()];
4938  el_hi=Last_el_plus_one_for_assembly[Communicator_pt->my_rank()]-1;
4939  }
4940 #endif
4941 
4942  // number of dofs
4943  unsigned ndof = this->ndof();
4944 
4945  //Find the number of vectors to be assembled
4946  const unsigned n_vector = residuals.size();
4947 
4948  //Find the number of matrices to be assembled
4949  const unsigned n_matrix = column_or_row_index.size();
4950 
4951  //Locally cache pointer to assembly handler
4953 
4954 #ifdef OOMPH_HAS_MPI
4955  bool doing_residuals=false;
4956  if (dynamic_cast<ParallelResidualsHandler*>(Assembly_handler_pt)!=0)
4957  {
4958  doing_residuals=true;
4959  }
4960 #endif
4961 
4962 //Error check dimensions
4963 #ifdef PARANOID
4964  if(row_or_column_start.size() != n_matrix)
4965  {
4966  std::ostringstream error_stream;
4967  error_stream
4968  << "Error: " << std::endl
4969  << "row_or_column_start.size() " << row_or_column_start.size()
4970  << " does not equal "
4971  << "column_or_row_index.size() "
4972  << column_or_row_index.size() << std::endl;
4973  throw OomphLibError(
4974  error_stream.str(),
4975  OOMPH_CURRENT_FUNCTION,
4976  OOMPH_EXCEPTION_LOCATION);
4977  }
4978 
4979  if(value.size() != n_matrix)
4980  {
4981  std::ostringstream error_stream;
4982  error_stream
4983  << "Error in Problem::sparse_assemble_row_or_column_compressed "
4984  << std::endl
4985  << "value.size() " << value.size() << " does not equal "
4986  << "column_or_row_index.size() "
4987  << column_or_row_index.size() << std::endl<< std::endl
4988  << std::endl;
4989  throw OomphLibError(
4990  error_stream.str(),
4991  OOMPH_CURRENT_FUNCTION,
4992  OOMPH_EXCEPTION_LOCATION);
4993  }
4994 #endif
4995 
4996 //The idea behind this sparse assembly routine is to use a vector of
4997 //lists for the entries in each row or column of the complete matrix.
4998 //The lists contain pairs of entries (global row/column number, value).
4999 //All non-zero contributions from each element are added to the lists.
5000 //We then sort each list by global row/column number and then combine
5001 //the entries corresponding to each row/column before adding to the
5002 //vectors column_or_row_index and value.
5003 
5004 //Note the trade off for "fast assembly" is that we will require
5005 //more memory during the assembly phase. Then again, if we can
5006 //only just assemble the sparse matrix, we're in real trouble.
5007 
5008 //Set up a vector of lists of paired entries of
5009 //(row/column index, jacobian matrix entry).
5010 //The entries of the vector correspond to all the rows or columns.
5011 //The use of the list storage scheme, should give fast insertion
5012 //and fast sorts later.
5014  matrix_data_list(n_matrix);
5015  //Loop over the number of matrices and resize
5016  for(unsigned m=0;m<n_matrix;m++) {matrix_data_list[m].resize(ndof);}
5017 
5018  //Resize the residuals vectors
5019  for(unsigned v=0;v<n_vector;v++)
5020  {
5021  residuals[v] = new double[ndof];
5022  for (unsigned i = 0; i < ndof; i++)
5023  {
5024  residuals[v][i] = 0;
5025  }
5026  }
5027 
5028 #ifdef OOMPH_HAS_MPI
5029 
5030 
5031  // Storage for assembly time for elements
5032  double t_assemble_start=0.0;
5033 
5034  // Storage for assembly times
5035  if ((!doing_residuals)&&
5037  {
5038  Elemental_assembly_time.resize(n_elements);
5039  }
5040 
5041 #endif
5042 
5043  //------------Assemble and populate the lists-----------------------
5044  {
5045  //Allocate local storage for the element's contribution to the
5046  //residuals vectors and system matrices of the size of the maximum
5047  //number of dofs in any element.
5048  //This means that the stored is only allocated (and deleted) once
5049  Vector<Vector<double> > el_residuals(n_vector);
5050  Vector<DenseMatrix<double> > el_jacobian(n_matrix);
5051 
5052 
5053  //Pointer to a single list to be used during the assembly
5054  std::list<std::pair<unsigned,double> > *list_pt;
5055 
5056  //Loop over the all elements
5057  for(unsigned long e=el_lo;e<=el_hi;e++)
5058  {
5059 
5060 #ifdef OOMPH_HAS_MPI
5061  // Time it?
5062  if ((!doing_residuals)&&
5064  {
5065  t_assemble_start=TimingHelpers::timer();
5066  }
5067 #endif
5068 
5069  //Get the pointer to the element
5070  GeneralisedElement* elem_pt = mesh_pt()->element_pt(e);
5071 
5072 #ifdef OOMPH_HAS_MPI
5073  //Ignore halo elements
5074  if (!elem_pt->is_halo())
5075  {
5076 #endif
5077 
5078  //Find number of degrees of freedom in the element
5079  const unsigned nvar = assembly_handler_pt->ndof(elem_pt);
5080 
5081  //Resize the storage for the elemental jacobian and residuals
5082  for(unsigned v=0;v<n_vector;v++) {el_residuals[v].resize(nvar);}
5083  for(unsigned m=0;m<n_matrix;m++) {el_jacobian[m].resize(nvar);}
5084 
5085  //Now get the residuals and jacobian for the element
5086  assembly_handler_pt->
5087  get_all_vectors_and_matrices(elem_pt,el_residuals, el_jacobian);
5088 
5089  //---------------- Insert the values into the lists -----------
5090 
5091  //Loop over the first index of local variables
5092  for(unsigned i=0;i<nvar;i++)
5093  {
5094  //Get the local equation number
5095  unsigned eqn_number
5096  = assembly_handler_pt->eqn_number(elem_pt,i);
5097 
5098  //Add the contribution to the residuals
5099  for(unsigned v=0;v<n_vector;v++)
5100  {
5101  //Fill in the residuals vector
5102  residuals[v][eqn_number] += el_residuals[v][i];
5103  }
5104 
5105  //Now loop over the other index
5106  for(unsigned j=0;j<nvar;j++)
5107  {
5108  //Get the number of the unknown
5109  unsigned unknown = assembly_handler_pt->eqn_number(elem_pt,j);
5110 
5111  //Loop over the matrices
5112  for(unsigned m=0;m<n_matrix;m++)
5113  {
5114  //Get the value of the matrix at this point
5115  double value = el_jacobian[m](i,j);
5116  //Only add to theif it's non-zero
5117  if(std::fabs(value) > Numerical_zero_for_sparse_assembly)
5118  {
5119  //If it's compressed row storage, then our vector is indexed
5120  //by row (the equation number)
5121  if(compressed_row_flag)
5122  {
5123  //Find the list that corresponds to the desired row
5124  list_pt = &matrix_data_list[m][eqn_number];
5125  //Insert the data into the list, the first entry
5126  //in the pair is the unknown (column index),
5127  //the second is the value itself.
5128  list_pt->
5129  insert(list_pt->end(),std::make_pair(unknown,value));
5130  }
5131  //Otherwise it's compressed column storage, and our
5132  //vector is indexed by column (the unknown)
5133  else
5134  {
5135  //Find the list that corresponds to the desired column
5136  list_pt = &matrix_data_list[m][unknown];
5137  //Insert the data into the list, the first entry
5138  //in the pair is the equation number (row index),
5139  //the second is the value itself.
5140  list_pt->
5141  insert(list_pt->end(),std::make_pair(eqn_number,value));
5142  }
5143  }
5144  }
5145  }
5146  }
5147 
5148 #ifdef OOMPH_HAS_MPI
5149  } // endif halo element
5150 #endif
5151 
5152 
5153 #ifdef OOMPH_HAS_MPI
5154  // Time it?
5155  if ((!doing_residuals)&&
5157  {
5158  Elemental_assembly_time[e]=TimingHelpers::timer()-t_assemble_start;
5159  }
5160 #endif
5161 
5162  } //End of loop over the elements
5163 
5164  } //list_pt goes out of scope
5165 
5166 
5167 #ifdef OOMPH_HAS_MPI
5168 
5169  // Postprocess timing information and re-allocate distribution of
5170  // elements during subsequent assemblies.
5171  if ((!doing_residuals)&&
5174  {
5176  }
5177 
5178  // We have determined load balancing for current setup.
5179  // This can remain the same until assign_eqn_numbers() is called
5180  // again -- the flag is re-set to true there.
5181  if ((!doing_residuals)&&
5183  {
5185  }
5186 
5187 #endif
5188 
5189 
5190  //----Finally we need to convert the beautiful list storage scheme---
5191  //----------to the containers required by SuperLU--------------------
5192 
5193  //Loop over the number of matrices
5194  for(unsigned m=0;m<n_matrix;m++)
5195  {
5196  //Set the number of rows or columns
5197  row_or_column_start[m] = new int[ndof+1];
5198  //Counter for the total number of entries in the storage scheme
5199  unsigned long entry_count=0;
5200  //The first entry is 0
5201  row_or_column_start[m][0] = entry_count;
5202 
5203  // first we compute the number of non-zeros
5204  nnz[m] = 0;
5205  for(unsigned long i_global=0;i_global<ndof;i_global++)
5206  {
5207  nnz[m] += matrix_data_list[m][i_global].size();
5208  }
5209 
5210  // and then resize the storage
5211  column_or_row_index[m] = new int[nnz[m]];
5212  value[m] = new double[nnz[m]];
5213 
5214  //Now we merely loop over the number of rows or columns
5215  for(unsigned long i_global=0;i_global<ndof;i_global++)
5216  {
5217  //Start index for the present row is the number of entries so far
5218  row_or_column_start[m][i_global] = entry_count;
5219  //If there are no entries in the list then skip the loop
5220  if(matrix_data_list[m][i_global].empty()) {continue;}
5221 
5222  //Sort the list corresponding to this row or column by the
5223  //column or row index (first entry in the pair).
5224  //This might be inefficient, but we only have to do the sort ONCE
5225  //for each list. This is faster than using a map storage scheme, where
5226  //we are sorting for every insertion (although the map structure
5227  //is cleaner and more memory efficient)
5228  matrix_data_list[m][i_global].sort();
5229 
5230  //Set up an iterator for start of the list
5231  std::list<std::pair<unsigned,double> >::iterator it
5232  = matrix_data_list[m][i_global].begin();
5233 
5234  //Get the first row or column index in the list...
5235  unsigned current_index = it->first;
5236  //...and the corresponding value
5237  double current_value = it->second;
5238 
5239  //Loop over all the entries in the sorted list
5240  //Increase the iterator so that we start at the second entry
5241  for(++it;it!=matrix_data_list[m][i_global].end();++it)
5242  {
5243  //If the index has not changed, then we must add the contribution
5244  //of the present entry to the value.
5245  //Additionally check that the entry is non-zero
5246  if((it->first == current_index) &&
5247  (std::fabs(it->second) > Numerical_zero_for_sparse_assembly))
5248  {
5249  current_value += it->second;
5250  }
5251  //Otherwise, we have added all the contributions to the index
5252  //to current_value, so add it to the SuperLU data structure
5253  else
5254  {
5255  //Add the row or column index to the vector
5256  column_or_row_index[m][entry_count] = current_index;
5257  //Add the actual value to the vector
5258  value[m][entry_count] = current_value;
5259  //Increase the counter for the number of entries in each vector
5260  entry_count++;
5261 
5262  //Set the index and value to be those of the current entry in the
5263  //list
5264  current_index = it->first;
5265  current_value = it->second;
5266  }
5267  } //End of loop over all list entries for this global row or column
5268 
5269  //There are TWO special cases to consider.
5270  //If there is only one equation number in the list, then it
5271  //will NOT have been added. We test this case by comparing the
5272  //number of entries with those stored in row_or_column_start[i_global]
5273  //Otherwise
5274  //If the final entry in the list has the same index as the penultimate
5275  //entry, then it will NOT have been added to the SuperLU storage scheme
5276  //Check this by comparing the current_index with the final index
5277  //stored in the SuperLU scheme. If they are not the same, then
5278  //add the current_index and value.
5279 
5280  //If single equation number in list
5281  if((static_cast<int>(entry_count) == row_or_column_start[m][i_global])
5282  //If we have a single equation number, this will not be evaluated.
5283  //If we don't then we do the test to check that the final
5284  //entry is added
5285  ||(static_cast<int>(current_index) !=
5286  column_or_row_index[m][entry_count-1]))
5287  {
5288  //Add the row or column index to the vector
5289  column_or_row_index[m][entry_count] = current_index;
5290  //Add the actual value to the vector
5291  value[m][entry_count] = current_value;
5292  //Increase the counter for the number of entries in each vector
5293  entry_count++;
5294  }
5295 
5296  } //End of loop over the rows or columns of the entire matrix
5297 
5298  //Final entry in the row/column start vector
5299  row_or_column_start[m][ndof] = entry_count;
5300  } //End of loop over matrices
5301 
5303  {
5304  oomph_info << "Pausing at end of sparse assembly." << std::endl;
5305  pause("Check memory usage now.");
5306  }
5307 
5308 }
5309 
5310 
5311 
5312 //=====================================================================
5313 /// This is a (private) helper function that is used to assemble system
5314 /// matrices in compressed row or column format
5315 /// and compute residual vectors using vectors of pairs
5316 /// The default action is to assemble the jacobian matrix and
5317 /// residuals for the Newton method. The action can be
5318 /// overloaded at an elemental level by chaging the default
5319 /// behaviour of the function Element::get_all_vectors_and_matrices().
5320 /// column_or_row_index: Column [or row] index of given entry
5321 /// row_or_column_start: Index of first entry for given row [or column]
5322 /// value : Vector of nonzero entries
5323 /// residuals : Residual vector
5324 /// compressed_row_flag: Bool flag to indicate if storage format is
5325 /// compressed row [if false interpretation of
5326 /// arguments is as stated in square brackets].
5327 //=====================================================================
5329  Vector<int* > &column_or_row_index,
5330  Vector<int* > &row_or_column_start,
5331  Vector<double* > &value,
5332  Vector<unsigned> &nnz,
5333  Vector<double*> &residuals,
5334  bool compressed_row_flag)
5335 {
5336  //Total number of elements
5337  const unsigned long n_elements = mesh_pt()->nelement();
5338 
5339  // Default range of elements for distributed problems
5340  unsigned long el_lo=0;
5341  unsigned long el_hi=n_elements-1;
5342 
5343 #ifdef OOMPH_HAS_MPI
5344  // Otherwise just loop over a fraction of the elements
5345  // (This will either have been initialised in
5346  // Problem::set_default_first_and_last_element_for_assembly() or
5347  // will have been re-assigned during a previous assembly loop
5348  // Note that following the re-assignment only the entries
5349  // for the current processor are relevant.
5351  {
5352  el_lo=First_el_for_assembly[Communicator_pt->my_rank()];
5353  el_hi=Last_el_plus_one_for_assembly[Communicator_pt->my_rank()]-1;
5354  }
5355 #endif
5356 
5357  // number of local eqns
5358  unsigned ndof = this->ndof();
5359 
5360  //Find the number of vectors to be assembled
5361  const unsigned n_vector = residuals.size();
5362 
5363  //Find the number of matrices to be assembled
5364  const unsigned n_matrix = column_or_row_index.size();
5365 
5366  //Locally cache pointer to assembly handler
5368 
5369 #ifdef OOMPH_HAS_MPI
5370  bool doing_residuals=false;
5371  if (dynamic_cast<ParallelResidualsHandler*>(Assembly_handler_pt)!=0)
5372  {
5373  doing_residuals=true;
5374  }
5375 #endif
5376 
5377 //Error check dimensions
5378 #ifdef PARANOID
5379  if(row_or_column_start.size() != n_matrix)
5380  {
5381  std::ostringstream error_stream;
5382  error_stream
5383  << "Error: " << std::endl
5384  << "row_or_column_start.size() " << row_or_column_start.size()
5385  << " does not equal "
5386  << "column_or_row_index.size() "
5387  << column_or_row_index.size() << std::endl;
5388  throw OomphLibError(
5389  error_stream.str(),
5390  OOMPH_CURRENT_FUNCTION,
5391  OOMPH_EXCEPTION_LOCATION);
5392  }
5393 
5394  if(value.size() != n_matrix)
5395  {
5396  std::ostringstream error_stream;
5397  error_stream
5398  << "Error: "
5399  << std::endl
5400  << "value.size() " << value.size() << " does not equal "
5401  << "column_or_row_index.size() "
5402  << column_or_row_index.size() << std::endl<< std::endl
5403  << std::endl;
5404  throw OomphLibError(
5405  error_stream.str(),
5406  OOMPH_CURRENT_FUNCTION,
5407  OOMPH_EXCEPTION_LOCATION);
5408  }
5409 #endif
5410 
5411 
5412 // The idea behind this sparse assembly routine is to use a Vector of
5413 // Vectors of pairs for each complete matrix.
5414 // Each inner Vector stores pairs and holds the row (or column) index
5415 // and the value of the matrix entry.
5416 
5417 // Set up Vector of Vectors to store the entries of each matrix,
5418 // indexed by either the column or row.
5419  Vector<Vector< Vector<std::pair<unsigned,double> > > > matrix_data(n_matrix);
5420 
5421  //Loop over the number of matrices being assembled and resize
5422  //each Vector of Vectors to the number of rows or columns of the matrix
5423  for(unsigned m=0;m<n_matrix;m++) {matrix_data[m].resize(ndof);}
5424 
5425  //Resize the residuals vectors
5426  for(unsigned v=0;v<n_vector;v++)
5427  {
5428  residuals[v] = new double[ndof];
5429  for (unsigned i = 0; i < ndof; i++)
5430  {
5431  residuals[v][i] = 0;
5432  }
5433  }
5434 
5435 #ifdef OOMPH_HAS_MPI
5436 
5437  // Storage for assembly time for elements
5438  double t_assemble_start=0.0;
5439 
5440  // Storage for assembly times
5441  if ((!doing_residuals)&&
5443  {
5444  Elemental_assembly_time.resize(n_elements);
5445  }
5446 
5447 #endif
5448 
5449  //----------------Assemble and populate the vector storage scheme--------
5450  {
5451  //Allocate local storage for the element's contribution to the
5452  //residuals vectors and system matrices of the size of the maximum
5453  //number of dofs in any element
5454  //This means that the storage is only allocated (and deleted) once
5455  Vector<Vector<double> > el_residuals(n_vector);
5456  Vector<DenseMatrix<double> > el_jacobian(n_matrix);
5457 
5458  //Loop over the elements
5459  for(unsigned long e=el_lo;e<=el_hi;e++)
5460  {
5461 
5462 #ifdef OOMPH_HAS_MPI
5463  // Time it?
5464  if ((!doing_residuals)&&
5466  {
5467  t_assemble_start=TimingHelpers::timer();
5468  }
5469 #endif
5470 
5471  //Get the pointer to the element
5472  GeneralisedElement* elem_pt = mesh_pt()->element_pt(e);
5473 
5474 #ifdef OOMPH_HAS_MPI
5475  //Ignore halo elements
5476  if (!elem_pt->is_halo())
5477  {
5478 #endif
5479 
5480  //Find number of degrees of freedom in the element
5481  const unsigned nvar = assembly_handler_pt->ndof(elem_pt);
5482 
5483  //Resize the storage for elemental jacobian and residuals
5484  for(unsigned v=0;v<n_vector;v++) {el_residuals[v].resize(nvar);}
5485  for(unsigned m=0;m<n_matrix;m++) {el_jacobian[m].resize(nvar);}
5486 
5487  //Now get the residuals and jacobian for the element
5488  assembly_handler_pt->
5489  get_all_vectors_and_matrices(elem_pt,el_residuals, el_jacobian);
5490 
5491  //---------------Insert the values into the vectors--------------
5492 
5493  //Loop over the first index of local variables
5494  for(unsigned i=0;i<nvar;i++)
5495  {
5496 
5497  //Get the local equation number
5498  unsigned eqn_number
5499  = assembly_handler_pt->eqn_number(elem_pt,i);
5500 
5501  //Add the contribution to the residuals
5502  for(unsigned v=0;v<n_vector;v++)
5503  {
5504  //Fill in each residuals vector
5505  residuals[v][eqn_number] += el_residuals[v][i];
5506  }
5507 
5508  //Now loop over the other index
5509  for(unsigned j=0;j<nvar;j++)
5510  {
5511  //Get the number of the unknown
5512  unsigned unknown = assembly_handler_pt->eqn_number(elem_pt,j);
5513 
5514  //Loop over the matrices
5515  //If it's compressed row storage, then our vector of maps
5516  //is indexed by row (equation number)
5517  for(unsigned m=0;m<n_matrix;m++)
5518  {
5519  //Get the value of the matrix at this point
5520  double value = el_jacobian[m](i,j);
5521  //Only bother to add to the vector if it's non-zero
5522  if(std::fabs(value) > Numerical_zero_for_sparse_assembly)
5523  {
5524  //If it's compressed row storage, then our vector of maps
5525  //is indexed by row (equation number)
5526  if(compressed_row_flag)
5527  {
5528  //Find the correct position and add the data into the vectors
5529  const unsigned size = matrix_data[m][eqn_number].size();
5530  for(unsigned k=0; k<=size; k++)
5531  {
5532  if(k==size)
5533  {
5534  matrix_data[m][eqn_number].push_back(
5535  std::make_pair(unknown,value));
5536  break;
5537  }
5538  else if(matrix_data[m][eqn_number][k].first == unknown)
5539  {
5540  matrix_data[m][eqn_number][k].second += value;
5541  break;
5542  }
5543  }
5544  }
5545  //Otherwise it's compressed column storage and our vector is
5546  //indexed by column (the unknown)
5547  else
5548  {
5549  //Add the data into the vectors in the correct position
5550  const unsigned size = matrix_data[m][unknown].size();
5551  for(unsigned k=0; k<=size; k++)
5552  {
5553  if(k==size)
5554  {
5555  matrix_data[m][unknown].push_back(
5556  std::make_pair(eqn_number,value));
5557  break;
5558  }
5559  else if(matrix_data[m][unknown][k].first == eqn_number)
5560  {
5561  matrix_data[m][unknown][k].second += value;
5562  break;
5563  }
5564  }
5565  }
5566  }
5567  } //End of loop over matrices
5568  }
5569  }
5570 
5571 #ifdef OOMPH_HAS_MPI
5572  } // endif halo element
5573 #endif
5574 
5575 
5576 #ifdef OOMPH_HAS_MPI
5577  // Time it?
5578  if ((!doing_residuals)&&
5580  {
5581  Elemental_assembly_time[e]=TimingHelpers::timer()-t_assemble_start;
5582  }
5583 #endif
5584 
5585  } //End of loop over the elements
5586 
5587 
5588  } //End of vector assembly
5589 
5590 
5591 
5592 #ifdef OOMPH_HAS_MPI
5593 
5594  // Postprocess timing information and re-allocate distribution of
5595  // elements during subsequent assemblies.
5596  if ((!doing_residuals)&&
5599  {
5601  }
5602 
5603  // We have determined load balancing for current setup.
5604  // This can remain the same until assign_eqn_numbers() is called
5605  // again -- the flag is re-set to true there.
5606  if ((!doing_residuals)&&
5608  {
5610  }
5611 
5612 #endif
5613 
5614 
5615  //-----------Finally we need to convert this vector storage scheme
5616  //------------------------to the containers required by SuperLU
5617 
5618  //Loop over the number of matrices
5619  for(unsigned m=0;m<n_matrix;m++)
5620  {
5621  //Set the number of rows or columns
5622  row_or_column_start[m] = new int[ndof+1];
5623 
5624  // fill row_or_column_start and find the number of entries
5625  row_or_column_start[m][0] = 0;
5626  for(unsigned long i=0;i<ndof;i++)
5627  {
5628  row_or_column_start[m][i+1] = row_or_column_start[m][i]
5629  + matrix_data[m][i].size();
5630  }
5631  const unsigned entries = row_or_column_start[m][ndof];
5632 
5633  // resize vectors
5634  column_or_row_index[m] = new int[entries];
5635  value[m] = new double[entries];
5636  nnz[m] = entries;
5637 
5638  //Now we merely loop over the number of rows or columns
5639  for(unsigned long i_global=0;i_global<ndof;i_global++)
5640  {
5641  //If there are no entries in the vector then skip the rest of the loop
5642  if(matrix_data[m][i_global].empty()) {continue;}
5643 
5644  //Loop over all the entries in the vectors corresponding to the given
5645  //row or column. It will NOT be ordered
5646  unsigned p = 0;
5647  for(int j=row_or_column_start[m][i_global];
5648  j<row_or_column_start[m][i_global+1]; j++)
5649  {
5650  column_or_row_index[m][j] = matrix_data[m][i_global][p].first;
5651  value[m][j] = matrix_data[m][i_global][p].second;
5652  ++p;
5653  }
5654  }
5655  } //End of the loop over the matrices
5656 
5658  {
5659  oomph_info << "Pausing at end of sparse assembly." << std::endl;
5660  pause("Check memory usage now.");
5661  }
5662 }
5663 
5664 
5665 
5666 
5667 
5668 
5669 
5670 
5671 //=====================================================================
5672 /// This is a (private) helper function that is used to assemble system
5673 /// matrices in compressed row or column format
5674 /// and compute residual vectors using two vectors.
5675 /// The default action is to assemble the jacobian matrix and
5676 /// residuals for the Newton method. The action can be
5677 /// overloaded at an elemental level by chaging the default
5678 /// behaviour of the function Element::get_all_vectors_and_matrices().
5679 /// column_or_row_index: Column [or row] index of given entry
5680 /// row_or_column_start: Index of first entry for given row [or column]
5681 /// value : Vector of nonzero entries
5682 /// residuals : Residual vector
5683 /// compressed_row_flag: Bool flag to indicate if storage format is
5684 /// compressed row [if false interpretation of
5685 /// arguments is as stated in square brackets].
5686 //=====================================================================
5688  Vector<int* > &column_or_row_index,
5689  Vector<int* > &row_or_column_start,
5690  Vector<double* > &value,
5691  Vector<unsigned> &nnz,
5692  Vector<double* > &residuals,
5693  bool compressed_row_flag)
5694 {
5695  //Total number of elements
5696  const unsigned long n_elements = mesh_pt()->nelement();
5697 
5698  // Default range of elements for distributed problems
5699  unsigned long el_lo=0;
5700  unsigned long el_hi=n_elements-1;
5701 
5702 
5703 #ifdef OOMPH_HAS_MPI
5704  // Otherwise just loop over a fraction of the elements
5705  // (This will either have been initialised in
5706  // Problem::set_default_first_and_last_element_for_assembly() or
5707  // will have been re-assigned during a previous assembly loop
5708  // Note that following the re-assignment only the entries
5709  // for the current processor are relevant.
5711  {
5712  el_lo=First_el_for_assembly[Communicator_pt->my_rank()];
5713  el_hi=Last_el_plus_one_for_assembly[Communicator_pt->my_rank()]-1;
5714  }
5715 #endif
5716 
5717  // number of local eqns
5718  unsigned ndof = this->ndof();
5719 
5720  //Find the number of vectors to be assembled
5721  const unsigned n_vector = residuals.size();
5722 
5723  //Find the number of matrices to be assembled
5724  const unsigned n_matrix = column_or_row_index.size();
5725 
5726  //Locally cache pointer to assembly handler
5728 
5729 #ifdef OOMPH_HAS_MPI
5730  bool doing_residuals=false;
5731  if (dynamic_cast<ParallelResidualsHandler*>(Assembly_handler_pt)!=0)
5732  {
5733  doing_residuals=true;
5734  }
5735 #endif
5736 
5737 //Error check dimensions
5738 #ifdef PARANOID
5739  if(row_or_column_start.size() != n_matrix)
5740  {
5741  std::ostringstream error_stream;
5742  error_stream
5743  << "Error: " << std::endl
5744  << "row_or_column_start.size() " << row_or_column_start.size()
5745  << " does not equal "
5746  << "column_or_row_index.size() "
5747  << column_or_row_index.size() << std::endl;
5748  throw OomphLibError(
5749  error_stream.str(),
5750  OOMPH_CURRENT_FUNCTION,
5751  OOMPH_EXCEPTION_LOCATION);
5752  }
5753 
5754  if(value.size() != n_matrix)
5755  {
5756  std::ostringstream error_stream;
5757  error_stream
5758  << "Error: "
5759  << std::endl
5760  << "value.size() " << value.size() << " does not equal "
5761  << "column_or_row_index.size() "
5762  << column_or_row_index.size() << std::endl<< std::endl
5763  << std::endl;
5764  throw OomphLibError(
5765  error_stream.str(),
5766  OOMPH_CURRENT_FUNCTION,
5767  OOMPH_EXCEPTION_LOCATION);
5768  }
5769 #endif
5770 
5771 // The idea behind this sparse assembly routine is to use Vectors of
5772 // Vectors for the entries in each complete matrix. And a second
5773 // Vector of Vectors stores the global row (or column) indeces. This
5774 // will not have the memory overheads associated with the methods using
5775 // lists or maps, but insertion will be more costly.
5776 
5777 // Set up two vector of vectors to store the entries of each matrix,
5778 // indexed by either the column or row. The entries of the vector for
5779 // each matrix correspond to all the rows or columns of that matrix.
5780  Vector<Vector<Vector<unsigned> > > matrix_row_or_col_indices(n_matrix);
5781  Vector<Vector<Vector<double> > > matrix_values(n_matrix);
5782 
5783  //Loop over the number of matrices being assembled and resize
5784  //each vector of vectors to the number of rows or columns of the matrix
5785  for(unsigned m=0;m<n_matrix;m++)
5786  {
5787  matrix_row_or_col_indices[m].resize(ndof);
5788  matrix_values[m].resize(ndof);
5789  }
5790 
5791  //Resize the residuals vectors
5792  for(unsigned v=0;v<n_vector;v++)
5793  {
5794  residuals[v] = new double[ndof];
5795  for (unsigned i = 0; i < ndof; i++)
5796  {
5797  residuals[v][i] = 0;
5798  }
5799  }
5800 
5801 #ifdef OOMPH_HAS_MPI
5802 
5803  // Storage for assembly time for elements
5804  double t_assemble_start=0.0;
5805 
5806  // Storage for assembly times
5807  if ((!doing_residuals)&&
5809  {
5810  Elemental_assembly_time.resize(n_elements);
5811  }
5812 
5813 #endif
5814 
5815 
5816  //----------------Assemble and populate the vector storage scheme-------
5817  {
5818  //Allocate local storage for the element's contribution to the
5819  //residuals vectors and system matrices of the size of the maximum
5820  //number of dofs in any element
5821  //This means that the storage will only be allocated (and deleted) once
5822  Vector<Vector<double> > el_residuals(n_vector);
5823  Vector<DenseMatrix<double> > el_jacobian(n_matrix);
5824 
5825  //Loop over the elements
5826  for(unsigned long e=el_lo;e<=el_hi;e++)
5827  {
5828 
5829 #ifdef OOMPH_HAS_MPI
5830  // Time it?
5831  if ((!doing_residuals)&&
5833  {
5834  t_assemble_start=TimingHelpers::timer();
5835  }
5836 #endif
5837 
5838  //Get the pointer to the element
5839  GeneralisedElement* elem_pt = mesh_pt()->element_pt(e);
5840 
5841 #ifdef OOMPH_HAS_MPI
5842  //Ignore halo elements
5843  if (!elem_pt->is_halo())
5844  {
5845 #endif
5846 
5847  //Find number of degrees of freedom in the element
5848  const unsigned nvar = assembly_handler_pt->ndof(elem_pt);
5849 
5850  //Resize the storage for elemental jacobian and residuals
5851  for(unsigned v=0;v<n_vector;v++) {el_residuals[v].resize(nvar);}
5852  for(unsigned m=0;m<n_matrix;m++) {el_jacobian[m].resize(nvar);}
5853 
5854  //Now get the residuals and jacobian for the element
5855  assembly_handler_pt->
5856  get_all_vectors_and_matrices(elem_pt,el_residuals, el_jacobian);
5857 
5858  //---------------Insert the values into the vectors--------------
5859 
5860  //Loop over the first index of local variables
5861  for(unsigned i=0;i<nvar;i++)
5862  {
5863  //Get the local equation number
5864  unsigned eqn_number
5865  = assembly_handler_pt->eqn_number(elem_pt,i);
5866 
5867  //Add the contribution to the residuals
5868  for(unsigned v=0;v<n_vector;v++)
5869  {
5870  //Fill in each residuals vector
5871  residuals[v][eqn_number] += el_residuals[v][i];
5872  }
5873 
5874  //Now loop over the other index
5875  for(unsigned j=0;j<nvar;j++)
5876  {
5877  //Get the number of the unknown
5878  unsigned unknown = assembly_handler_pt->eqn_number(elem_pt,j);
5879 
5880  //Loop over the matrices
5881  //If it's compressed row storage, then our vector of maps
5882  //is indexed by row (equation number)
5883  for(unsigned m=0;m<n_matrix;m++)
5884  {
5885  //Get the value of the matrix at this point
5886  double value = el_jacobian[m](i,j);
5887  //Only bother to add to the vector if it's non-zero
5888  if(std::fabs(value) > Numerical_zero_for_sparse_assembly)
5889  {
5890  //If it's compressed row storage, then our vector of maps
5891  //is indexed by row (equation number)
5892  if(compressed_row_flag)
5893  {
5894  //Find the correct position and add the data into the vectors
5895  const unsigned size =
5896  matrix_row_or_col_indices[m][eqn_number].size();
5897 
5898  for(unsigned k=0; k<=size; k++)
5899  {
5900  if(k==size)
5901  {
5902  matrix_row_or_col_indices[m][eqn_number].
5903  push_back(unknown);
5904  matrix_values[m][eqn_number].push_back(value);
5905  break;
5906  }
5907  else if(matrix_row_or_col_indices[m][eqn_number][k] ==
5908  unknown)
5909  {
5910  matrix_values[m][eqn_number][k] += value;
5911  break;
5912  }
5913  }
5914  }
5915  //Otherwise it's compressed column storage and our vector is
5916  //indexed by column (the unknown)
5917  else
5918  {
5919  //Add the data into the vectors in the correct position
5920  const unsigned size =
5921  matrix_row_or_col_indices[m][unknown].size();
5922  for (unsigned k=0; k<=size; k++)
5923  {
5924  if (k==size)
5925  {
5926  matrix_row_or_col_indices[m][unknown].
5927  push_back(eqn_number);
5928  matrix_values[m][unknown].push_back(value);
5929  break;
5930  }
5931  else if (matrix_row_or_col_indices[m][unknown][k] ==
5932  eqn_number)
5933  {
5934  matrix_values[m][unknown][k] += value;
5935  break;
5936  }
5937  }
5938  }
5939  }
5940  } //End of loop over matrices
5941  }
5942  }
5943 
5944 #ifdef OOMPH_HAS_MPI
5945  } // endif halo element
5946 #endif
5947 
5948 
5949 #ifdef OOMPH_HAS_MPI
5950  // Time it?
5951  if ((!doing_residuals)&&
5953  {
5954  Elemental_assembly_time[e]=TimingHelpers::timer()-t_assemble_start;
5955  }
5956 #endif
5957 
5958  } //End of loop over the elements
5959 
5960  } //End of vector assembly
5961 
5962 
5963 
5964 #ifdef OOMPH_HAS_MPI
5965 
5966  // Postprocess timing information and re-allocate distribution of
5967  // elements during subsequent assemblies.
5968  if ((!doing_residuals)&&
5971  {
5973  }
5974 
5975  // We have determined load balancing for current setup.
5976  // This can remain the same until assign_eqn_numbers() is called
5977  // again -- the flag is re-set to true there.
5978  if ((!doing_residuals)&&
5980  {
5982  }
5983 
5984 #endif
5985 
5986  //-----------Finally we need to convert this lousy vector storage scheme
5987  //------------------------to the containers required by SuperLU
5988 
5989  //Loop over the number of matrices
5990  for(unsigned m=0;m<n_matrix;m++)
5991  {
5992  //Set the number of rows or columns
5993  row_or_column_start[m] = new int[ndof+1];
5994 
5995  // fill row_or_column_start and find the number of entries
5996  row_or_column_start[m][0] = 0;
5997  for (unsigned long i=0;i<ndof;i++)
5998  {
5999  row_or_column_start[m][i+1] = row_or_column_start[m][i]
6000  + matrix_values[m][i].size();
6001  }
6002  const unsigned entries = row_or_column_start[m][ndof];
6003 
6004  // resize vectors
6005  column_or_row_index[m] = new int[entries];
6006  value[m] = new double[entries];
6007  nnz[m] = entries;
6008 
6009  //Now we merely loop over the number of rows or columns
6010  for(unsigned long i_global=0;i_global<ndof;i_global++)
6011  {
6012  //If there are no entries in the vector then skip the rest of the loop
6013  if(matrix_values[m][i_global].empty()) {continue;}
6014 
6015  //Loop over all the entries in the vectors corresponding to the given
6016  //row or column. It will NOT be ordered
6017  unsigned p = 0;
6018  for(int j=row_or_column_start[m][i_global];
6019  j<row_or_column_start[m][i_global+1]; j++)
6020  {
6021  column_or_row_index[m][j] = matrix_row_or_col_indices[m][i_global][p];
6022  value[m][j] = matrix_values[m][i_global][p];
6023  ++p;
6024  }
6025  }
6026  } //End of the loop over the matrices
6027 
6029  {
6030  oomph_info << "Pausing at end of sparse assembly." << std::endl;
6031  pause("Check memory usage now.");
6032  }
6033 
6034 }
6035 
6036 
6037 //=====================================================================
6038 /// This is a (private) helper function that is used to assemble system
6039 /// matrices in compressed row or column format
6040 /// and compute residual vectors using two vectors.
6041 /// The default action is to assemble the jacobian matrix and
6042 /// residuals for the Newton method. The action can be
6043 /// overloaded at an elemental level by chaging the default
6044 /// behaviour of the function Element::get_all_vectors_and_matrices().
6045 /// column_or_row_index: Column [or row] index of given entry
6046 /// row_or_column_start: Index of first entry for given row [or column]
6047 /// value : Vector of nonzero entries
6048 /// residuals : Residual vector
6049 /// compressed_row_flag: Bool flag to indicate if storage format is
6050 /// compressed row [if false interpretation of
6051 /// arguments is as stated in square brackets].
6052 //=====================================================================
6054  Vector<int* > &column_or_row_index,
6055  Vector<int* > &row_or_column_start,
6056  Vector<double* > &value,
6057  Vector<unsigned> &nnz,
6058  Vector<double* > &residuals,
6059  bool compressed_row_flag)
6060 {
6061 
6062  //Total number of elements
6063  const unsigned long n_elements = mesh_pt()->nelement();
6064 
6065  // Default range of elements for distributed problems
6066  unsigned long el_lo=0;
6067  unsigned long el_hi=n_elements-1;
6068 
6069 
6070 #ifdef OOMPH_HAS_MPI
6071  // Otherwise just loop over a fraction of the elements
6072  // (This will either have been initialised in
6073  // Problem::set_default_first_and_last_element_for_assembly() or
6074  // will have been re-assigned during a previous assembly loop
6075  // Note that following the re-assignment only the entries
6076  // for the current processor are relevant.
6078  {
6079  el_lo=First_el_for_assembly[Communicator_pt->my_rank()];
6080  el_hi=Last_el_plus_one_for_assembly[Communicator_pt->my_rank()]-1;
6081  }
6082 #endif
6083 
6084  // number of local eqns
6085  unsigned ndof = this->ndof();
6086 
6087  //Find the number of vectors to be assembled
6088  const unsigned n_vector = residuals.size();
6089 
6090  //Find the number of matrices to be assembled
6091  const unsigned n_matrix = column_or_row_index.size();
6092 
6093  //Locally cache pointer to assembly handler
6095 
6096 #ifdef OOMPH_HAS_MPI
6097  bool doing_residuals=false;
6098  if (dynamic_cast<ParallelResidualsHandler*>(Assembly_handler_pt)!=0)
6099  {
6100  doing_residuals=true;
6101  }
6102 #endif
6103 
6104 //Error check dimensions
6105 #ifdef PARANOID
6106  if(row_or_column_start.size() != n_matrix)
6107  {
6108  std::ostringstream error_stream;
6109  error_stream
6110  << "Error: " << std::endl
6111  << "row_or_column_start.size() " << row_or_column_start.size()
6112  << " does not equal "
6113  << "column_or_row_index.size() "
6114  << column_or_row_index.size() << std::endl;
6115  throw OomphLibError(
6116  error_stream.str(),
6117  OOMPH_CURRENT_FUNCTION,
6118  OOMPH_EXCEPTION_LOCATION);
6119  }
6120 
6121  if(value.size() != n_matrix)
6122  {
6123  std::ostringstream error_stream;
6124  error_stream
6125  << "Error: "
6126  << std::endl
6127  << "value.size() " << value.size() << " does not equal "
6128  << "column_or_row_index.size() "
6129  << column_or_row_index.size() << std::endl<< std::endl
6130  << std::endl;
6131  throw OomphLibError(
6132  error_stream.str(),
6133  OOMPH_CURRENT_FUNCTION,
6134  OOMPH_EXCEPTION_LOCATION);
6135  }
6136 #endif
6137 
6138 // The idea behind this sparse assembly routine is to use Vectors of
6139 // Vectors for the entries in each complete matrix. And a second
6140 // Vector of Vectors stores the global row (or column) indeces. This
6141 // will not have the memory overheads associated with the methods using
6142 // lists or maps, but insertion will be more costly.
6143 
6144 // Set up two vector of vectors to store the entries of each matrix,
6145 // indexed by either the column or row. The entries of the vector for
6146 // each matrix correspond to all the rows or columns of that matrix.
6147  Vector<unsigned**> matrix_row_or_col_indices(n_matrix);
6148  Vector<double**> matrix_values(n_matrix);
6149 
6150  //Loop over the number of matrices being assembled and resize
6151  //each vector of vectors to the number of rows or columns of the matrix
6152  for(unsigned m=0;m<n_matrix;m++)
6153  {
6154  matrix_row_or_col_indices[m] = new unsigned*[ndof];
6155  matrix_values[m] = new double*[ndof];
6156  }
6157 
6158  //Resize the residuals vectors
6159  for(unsigned v=0;v<n_vector;v++)
6160  {
6161  residuals[v] = new double[ndof];
6162  for (unsigned i = 0; i < ndof; i++)
6163  {
6164  residuals[v][i] = 0;
6165  }
6166  }
6167 
6168 #ifdef OOMPH_HAS_MPI
6169 
6170  // Storage for assembly time for elements
6171  double t_assemble_start=0.0;
6172 
6173  // Storage for assembly times
6174  if ((!doing_residuals)&&
6176  {
6177  Elemental_assembly_time.resize(n_elements);
6178  }
6179 
6180 #endif
6181 
6182  // number of coefficients in each row
6183  Vector<Vector<unsigned> > ncoef(n_matrix);
6184  for (unsigned m = 0; m < n_matrix; m++)
6185  {
6186  ncoef[m].resize(ndof,0);
6187  }
6188 
6190  {
6192  for (unsigned m = 0; m < n_matrix; m++)
6193  {
6195  }
6196  }
6197 
6198  //----------------Assemble and populate the vector storage scheme-------
6199  {
6200  //Allocate local storage for the element's contribution to the
6201  //residuals vectors and system matrices of the size of the maximum
6202  //number of dofs in any element
6203  //This means that the storage will only be allocated (and deleted) once
6204  Vector<Vector<double> > el_residuals(n_vector);
6205  Vector<DenseMatrix<double> > el_jacobian(n_matrix);
6206 
6207  //Loop over the elements
6208  for(unsigned long e=el_lo;e<=el_hi;e++)
6209  {
6210 
6211 #ifdef OOMPH_HAS_MPI
6212  // Time it?
6213  if ((!doing_residuals)&&
6215  {
6216  t_assemble_start=TimingHelpers::timer();
6217  }
6218 #endif
6219 
6220  //Get the pointer to the element
6221  GeneralisedElement* elem_pt = mesh_pt()->element_pt(e);
6222 
6223 #ifdef OOMPH_HAS_MPI
6224  //Ignore halo elements
6225  if (!elem_pt->is_halo())
6226  {
6227 #endif
6228 
6229  //Find number of degrees of freedom in the element
6230  const unsigned nvar = assembly_handler_pt->ndof(elem_pt);
6231 
6232  //Resize the storage for elemental jacobian and residuals
6233  for(unsigned v=0;v<n_vector;v++) {el_residuals[v].resize(nvar);}
6234  for(unsigned m=0;m<n_matrix;m++) {el_jacobian[m].resize(nvar);}
6235 
6236  //Now get the residuals and jacobian for the element
6237  assembly_handler_pt->
6238  get_all_vectors_and_matrices(elem_pt,el_residuals, el_jacobian);
6239 
6240  //---------------Insert the values into the vectors--------------
6241 
6242  //Loop over the first index of local variables
6243  for(unsigned i=0;i<nvar;i++)
6244  {
6245  //Get the local equation number
6246  unsigned eqn_number
6247  = assembly_handler_pt->eqn_number(elem_pt,i);
6248 
6249  //Add the contribution to the residuals
6250  for(unsigned v=0;v<n_vector;v++)
6251  {
6252  //Fill in each residuals vector
6253  residuals[v][eqn_number] += el_residuals[v][i];
6254  }
6255 
6256  //Now loop over the other index
6257  for(unsigned j=0;j<nvar;j++)
6258  {
6259  //Get the number of the unknown
6260  unsigned unknown = assembly_handler_pt->eqn_number(elem_pt,j);
6261 
6262  //Loop over the matrices
6263  //If it's compressed row storage, then our vector of maps
6264  //is indexed by row (equation number)
6265  for(unsigned m=0;m<n_matrix;m++)
6266  {
6267  //Get the value of the matrix at this point
6268  double value = el_jacobian[m](i,j);
6269  //Only bother to add to the vector if it's non-zero
6270  if(std::fabs(value) > Numerical_zero_for_sparse_assembly)
6271  {
6272  // number of entrys in this row
6273  const unsigned size = ncoef[m][eqn_number];
6274 
6275  // if no data has been allocated for this row then allocate
6276  if (size == 0)
6277  {
6278  // do we have previous allocation data
6280  [m][eqn_number] != 0)
6281  {
6282  matrix_row_or_col_indices[m][eqn_number] =
6283  new unsigned
6285  [eqn_number]];
6286  matrix_values[m][eqn_number] =
6287  new double
6288  [Sparse_assemble_with_arrays_previous_allocation[m]
6289  [eqn_number]];
6290  }
6291  else
6292  {
6293  matrix_row_or_col_indices[m][eqn_number] =
6294  new unsigned
6296  matrix_values[m][eqn_number] =
6297  new double
6300  [eqn_number]=
6302  }
6303  }
6304 
6305  //If it's compressed row storage, then our vector of maps
6306  //is indexed by row (equation number)
6307  if(compressed_row_flag)
6308  {
6309  // next add the data
6310  for(unsigned k=0; k<=size; k++)
6311  {
6312  if(k==size)
6313  {
6314  // do we need to allocate more storage
6316  [m][eqn_number] == ncoef[m][eqn_number])
6317  {
6318  unsigned new_allocation = ncoef[m][eqn_number]+
6320  double* new_values = new double[new_allocation];
6321  unsigned* new_indices = new unsigned[new_allocation];
6322  for (unsigned c = 0; c < ncoef[m][eqn_number]; c++)
6323  {
6324  new_values[c] = matrix_values[m][eqn_number][c];
6325  new_indices[c] =
6326  matrix_row_or_col_indices[m][eqn_number][c];
6327  }
6328  delete[] matrix_values[m][eqn_number];
6329  delete[] matrix_row_or_col_indices[m][eqn_number];
6330  matrix_values[m][eqn_number]=new_values;
6331  matrix_row_or_col_indices[m][eqn_number]=new_indices;
6333  [m][eqn_number] = new_allocation;
6334  }
6335  // and now add the data
6336  unsigned entry = ncoef[m][eqn_number];
6337  ncoef[m][eqn_number]++;
6338  matrix_row_or_col_indices[m][eqn_number][entry] = unknown;
6339  matrix_values[m][eqn_number][entry] = value;
6340  break;
6341  }
6342  else if(matrix_row_or_col_indices[m][eqn_number][k] ==
6343  unknown)
6344  {
6345  matrix_values[m][eqn_number][k] += value;
6346  break;
6347  }
6348  }
6349  }
6350  //Otherwise it's compressed column storage and our vector is
6351  //indexed by column (the unknown)
6352  else
6353  {
6354  //Add the data into the vectors in the correct position
6355  for(unsigned k=0; k<=size; k++)
6356  {
6357  if(k==size)
6358  {
6359  // do we need to allocate more storage
6361  [m][unknown] == ncoef[m][unknown])
6362  {
6363  unsigned new_allocation = ncoef[m][unknown]+
6365  double* new_values = new double[new_allocation];
6366  unsigned* new_indices = new unsigned[new_allocation];
6367  for (unsigned c = 0; c < ncoef[m][unknown]; c++)
6368  {
6369  new_values[c] = matrix_values[m][unknown][c];
6370  new_indices[c] =
6371  matrix_row_or_col_indices[m][unknown][c];
6372  }
6373  delete[] matrix_values[m][unknown];
6374  delete[] matrix_row_or_col_indices[m][unknown];
6376  [m][unknown] = new_allocation;
6377  }
6378  // and now add the data
6379  unsigned entry = ncoef[m][unknown];
6380  ncoef[m][unknown]++;
6381  matrix_row_or_col_indices[m][unknown][entry] = eqn_number;
6382  matrix_values[m][unknown][entry] = value;
6383  break;
6384  }
6385  else if(matrix_row_or_col_indices[m][unknown][k] ==
6386  eqn_number)
6387  {
6388  matrix_values[m][unknown][k] += value;
6389  break;
6390  }
6391  }
6392  }
6393  }
6394  } //End of loop over matrices
6395  }
6396  }
6397 
6398 #ifdef OOMPH_HAS_MPI
6399  } // endif halo element
6400 #endif
6401 
6402 
6403 #ifdef OOMPH_HAS_MPI
6404  // Time it?
6405  if ((!doing_residuals)&&
6407  {
6408  Elemental_assembly_time[e]=TimingHelpers::timer()-t_assemble_start;
6409  }
6410 #endif
6411 
6412  } //End of loop over the elements
6413 
6414  } //End of vector assembly
6415 
6416 
6417 
6418 #ifdef OOMPH_HAS_MPI
6419 
6420  // Postprocess timing information and re-allocate distribution of
6421  // elements during subsequent assemblies.
6422  if ((!doing_residuals)&&
6425  {
6427  }
6428 
6429  // We have determined load balancing for current setup.
6430  // This can remain the same until assign_eqn_numbers() is called
6431  // again -- the flag is re-set to true there.
6432  if ((!doing_residuals)&&
6434  {
6436  }
6437 
6438 #endif
6439 
6440  //-----------Finally we need to convert this lousy vector storage scheme
6441  //------------------------to the containers required by SuperLU
6442 
6443  //Loop over the number of matrices
6444  for(unsigned m=0;m<n_matrix;m++)
6445  {
6446  //Set the number of rows or columns
6447  row_or_column_start[m] = new int[ndof+1];
6448 
6449  // fill row_or_column_start and find the number of entries
6450  row_or_column_start[m][0] = 0;
6451  for (unsigned long i=0;i<ndof;i++)
6452  {
6453  row_or_column_start[m][i+1] = row_or_column_start[m][i]
6454  + ncoef[m][i];
6456  }
6457  const unsigned entries = row_or_column_start[m][ndof];
6458 
6459  // resize vectors
6460  column_or_row_index[m] = new int[entries];
6461  value[m] = new double[entries];
6462  nnz[m] = entries;
6463 
6464  //Now we merely loop over the number of rows or columns
6465  for(unsigned long i_global=0;i_global<ndof;i_global++)
6466  {
6467  //If there are no entries in the vector then skip the rest of the loop
6468  if(ncoef[m][i_global]==0) {continue;}
6469 
6470  //Loop over all the entries in the vectors corresponding to the given
6471  //row or column. It will NOT be ordered
6472  unsigned p = 0;
6473  for(int j=row_or_column_start[m][i_global];
6474  j<row_or_column_start[m][i_global+1]; j++)
6475  {
6476  column_or_row_index[m][j] = matrix_row_or_col_indices[m][i_global][p];
6477  value[m][j] = matrix_values[m][i_global][p];
6478  ++p;
6479  }
6480 
6481  // and delete
6482  delete[] matrix_row_or_col_indices[m][i_global];
6483  delete[] matrix_values[m][i_global];
6484  }
6485 
6486  //
6487  delete[] matrix_row_or_col_indices[m];
6488  delete[] matrix_values[m];
6489  } //End of the loop over the matrices
6490 
6492  {
6493  oomph_info << "Pausing at end of sparse assembly." << std::endl;
6494  pause("Check memory usage now.");
6495  }
6496 }
6497 
6498 
6499 #ifdef OOMPH_HAS_MPI
6500 //=======================================================================
6501 ///\short Helper method that returns the global equations to which
6502 ///the elements in the range el_lo to el_hi contribute on this
6503 ///processor
6504 //=======================================================================
6506  const unsigned &el_lo, const unsigned &el_hi,
6507  Vector<unsigned> &my_eqns)
6508 {
6509  //Index to keep track of the equations counted
6510  unsigned my_eqns_index=0;
6511 
6512  //Loop over the selection of elements
6513  for(unsigned long e=el_lo;e<=el_hi;e++)
6514  {
6515  //Get the pointer to the element
6516  GeneralisedElement* elem_pt = this->mesh_pt()->element_pt(e);
6517 
6518  //Ignore halo elements
6519  if (!elem_pt->is_halo())
6520  {
6521  //Find number of degrees of freedom in the element
6522  const unsigned nvar = assembly_handler_pt->ndof(elem_pt);
6523  //Add the number of dofs to the current size of my_eqns
6524  my_eqns.resize(my_eqns_index+nvar);
6525 
6526  //Loop over the first index of local variables
6527  for(unsigned i=0;i<nvar;i++)
6528  {
6529  //Get the local equation number
6530  unsigned global_eqn_number
6531  = assembly_handler_pt->eqn_number(elem_pt,i);
6532  //Add into the vector
6533  my_eqns[my_eqns_index+i] = global_eqn_number;
6534  }
6535  //Update the number of elements in the vector
6536  my_eqns_index += nvar;
6537  }
6538  }
6539 
6540  // now sort and remove duplicate entries in the vector
6541  std::sort(my_eqns.begin(),my_eqns.end());
6542  Vector<unsigned>::iterator it = std::unique(my_eqns.begin(),my_eqns.end());
6543  my_eqns.resize(it-my_eqns.begin());
6544 }
6545 
6546 
6547 //=============================================================================
6548 /// \short Helper method to assemble CRDoubleMatrices from distributed
6549 /// on multiple processors.
6550 //=============================================================================
6552 (const LinearAlgebraDistribution* const& target_dist_pt,
6553  Vector<int* > &column_indices,
6554  Vector<int* > &row_start,
6555  Vector<double* > &values,
6556  Vector<unsigned > &nnz,
6557  Vector<double* > &residuals)
6558 {
6559 
6560  // Time assembly
6561  double t_start=TimingHelpers::timer();
6562 
6563  // my rank and nproc
6564  unsigned my_rank = Communicator_pt->my_rank();
6565  unsigned nproc = Communicator_pt->nproc();
6566 
6567  //Total number of elements
6568  const unsigned long n_elements = mesh_pt()->nelement();
6569 
6570 #ifdef PARANOID
6571  // No elements? This is usually a sign that the problem distribution has
6572  // led to one processor not having any elements. Either
6573  // a sign of something having gone wrong or a relatively small
6574  // problem on a huge number of processors
6575  if (n_elements==0)
6576  {
6577  std::ostringstream error_stream;
6578  error_stream
6579  << "Processsor " << my_rank
6580  << " has no elements. \n"
6581  << "This is usually a sign that the problem distribution \n"
6582  << "or the load balancing have gone wrong.";
6584  error_stream.str(),
6585  "Problem::parallel_sparse_assemble()",
6586  OOMPH_EXCEPTION_LOCATION);
6587  }
6588 #endif
6589 
6590 
6591  // Default range of elements for distributed problems.
6592  unsigned long el_lo=0;
6593  unsigned long el_hi_plus_one=n_elements;
6594 
6595  // Otherwise just loop over a fraction of the elements
6596  // (This will either have been initialised in
6597  // Problem::set_default_first_and_last_element_for_assembly() or
6598  // will have been re-assigned during a previous assembly loop
6599  // Note that following the re-assignment only the entries
6600  // for the current processor are relevant.
6602  {
6603  el_lo=First_el_for_assembly[my_rank];
6604  el_hi_plus_one=Last_el_plus_one_for_assembly[my_rank];
6605  }
6606 
6607  //Find the number of vectors to be assembled
6608  const unsigned n_vector = residuals.size();
6609 
6610  //Find the number of matrices to be assembled
6611  const unsigned n_matrix = column_indices.size();
6612 
6613  //Locally cache pointer to assembly handler
6615 
6616  bool doing_residuals=false;
6617  if (dynamic_cast<ParallelResidualsHandler*>(Assembly_handler_pt)!=0)
6618  {
6619  doing_residuals=true;
6620  }
6621 
6622 //Error check dimensions
6623 #ifdef PARANOID
6624  if(row_start.size() != n_matrix)
6625  {
6626  std::ostringstream error_stream;
6627  error_stream
6628  << "Error: " << std::endl
6629  << "row_or_column_start.size() " << row_start.size()
6630  << " does not equal "
6631  << "column_or_row_index.size() "
6632  << column_indices.size() << std::endl;
6633  throw OomphLibError(
6634  error_stream.str(),
6635  OOMPH_CURRENT_FUNCTION,
6636  OOMPH_EXCEPTION_LOCATION);
6637  }
6638 
6639  if(values.size() != n_matrix)
6640  {
6641  std::ostringstream error_stream;
6642  error_stream
6643  << "Error: "
6644  << std::endl
6645  << "value.size() " << values.size() << " does not equal "
6646  << "column_or_row_index.size() "
6647  << column_indices.size() << std::endl<< std::endl
6648  << std::endl;
6649  throw OomphLibError(
6650  error_stream.str(),
6651  OOMPH_CURRENT_FUNCTION,
6652  OOMPH_EXCEPTION_LOCATION);
6653  }
6654 #endif
6655 
6656 
6657  // start by assembling the sorted set of equations to which this processor
6658  // contributes. Essentially this is every global equation that features in
6659  // all the non-halo elements. This may not be the same as the locally-stored
6660  // dofs because some of the Nodes in non-halo elements may actually
6661  // be halos.
6662  //======================================================================
6663  Vector<unsigned> my_eqns;
6664  if (n_elements!=0)
6665  {
6666  this->get_my_eqns(assembly_handler_pt,el_lo,el_hi_plus_one-1,my_eqns);
6667  }
6668 
6669  // number of equations
6670  unsigned my_n_eqn = my_eqns.size();
6671 
6672  // next we assemble the data into an array of arrays
6673  // =================================================
6674 // The idea behind this sparse assembly routine is to use an array of
6675 // arrays for the entries in each complete matrix. And a second
6676 // array of arrays stores the global row (or column) indeces.
6677 
6678 // Set up two vector of vectors to store the entries of each matrix,
6679 // indexed by either the column or row. The entries of the vector for
6680 // each matrix correspond to all the rows or columns of that matrix.
6681  Vector<unsigned**> matrix_col_indices(n_matrix);
6682  Vector<double**> matrix_values(n_matrix);
6683 
6684  //Loop over the number of matrices being assembled and resize
6685  //each vector of vectors to the number of rows or columns of the matrix
6686  for(unsigned m=0;m<n_matrix;m++)
6687  {
6688  matrix_col_indices[m] = new unsigned*[my_n_eqn];
6689  matrix_values[m] = new double*[my_n_eqn];
6690  for (unsigned i = 0; i < my_n_eqn; i++)
6691  {
6692  matrix_col_indices[m][i]=0;
6693  matrix_values[m][i]=0;
6694  }
6695  }
6696 
6697  //Resize the residuals vectors
6698  Vector<double*> residuals_data(n_vector);
6699  for(unsigned v=0;v<n_vector;v++)
6700  {
6701  residuals_data[v] = new double[my_n_eqn];
6702  for (unsigned i = 0; i < my_n_eqn; i++)
6703  {
6704  residuals_data[v][i] = 0;
6705  }
6706  }
6707 
6708  // Storage for assembly time for elements
6709  double t_assemble_start=0.0;
6710 
6711  // Storage for assembly times
6712  if ((!doing_residuals)&&
6714  {
6715  Elemental_assembly_time.resize(n_elements);
6716  }
6717 
6718  // number of coefficients in each row
6719  Vector<Vector<unsigned> > ncoef(n_matrix);
6720  for (unsigned m = 0; m < n_matrix; m++)
6721  {
6722  ncoef[m].resize(my_n_eqn,0);
6723  }
6724 
6725  // Sparse_assemble_with_arrays_previous_allocation stores the number of
6726  // coefs in each row.
6727  // if a matrix of this size has not been assembled before then resize this
6728  // storage
6730  {
6732  for (unsigned m = 0; m < n_matrix; m++)
6733  {
6735  }
6736  }
6737 
6738 
6739  // assemble and populate an array based storage scheme
6740  {
6741  //Allocate local storage for the element's contribution to the
6742  //residuals vectors and system matrices of the size of the maximum
6743  //number of dofs in any element
6744  //This means that the storage will only be allocated (and deleted) once
6745  Vector<Vector<double> > el_residuals(n_vector);
6746  Vector<DenseMatrix<double> > el_jacobian(n_matrix);
6747 
6748  //Loop over the elements
6749  for(unsigned long e=el_lo;e<el_hi_plus_one;e++)
6750  {
6751  // Time it?
6752  if ((!doing_residuals)&&
6754  {
6755  t_assemble_start=TimingHelpers::timer();
6756  }
6757 
6758  //Get the pointer to the element
6759  GeneralisedElement* elem_pt = mesh_pt()->element_pt(e);
6760 
6761  //Ignore halo elements
6762  if (!elem_pt->is_halo())
6763  {
6764 
6765  //Find number of degrees of freedom in the element
6766  const unsigned nvar = assembly_handler_pt->ndof(elem_pt);
6767 
6768  //Resize the storage for elemental jacobian and residuals
6769  for(unsigned v=0;v<n_vector;v++) {el_residuals[v].resize(nvar);}
6770  for(unsigned m=0;m<n_matrix;m++) {el_jacobian[m].resize(nvar);}
6771 
6772  //Now get the residuals and jacobian for the element
6773  assembly_handler_pt->
6774  get_all_vectors_and_matrices(elem_pt,el_residuals, el_jacobian);
6775 
6776  //---------------Insert the values into the vectors--------------
6777 
6778  //Loop over the first index of local variables
6779  for(unsigned i=0;i<nvar;i++)
6780  {
6781  //Get the local equation number
6782  unsigned global_eqn_number
6783  = assembly_handler_pt->eqn_number(elem_pt,i);
6784 
6785  // determine the element number in my set of eqns using the
6786  // bisection method
6787  int left = 0;
6788  int right = my_n_eqn-1;
6789  int eqn_number = right/2;
6790  while (my_eqns[eqn_number] != global_eqn_number)
6791  {
6792  if (left == right)
6793  {
6794  // Check that the residuals associated with the
6795  // eqn number that can't be found are all zero
6796  bool broken=false;
6797  for(unsigned v=0;v<n_vector;v++)
6798  {
6799  if (el_residuals[v][i]!=0.0)
6800  {
6801  broken=true;
6802  break;
6803  }
6804  }
6805 
6806  // Now loop over the other index to check the entries
6807  // in the appropriate row of the Jacobians are zero too
6808  for(unsigned j=0;j<nvar;j++)
6809  {
6810  //Get the number of the unknown
6811  //unsigned unknown = assembly_handler_pt->eqn_number(elem_pt,j);
6812 
6813  //Loop over the matrices
6814  //If it's compressed row storage, then our vector of maps
6815  //is indexed by row (equation number)
6816  for(unsigned m=0;m<n_matrix;m++)
6817  {
6818  //Get the value of the matrix at this point
6819  double value = el_jacobian[m](i,j);
6820  if (value!=0.0)
6821  {
6822  broken=true;
6823  break;
6824  }
6825  if (broken) break;
6826  }
6827  }
6828 
6829  if (broken)
6830  {
6831  std::ostringstream error_stream;
6832  error_stream
6833  << "Internal Error: "
6834  << std::endl
6835  << "Could not find global equation number "
6836  << global_eqn_number
6837  << " in my_eqns vector of equation numbers but\n"
6838  << "at least one entry in the residual vector is nonzero.";
6839  throw OomphLibError(
6840  error_stream.str(),
6841  OOMPH_CURRENT_FUNCTION,
6842  OOMPH_EXCEPTION_LOCATION);
6843  }
6844  else
6845  {
6846  break;
6847  }
6848  }
6849  if (my_eqns[eqn_number] > global_eqn_number)
6850  {
6851  right = std::max(eqn_number-1,left);
6852  }
6853  else
6854  {
6855  left = std::min(eqn_number+1,right);
6856  }
6857  eqn_number = (right+left)/2;
6858  }
6859 
6860  //Add the contribution to the residuals
6861  for(unsigned v=0;v<n_vector;v++)
6862  {
6863  //Fill in each residuals vector
6864  residuals_data[v][eqn_number] += el_residuals[v][i];
6865  }
6866 
6867  //Now loop over the other index
6868  for(unsigned j=0;j<nvar;j++)
6869  {
6870  //Get the number of the unknown
6871  unsigned unknown = assembly_handler_pt->eqn_number(elem_pt,j);
6872 
6873  //Loop over the matrices
6874  //If it's compressed row storage, then our vector of maps
6875  //is indexed by row (equation number)
6876  for(unsigned m=0;m<n_matrix;m++)
6877  {
6878  //Get the value of the matrix at this point
6879  double value = el_jacobian[m](i,j);
6880  //Only bother to add to the vector if it's non-zero
6881  if(std::fabs(value) > Numerical_zero_for_sparse_assembly)
6882  {
6883  // number of entrys in this row
6884  const unsigned size = ncoef[m][eqn_number];
6885 
6886  // if no data has been allocated for this row then allocate
6887  if (size == 0)
6888  {
6889  // do we have previous allocation data
6891  [m][eqn_number] != 0)
6892  {
6893  matrix_col_indices[m][eqn_number] =
6894  new unsigned
6896  [eqn_number]];
6897 
6898  matrix_values[m][eqn_number] =
6899  new double
6900  [Sparse_assemble_with_arrays_previous_allocation[m]
6901  [eqn_number]];
6902  }
6903  else
6904  {
6905  matrix_col_indices[m][eqn_number] =
6906  new unsigned
6908 
6909  matrix_values[m][eqn_number] =
6910  new double
6912 
6914  [eqn_number]=
6916  }
6917  }
6918 
6919  // next add the data
6920  for(unsigned k=0; k<=size; k++)
6921  {
6922  if(k==size)
6923  {
6924  // do we need to allocate more storage
6926  [m][eqn_number] == ncoef[m][eqn_number])
6927  {
6928  unsigned new_allocation = ncoef[m][eqn_number]+
6930  double* new_values = new double[new_allocation];
6931  unsigned* new_indices = new unsigned[new_allocation];
6932  for (unsigned c = 0; c < ncoef[m][eqn_number]; c++)
6933  {
6934  new_values[c] = matrix_values[m][eqn_number][c];
6935  new_indices[c] = matrix_col_indices[m][eqn_number][c];
6936  }
6937  delete[] matrix_values[m][eqn_number];
6938  delete[] matrix_col_indices[m][eqn_number];
6939 
6940  matrix_values[m][eqn_number] = new_values;
6941  matrix_col_indices[m][eqn_number] = new_indices;
6942 
6944  [m][eqn_number] = new_allocation;
6945  }
6946  // and now add the data
6947  unsigned entry = ncoef[m][eqn_number];
6948  ncoef[m][eqn_number]++;
6949  matrix_col_indices[m][eqn_number][entry] = unknown;
6950  matrix_values[m][eqn_number][entry] = value;
6951  break;
6952  }
6953  else if(matrix_col_indices[m][eqn_number][k] == unknown)
6954  {
6955  matrix_values[m][eqn_number][k] += value;
6956  break;
6957  }
6958  }
6959  }//numerical zero check
6960  } //End of loop over matrices
6961  }
6962  }
6963  } // endif halo element
6964 
6965  // Time it?
6966  if ((!doing_residuals)&&
6968  {
6969  Elemental_assembly_time[e]=TimingHelpers::timer()-t_assemble_start;
6970  }
6971  } //End of loop over the elements
6972  } //End of vector assembly
6973 
6974 
6975  // Doc?
6976  double t_end=0.0;
6977  double t_local=0.0;
6978  double t_max=0.0;
6979  double t_min=0.0;
6980  double t_sum=0.0;
6982  {
6983  t_end=TimingHelpers::timer();
6984  t_local=t_end-t_start;
6985  t_max=0.0;
6986  t_min=0.0;
6987  t_sum=0.0;
6988  MPI_Allreduce(&t_local,&t_max,1,
6989  MPI_DOUBLE,MPI_MAX,
6990  this->communicator_pt()->mpi_comm());
6991  MPI_Allreduce(&t_local,&t_min,1,
6992  MPI_DOUBLE,MPI_MIN,
6993  this->communicator_pt()->mpi_comm());
6994  MPI_Allreduce(&t_local,&t_sum,1,
6995  MPI_DOUBLE,MPI_SUM,
6996  this->communicator_pt()->mpi_comm());
6997  double imbalance=(t_max-t_min)/(t_sum/double(nproc))*100.0;
6998 
6999  if (doing_residuals)
7000  {
7001  oomph_info
7002  << "\nCPU for residual computation (loc/max/min/imbal): ";
7003  }
7004  else
7005  {
7006  oomph_info
7007  << "\nCPU for Jacobian computation (loc/max/min/imbal): ";
7008  }
7009  oomph_info
7010  << t_local << " "
7011  << t_max << " "
7012  << t_min << " "
7013  << imbalance << "%\n";
7014 
7015  t_start=TimingHelpers::timer();
7016  }
7017 
7018 
7019  // Adjust number of coefficients in each row
7020  for (unsigned m = 0; m < n_matrix; m++)
7021  {
7022  unsigned max=0;
7023  unsigned min=INT_MAX;
7024  unsigned sum=0;
7025  unsigned sum_total=0;
7026  for (unsigned e = 0; e < my_n_eqn; e++)
7027  {
7028  sum+=ncoef[m][e];
7030  if (ncoef[m][e]>max) max=ncoef[m][e];
7031  if (ncoef[m][e]<min) min=ncoef[m][e];
7032 
7033  // Now shrink the storage to what we actually need
7034  unsigned new_allocation = ncoef[m][e];
7035  double* new_values = new double[new_allocation];
7036  unsigned* new_indices = new unsigned[new_allocation];
7037  for (unsigned c = 0; c < ncoef[m][e]; c++)
7038  {
7039  new_values[c] = matrix_values[m][e][c];
7040  new_indices[c] = matrix_col_indices[m][e][c];
7041  }
7042  delete[] matrix_values[m][e];
7043  delete[] matrix_col_indices[m][e];
7044 
7045  matrix_values[m][e] = new_values;
7046  matrix_col_indices[m][e] = new_indices;
7047 
7048  }
7049  }
7050 
7051 
7052  // Postprocess timing information and re-allocate distribution of
7053  // elements during subsequent assemblies.
7054  if ((!doing_residuals)&&
7057  {
7059  }
7060 
7061  // We have determined load balancing for current setup.
7062  // This can remain the same until assign_eqn_numbers() is called
7063  // again -- the flag is re-set to true there.
7064  if ((!doing_residuals)&&
7066  {
7068  }
7069 
7070 
7071  // next we compute the number of equations and number of non-zeros to be
7072  // sent to each processor, and send/recv that information
7073  // =====================================================================
7074 
7075  // determine the number of eqns to be sent to each processor
7076  Vector<unsigned> n_eqn_for_proc(nproc,0);
7077  Vector<unsigned> first_eqn_element_for_proc(nproc,0);
7078  //If no equations are assembled then we don't need to do any of this
7079  if(my_n_eqn > 0)
7080  {
7081  unsigned current_p = target_dist_pt->rank_of_global_row(my_eqns[0]);
7082  first_eqn_element_for_proc[current_p] = 0;
7083  n_eqn_for_proc[current_p] = 1;
7084  for (unsigned i = 1; i < my_n_eqn; i++)
7085  {
7086  unsigned next_p = target_dist_pt->rank_of_global_row(my_eqns[i]);
7087  if (next_p != current_p)
7088  {
7089  current_p = next_p;
7090  first_eqn_element_for_proc[current_p] = i;
7091  }
7092  n_eqn_for_proc[current_p]++;
7093  }
7094  }
7095 
7096  // determine the number of non-zeros to be sent to each processor for each
7097  // matrix (if n_eqn_for_proc[p]=0, then nothing will be assembled)
7098  DenseMatrix<unsigned> nnz_for_proc(nproc,n_matrix,0);
7099  for (unsigned p = 0; p < nproc; p++)
7100  {
7101  int first_eqn_element = first_eqn_element_for_proc[p];
7102  int last_eqn_element = (int)(first_eqn_element + n_eqn_for_proc[p]) - 1;
7103  for (unsigned m = 0; m < n_matrix; m++)
7104  {
7105  for (int i = first_eqn_element; i <= last_eqn_element; i++)
7106  {
7107  nnz_for_proc(p,m) += ncoef[m][i];
7108  }
7109  }
7110  }
7111 
7112  // next post the sends and recvs to the corresponding processors
7113  Vector<unsigned*> temp_send_storage(nproc);
7114  Vector<unsigned*> temp_recv_storage(nproc);
7115  Vector<MPI_Request> send_nnz_reqs;
7116  Vector<MPI_Request> recv_nnz_reqs;
7117  for (unsigned p = 0; p < nproc; p++)
7118  {
7119  if (p != my_rank)
7120  {
7121  temp_send_storage[p] = new unsigned[n_matrix+1];
7122  temp_send_storage[p][0] = n_eqn_for_proc[p];
7123  for (unsigned m = 0; m < n_matrix; m++)
7124  {
7125  temp_send_storage[p][m+1] = nnz_for_proc(p,m);
7126  }
7127  MPI_Request sreq;
7128  MPI_Isend(temp_send_storage[p],n_matrix+1,MPI_UNSIGNED,p,0,
7129  Communicator_pt->mpi_comm(),&sreq);
7130  send_nnz_reqs.push_back(sreq);
7131  temp_recv_storage[p] = new unsigned[n_matrix+1];
7132  MPI_Request rreq;
7133  MPI_Irecv(temp_recv_storage[p],n_matrix+1,MPI_UNSIGNED,p,0,
7134  Communicator_pt->mpi_comm(),&rreq);
7135  recv_nnz_reqs.push_back(rreq);
7136  }
7137  }
7138 
7139  // assemble the data to be sent to each processor
7140  // ==============================================
7141 
7142  // storage
7143  Vector<unsigned*> eqns_for_proc(nproc);
7144  DenseMatrix<double*> residuals_for_proc(nproc,n_vector);
7145  DenseMatrix<unsigned*> row_start_for_proc(nproc,n_matrix);
7146  DenseMatrix<unsigned*> column_indices_for_proc(nproc,n_matrix);
7147  DenseMatrix<double*> values_for_proc(nproc,n_matrix);
7148 
7149  // equation numbers
7150  for (unsigned p = 0; p < nproc; p++)
7151  {
7152  unsigned n_eqns_p = n_eqn_for_proc[p];
7153  if (n_eqns_p > 0)
7154  {
7155  unsigned first_eqn_element = first_eqn_element_for_proc[p];
7156  unsigned first_row = target_dist_pt->first_row(p);
7157  eqns_for_proc[p] = new unsigned[n_eqns_p];
7158  for (unsigned i = 0; i < n_eqns_p; i++)
7159  {
7160  eqns_for_proc[p][i] = my_eqns[i+first_eqn_element]-first_row;
7161  }
7162  }
7163  }
7164 
7165  // residuals for p
7166  for (unsigned v = 0; v < n_vector; v++)
7167  {
7168  for (unsigned p = 0; p < nproc; p++)
7169  {
7170  unsigned n_eqns_p = n_eqn_for_proc[p];
7171  if (n_eqns_p > 0)
7172  {
7173  unsigned first_eqn_element = first_eqn_element_for_proc[p];
7174  residuals_for_proc(p,v) = new double[n_eqns_p];
7175  for (unsigned i = 0; i < n_eqns_p; i++)
7176  {
7177  residuals_for_proc(p,v)[i] = residuals_data[v][first_eqn_element+i];
7178  }
7179  }
7180  }
7181  delete[] residuals_data[v];
7182  }
7183 
7184  // matrices for p
7185  for (unsigned m = 0; m < n_matrix; m++)
7186  {
7187  for (unsigned p = 0; p < nproc; p++)
7188  {
7189  unsigned n_eqns_p = n_eqn_for_proc[p];
7190  if (n_eqns_p > 0)
7191  {
7192  unsigned first_eqn_element = first_eqn_element_for_proc[p];
7193  row_start_for_proc(p,m) = new unsigned[n_eqns_p+1];
7194  column_indices_for_proc(p,m) = new unsigned[nnz_for_proc(p,m)];
7195  values_for_proc(p,m) = new double[nnz_for_proc(p,m)];
7196  unsigned entry = 0;
7197  for (unsigned i = 0; i < n_eqns_p; i++)
7198  {
7199  row_start_for_proc(p,m)[i] = entry;
7200  unsigned n_coef_in_row = ncoef[m][first_eqn_element+i];
7201  for (unsigned j = 0; j < n_coef_in_row; j++)
7202  {
7203  column_indices_for_proc(p,m)[entry]
7204  = matrix_col_indices[m][i+first_eqn_element][j];
7205  values_for_proc(p,m)[entry]
7206  = matrix_values[m][i+first_eqn_element][j];
7207  entry++;
7208  }
7209  }
7210  row_start_for_proc(p,m)[n_eqns_p]=entry;
7211  }
7212  }
7213  for (unsigned i = 0; i < my_n_eqn; i++)
7214  {
7215  delete[] matrix_col_indices[m][i];
7216  delete[] matrix_values[m][i];
7217  }
7218  delete[] matrix_col_indices[m];
7219  delete[] matrix_values[m];
7220  }
7221 
7222  // need to wait for the recv nnzs to complete
7223  // before we can allocate storage for the matrix recvs
7224  // ===================================================
7225 
7226  // recv and copy the datafrom the recv storage to
7227  // + nnz_from_proc
7228  // + n_eqn_from_proc
7229  Vector<MPI_Status> recv_nnz_stat(nproc-1);
7230  MPI_Waitall(nproc-1,&recv_nnz_reqs[0],&recv_nnz_stat[0]);
7231  Vector<unsigned> n_eqn_from_proc(nproc);
7232  DenseMatrix<unsigned> nnz_from_proc(nproc,n_matrix);
7233  for (unsigned p = 0; p < nproc; p++)
7234  {
7235  if (p != my_rank)
7236  {
7237  n_eqn_from_proc[p] = temp_recv_storage[p][0];
7238  for (unsigned m = 0; m < n_matrix; m++)
7239  {
7240  nnz_from_proc(p,m) = temp_recv_storage[p][m+1];
7241  }
7242  delete[] temp_recv_storage[p];
7243  }
7244  else
7245  {
7246  n_eqn_from_proc[p] = n_eqn_for_proc[p];
7247  for (unsigned m = 0; m < n_matrix; m++)
7248  {
7249  nnz_from_proc(p,m) = nnz_for_proc(p,m);
7250  }
7251  }
7252  }
7253  recv_nnz_stat.clear();
7254  recv_nnz_reqs.clear();
7255 
7256  // allocate the storage for the data to be recv and post the sends recvs
7257  // =====================================================================
7258 
7259  // storage
7260  Vector<unsigned*> eqns_from_proc(nproc);
7261  DenseMatrix<double*> residuals_from_proc(nproc,n_vector);
7262  DenseMatrix<unsigned*> row_start_from_proc(nproc,n_matrix);
7263  DenseMatrix<unsigned*> column_indices_from_proc(nproc,n_matrix);
7264  DenseMatrix<double*> values_from_proc(nproc,n_matrix);
7265 
7266  // allocate and post sends and recvs
7267  double base;
7268  MPI_Aint communication_base;
7269  MPI_Get_address(&base,&communication_base);
7270  unsigned n_comm_types = 1 + 1*n_vector + 3*n_matrix;
7271  Vector<MPI_Request> recv_reqs;
7272  Vector<MPI_Request> send_reqs;
7273  for (unsigned p = 0; p < nproc; p++)
7274  {
7275  if (p != my_rank)
7276  {
7277 
7278  // allocate
7279  if (n_eqn_from_proc[p] > 0)
7280  {
7281  eqns_from_proc[p] = new unsigned[n_eqn_from_proc[p]];
7282  for (unsigned v = 0; v < n_vector; v++)
7283  {
7284  residuals_from_proc(p,v) = new double[n_eqn_from_proc[p]];
7285  }
7286  for (unsigned m = 0; m < n_matrix; m++)
7287  {
7288  row_start_from_proc(p,m) = new unsigned[n_eqn_from_proc[p]+1];
7289  column_indices_from_proc(p,m) = new unsigned[nnz_from_proc(p,m)];
7290  values_from_proc(p,m) = new double[nnz_from_proc(p,m)];
7291  }
7292  }
7293 
7294  // recv
7295  if (n_eqn_from_proc[p] > 0)
7296  {
7297  MPI_Datatype types[n_comm_types];
7298  MPI_Aint offsets[n_comm_types];
7299  int count[n_comm_types];
7300  int pt = 0;
7301 
7302  // equations
7303  count[pt] = 1;
7304  MPI_Get_address(eqns_from_proc[p],&offsets[pt]);
7305  offsets[pt] -= communication_base;
7306  MPI_Type_contiguous(n_eqn_from_proc[p],MPI_UNSIGNED,&types[pt]);
7307  MPI_Type_commit(&types[pt]);
7308  pt++;
7309 
7310  // vectors
7311  for (unsigned v = 0; v < n_vector; v++)
7312  {
7313  count[pt] = 1;
7314  MPI_Get_address(residuals_from_proc(p,v),&offsets[pt]);
7315  offsets[pt] -= communication_base;
7316  MPI_Type_contiguous(n_eqn_from_proc[p],MPI_DOUBLE,&types[pt]);
7317  MPI_Type_commit(&types[pt]);
7318  pt++;
7319  }
7320 
7321  // matrices
7322  for (unsigned m = 0; m < n_matrix; m++)
7323  {
7324  // row start
7325  count[pt] = 1;
7326  MPI_Get_address(row_start_from_proc(p,m),&offsets[pt]);
7327  offsets[pt] -= communication_base;
7328  MPI_Type_contiguous(n_eqn_from_proc[p]+1,MPI_UNSIGNED,&types[pt]);
7329  MPI_Type_commit(&types[pt]);
7330  pt++;
7331 
7332 
7333  // column indices
7334  count[pt] = 1;
7335  MPI_Get_address(column_indices_from_proc(p,m),&offsets[pt]);
7336  offsets[pt] -= communication_base;
7337  MPI_Type_contiguous(nnz_from_proc(p,m),MPI_UNSIGNED,&types[pt]);
7338  MPI_Type_commit(&types[pt]);
7339  pt++;
7340 
7341  // values
7342  count[pt] = 1;
7343  MPI_Get_address(values_from_proc(p,m),&offsets[pt]);
7344  offsets[pt] -= communication_base;
7345  MPI_Type_contiguous(nnz_from_proc(p,m),MPI_DOUBLE,&types[pt]);
7346  MPI_Type_commit(&types[pt]);
7347  pt++;
7348  }
7349 
7350  // build the combined type
7351  MPI_Datatype recv_type;
7352  MPI_Type_create_struct(n_comm_types,count,offsets,types,&recv_type);
7353  MPI_Type_commit(&recv_type);
7354  for (unsigned t = 0; t < n_comm_types; t++)
7355  {
7356  MPI_Type_free(&types[t]);
7357  }
7358  MPI_Request req;
7359  MPI_Irecv(&base,1,recv_type,p,1,
7360  Communicator_pt->mpi_comm(),&req);
7361  MPI_Type_free(&recv_type);
7362  recv_reqs.push_back(req);
7363  }
7364 
7365  // send
7366  if (n_eqn_for_proc[p] > 0)
7367  {
7368  MPI_Datatype types[n_comm_types];
7369  MPI_Aint offsets[n_comm_types];
7370  int count[n_comm_types];
7371  int pt = 0;
7372 
7373  // equations
7374  count[pt] = 1;
7375  MPI_Get_address(eqns_for_proc[p],&offsets[pt]);
7376  offsets[pt] -= communication_base;
7377  MPI_Type_contiguous(n_eqn_for_proc[p],MPI_UNSIGNED,&types[pt]);
7378  MPI_Type_commit(&types[pt]);
7379  pt++;
7380 
7381  // vectors
7382  for (unsigned v = 0; v < n_vector; v++)
7383  {
7384  count[pt] = 1;
7385  MPI_Get_address(residuals_for_proc(p,v),&offsets[pt]);
7386  offsets[pt] -= communication_base;
7387  MPI_Type_contiguous(n_eqn_for_proc[p],MPI_DOUBLE,&types[pt]);
7388  MPI_Type_commit(&types[pt]);
7389  pt++;
7390  }
7391 
7392  // matrices
7393  for (unsigned m = 0; m < n_matrix; m++)
7394  {
7395  // row start
7396  count[pt] = 1;
7397  MPI_Get_address(row_start_for_proc(p,m),&offsets[pt]);
7398  offsets[pt] -= communication_base;
7399  MPI_Type_contiguous(n_eqn_for_proc[p]+1,MPI_UNSIGNED,&types[pt]);
7400  MPI_Type_commit(&types[pt]);
7401  pt++;
7402 
7403 
7404  // column indices
7405  count[pt] = 1;
7406  MPI_Get_address(column_indices_for_proc(p,m),&offsets[pt]);
7407  offsets[pt] -= communication_base;
7408  MPI_Type_contiguous(nnz_for_proc(p,m),MPI_UNSIGNED,&types[pt]);
7409  MPI_Type_commit(&types[pt]);
7410  pt++;
7411 
7412  // values
7413  count[pt] = 1;
7414  MPI_Get_address(values_for_proc(p,m),&offsets[pt]);
7415  offsets[pt] -= communication_base;
7416  MPI_Type_contiguous(nnz_for_proc(p,m),MPI_DOUBLE,&types[pt]);
7417  MPI_Type_commit(&types[pt]);
7418  pt++;
7419  }
7420 
7421  // build the combined type
7422  MPI_Datatype send_type;
7423  MPI_Type_create_struct(n_comm_types,count,offsets,types,&send_type);
7424  MPI_Type_commit(&send_type);
7425  for (unsigned t = 0; t < n_comm_types; t++)
7426  {
7427  MPI_Type_free(&types[t]);
7428  }
7429  MPI_Request req;
7430  MPI_Isend(&base,1,send_type,p,1,
7431  Communicator_pt->mpi_comm(),&req);
7432  MPI_Type_free(&send_type);
7433  send_reqs.push_back(req);
7434  }
7435  }
7436  // otherwise send to self
7437  else
7438  {
7439  eqns_from_proc[p] = eqns_for_proc[p];
7440  for (unsigned v = 0; v < n_vector; v++)
7441  {
7442  residuals_from_proc(p,v) = residuals_for_proc(p,v);
7443  }
7444  for (unsigned m = 0; m < n_matrix; m++)
7445  {
7446  row_start_from_proc(p,m) = row_start_for_proc(p,m);
7447  column_indices_from_proc(p,m) = column_indices_for_proc(p,m);
7448  values_from_proc(p,m) = values_for_proc(p,m);
7449  }
7450  }
7451  }
7452 
7453  // wait for the recvs to complete
7454  unsigned n_recv_req = recv_reqs.size();
7455  if (n_recv_req > 0)
7456  {
7457  Vector<MPI_Status> recv_stat(n_recv_req);
7458  MPI_Waitall(n_recv_req,&recv_reqs[0],&recv_stat[0]);
7459  }
7460 
7461  // ==============================================
7462  unsigned target_nrow_local = target_dist_pt->nrow_local();
7463 
7464  // loop over the matrices
7465  for (unsigned m = 0; m < n_matrix; m++)
7466  {
7467 
7468  // allocate row_start
7469  row_start[m] = new int[target_nrow_local+1];
7470  row_start[m][0] = 0;
7471 
7472  // initially allocate storage based on the maximum number of non-zeros
7473  // from any one processor
7474  unsigned nnz_allocation = Parallel_sparse_assemble_previous_allocation;
7475  for (unsigned p = 0; p < nproc; p++)
7476  {
7477  nnz_allocation = std::max(nnz_allocation,nnz_from_proc(p,m));
7478  }
7479  Vector<double*> values_chunk(1);
7480  values_chunk[0] = new double[nnz_allocation];
7481  Vector<int*> column_indices_chunk(1);
7482  column_indices_chunk[0] = new int[nnz_allocation];
7483  Vector<unsigned> ncoef_in_chunk(1,0);
7484  Vector<unsigned> size_of_chunk(1,0);
7485  size_of_chunk[0] = nnz_allocation;
7486  unsigned current_chunk = 0;
7487 
7488  // for each row on this processor
7489  for (unsigned i = 0; i < target_nrow_local; i++)
7490  {
7491  row_start[m][i]=0;
7492 
7493  // determine the processors that this row is on
7494  Vector<int> row_on_proc(nproc,-1);
7495  for (unsigned p = 0; p < nproc; p++)
7496  {
7497  if (n_eqn_from_proc[p]==0)
7498  {
7499  row_on_proc[p]=-1;
7500  }
7501  else
7502  {
7503  int left = 0;
7504  int right = n_eqn_from_proc[p]-1;
7505  int midpoint = right/2;
7506  bool complete = false;
7507  while (!complete)
7508  {
7509  midpoint = (right+left)/2;
7510  if (midpoint > right) { midpoint = right; }
7511  if (midpoint < left) { midpoint = left; }
7512  if (left==right)
7513  {
7514  if (eqns_from_proc[p][midpoint] == i)
7515  {
7516  midpoint=left;
7517  }
7518  else
7519  {
7520  midpoint = -1;
7521  }
7522  complete=true;
7523  }
7524  else if (eqns_from_proc[p][midpoint] == i)
7525  {
7526  complete = true;
7527  }
7528  else if (eqns_from_proc[p][midpoint] > i)
7529  {
7530  right = std::max(midpoint-1,left);
7531  }
7532  else
7533  {
7534  left = std::min(midpoint+1,right);
7535  }
7536  }
7537  row_on_proc[p] = midpoint;
7538  }
7539  }
7540 
7541  // for each processor build this row of the matrix
7542  unsigned check_first = ncoef_in_chunk[current_chunk];
7543  unsigned check_last = check_first;
7544  for (unsigned p = 0; p < nproc; p++)
7545  {
7546  if (row_on_proc[p] != -1)
7547  {
7548  int row = row_on_proc[p];
7549  unsigned first = row_start_from_proc(p,m)[row];
7550  unsigned last = row_start_from_proc(p,m)[row+1];
7551  for (unsigned l = first; l < last; l++)
7552  {
7553  bool done = false;
7554  for (unsigned j = check_first; j <=check_last && !done; j++)
7555  {
7556  if (j==check_last)
7557  {
7558  // is this temp array full, do we need to allocate
7559  // a new temp array
7560  if (ncoef_in_chunk[current_chunk] ==
7561  size_of_chunk[current_chunk])
7562  {
7563 
7564  // number of chunks allocated
7565  unsigned n_chunk = values_chunk.size();
7566 
7567  // determine the number of non-zeros added so far
7568  // (excluding the current row)
7569  unsigned nnz_so_far = 0;
7570  for (unsigned c = 0; c < n_chunk; c++)
7571  {
7572  nnz_so_far += ncoef_in_chunk[c];
7573  }
7574  nnz_so_far -= row_start[m][i];
7575 
7576  // average number of non-zeros per row
7577  unsigned avg_nnz = nnz_so_far/(i+1);
7578 
7579  // number of rows left +1
7580  unsigned nrows_left = target_nrow_local-i;
7581 
7582  // allocation for next chunk
7583  unsigned next_chunk_size = avg_nnz*nrows_left+row_start[m][i];
7584 
7585  // allocate storage in next chunk
7586  current_chunk++;
7587  n_chunk++;
7588  values_chunk.resize(n_chunk);
7589  values_chunk[current_chunk] = new double[next_chunk_size];
7590  column_indices_chunk.resize(n_chunk);
7591  column_indices_chunk[current_chunk]
7592  = new int[next_chunk_size];
7593  size_of_chunk.resize(n_chunk);
7594  size_of_chunk[current_chunk] = next_chunk_size;
7595  ncoef_in_chunk.resize(n_chunk);
7596 
7597  // copy current row from previous chunk to new chunk
7598  for (unsigned k = check_first; k < check_last; k++)
7599  {
7600  values_chunk[current_chunk][k-check_first] =
7601  values_chunk[current_chunk-1][k];
7602  column_indices_chunk[current_chunk][k-check_first] =
7603  column_indices_chunk[current_chunk-1][k];
7604  }
7605  ncoef_in_chunk[current_chunk-1]-=row_start[m][i];
7606  ncoef_in_chunk[current_chunk]=row_start[m][i];
7607 
7608  // update first_check and last_check
7609  check_first=0;
7610  check_last=row_start[m][i];
7611  j=check_last;
7612  }
7613 
7614  // add the coefficient
7615  values_chunk[current_chunk][j]=values_from_proc(p,m)[l];
7616  column_indices_chunk[current_chunk][j]
7617  =column_indices_from_proc(p,m)[l];
7618  ncoef_in_chunk[current_chunk]++;
7619  row_start[m][i]++;
7620  check_last++;
7621  done=true;
7622  }
7623  else if (column_indices_chunk[current_chunk][j] ==
7624  (int)column_indices_from_proc(p,m)[l])
7625  {
7626  values_chunk[current_chunk][j] += values_from_proc(p,m)[l];
7627  done=true;
7628  }
7629  }
7630  }
7631  }
7632  }
7633  }
7634 
7635  // delete recv data for this matrix
7636  for (unsigned p = 0; p < nproc; p++)
7637  {
7638  if (n_eqn_from_proc[p] > 0)
7639  {
7640  delete[] row_start_from_proc(p,m);
7641  delete[] column_indices_from_proc(p,m);
7642  delete[] values_from_proc(p,m);
7643  }
7644  }
7645 
7646  // next we take the chunk base storage of the column indices and values
7647  // and copy into a single contiguous block of memory
7648  // ====================================================================
7649  unsigned n_chunk = values_chunk.size();
7650  nnz[m]=0;
7651  for (unsigned c = 0; c < n_chunk; c++)
7652  {
7653  nnz[m]+=ncoef_in_chunk[c];
7654  }
7656 
7657  // allocate
7658  values[m] = new double[nnz[m]];
7659  column_indices[m] = new int[nnz[m]];
7660 
7661  // copy
7662  unsigned pt = 0;
7663  for (unsigned c = 0; c < n_chunk; c++)
7664  {
7665  unsigned nc = ncoef_in_chunk[c];
7666  for (unsigned i = 0; i < nc; i++)
7667  {
7668  values[m][pt+i]=values_chunk[c][i];
7669  column_indices[m][pt+i]=column_indices_chunk[c][i];
7670  }
7671  pt += nc;
7672  delete[] values_chunk[c];
7673  delete[] column_indices_chunk[c];
7674  }
7675 
7676  // the row_start vector currently contains the number of coefs in each
7677  // row. Update
7678  // ===================================================================
7679  unsigned g = row_start[m][0];
7680  row_start[m][0] = 0;
7681  for (unsigned i = 1; i < target_nrow_local; i++)
7682  {
7683  unsigned h = g+row_start[m][i];
7684  row_start[m][i] = g;
7685  g=h;
7686  }
7687  row_start[m][target_nrow_local]=g;
7688  }
7689 
7690  // next accumulate the residuals
7691  for (unsigned v = 0; v < n_vector; v++)
7692  {
7693  residuals[v] = new double[target_nrow_local];
7694  for (unsigned i = 0; i < target_nrow_local; i++)
7695  {
7696  residuals[v][i] = 0;
7697  }
7698  for (unsigned p = 0; p < nproc; p++)
7699  {
7700  if (n_eqn_from_proc[p] > 0)
7701  {
7702  unsigned n_eqn_p = n_eqn_from_proc[p];
7703  for (unsigned i = 0; i < n_eqn_p; i++)
7704  {
7705  residuals[v][eqns_from_proc[p][i]]+=residuals_from_proc(p,v)[i];
7706  }
7707  delete[] residuals_from_proc(p,v);
7708  }
7709  }
7710  }
7711 
7712  // delete list of eqns from proc
7713  for (unsigned p = 0; p < nproc; p++)
7714  {
7715  if (n_eqn_from_proc[p] > 0)
7716  {
7717  delete[] eqns_from_proc[p];
7718  }
7719  }
7720 
7721  // and wait for sends to complete
7722  Vector<MPI_Status> send_nnz_stat(nproc-1);
7723  MPI_Waitall(nproc-1,&send_nnz_reqs[0],&send_nnz_stat[0]);
7724  for (unsigned p = 0; p < nproc; p++)
7725  {
7726  if (p != my_rank)
7727  {
7728  delete[] temp_send_storage[p];
7729  }
7730  }
7731  send_nnz_stat.clear();
7732  send_nnz_reqs.clear();
7733 
7734  // wait for the matrix data sends to complete and delete the data
7735  unsigned n_send_reqs = send_reqs.size();
7736  if (n_send_reqs > 0)
7737  {
7738  Vector<MPI_Status> send_stat(n_send_reqs);
7739  MPI_Waitall(n_send_reqs,&send_reqs[0],&send_stat[0]);
7740  for (unsigned p = 0; p < nproc; p++)
7741  {
7742  if (p != my_rank)
7743  {
7744  if (n_eqn_for_proc[p])
7745  {
7746  delete[] eqns_for_proc[p];
7747  for (unsigned m = 0; m < n_matrix; m++)
7748  {
7749  delete[] row_start_for_proc(p,m);
7750  delete[] column_indices_for_proc(p,m);
7751  delete[] values_for_proc(p,m);
7752  }
7753  for (unsigned v = 0; v < n_vector; v++)
7754  {
7755  delete[] residuals_for_proc(p,v);
7756  }
7757  }
7758  }
7759  }
7760  }
7761 
7762  // Doc?
7764  {
7765  t_end=TimingHelpers::timer();
7766  t_local=t_end-t_start;
7767  t_max=0.0;
7768  t_min=0.0;
7769  t_sum=0.0;
7770  MPI_Allreduce(&t_local,&t_max,1,
7771  MPI_DOUBLE,MPI_MAX,
7772  this->communicator_pt()->mpi_comm());
7773  MPI_Allreduce(&t_local,&t_min,1,
7774  MPI_DOUBLE,MPI_MIN,
7775  this->communicator_pt()->mpi_comm());
7776  MPI_Allreduce(&t_local,&t_sum,1,
7777  MPI_DOUBLE,MPI_SUM,
7778  this->communicator_pt()->mpi_comm());
7779  double imbalance=(t_max-t_min)/(t_sum/double(nproc))*100.0;
7780  if (doing_residuals)
7781  {
7782  oomph_info
7783  << "CPU for residual distribut. (loc/max/min/imbal): ";
7784  }
7785  else
7786  {
7787  oomph_info
7788  << "CPU for Jacobian distribut. (loc/max/min/imbal): ";
7789  }
7790  oomph_info
7791  << t_local << " "
7792  << t_max << " "
7793  << t_min << " "
7794  << imbalance << "%\n\n";
7795  }
7796 
7797 }
7798 
7799 #endif
7800 
7801 
7802 //================================================================
7803 /// \short Get the full Jacobian by finite differencing
7804 //================================================================
7806  DenseMatrix<double> &jacobian)
7807 {
7808 
7809 #ifdef OOMPH_HAS_MPI
7810 
7812  {
7813  OomphLibWarning("This is unlikely to work with a distributed problem",
7814  " Problem::get_fd_jacobian()",
7815  OOMPH_EXCEPTION_LOCATION);
7816  }
7817 #endif
7818 
7819 
7820  //Find number of dofs
7821  const unsigned long n_dof = ndof();
7822 
7823 // Advanced residuals
7824  DoubleVector residuals_pls;
7825 
7826  // Get reference residuals
7827  get_residuals(residuals);
7828 
7829  const double FD_step=1.0e-8;
7830 
7831  // Make sure the Jacobian is the right size (since we don't care about speed).
7832  jacobian.resize(n_dof,n_dof);
7833 
7834  //Loop over all dofs
7835  for(unsigned long jdof=0;jdof<n_dof;jdof++)
7836  {
7837  double backup=*Dof_pt[jdof];
7838  *Dof_pt[jdof]+=FD_step;
7839 
7840  // We're checking if the new values for Dof_pt[] actually
7841  // solve the entire problem --> update as if problem had
7842  // been solved
7846 
7847  // Get advanced residuals
7848  get_residuals(residuals_pls);
7849 
7850  for (unsigned long ieqn=0;ieqn<n_dof;ieqn++)
7851  {
7852  jacobian(ieqn,jdof)=(residuals_pls[ieqn]-residuals[ieqn])/FD_step;
7853  }
7854 
7855  *Dof_pt[jdof]=backup;
7856  }
7857 
7858  // Reset problem to state it was in
7862 
7863 }
7864 
7865 //======================================================================
7866 /// \short Get derivative of the residuals vector wrt a global parameter
7867 /// This is required in continuation problems
7868 //=======================================================================
7869 void Problem::get_derivative_wrt_global_parameter(double* const &parameter_pt,
7870  DoubleVector &result)
7871 {
7872  //If we are doing the calculation analytically then call the appropriate
7873  //handler and then calling get_residuals
7874  if(is_dparameter_calculated_analytically(parameter_pt))
7875  {
7876  //Locally cache pointer to assembly handler
7877  AssemblyHandler* const old_assembly_handler_pt = Assembly_handler_pt;
7878  //Create a new assembly handler that replaces get_residuals by
7879  //get_dresiduals_dparameter for each element
7881  old_assembly_handler_pt,parameter_pt);
7882  //Get the residuals, which will be dresiduals by dparameter
7883  this->get_residuals(result);
7884  //Delete the parameter derivative handler
7885  delete Assembly_handler_pt;
7886  //Reset the assembly handler to the original handler
7887  Assembly_handler_pt = old_assembly_handler_pt;
7888 
7889  /*AssemblyHandler* const assembly_handler_pt = Assembly_handler_pt;
7890  //Loop over all the elements
7891  unsigned long Element_pt_range = Mesh_pt->nelement();
7892  for(unsigned long e=0;e<Element_pt_range;e++)
7893  {
7894  //Get the pointer to the element
7895  GeneralisedElement* elem_pt = Mesh_pt->element_pt(e);
7896  //Find number of dofs in the element
7897  unsigned n_element_dofs = assembly_handler_pt->ndof(elem_pt);
7898  //Set up an array
7899  Vector<double> element_residuals(n_element_dofs);
7900  //Fill the array
7901  assembly_handler_pt->get_dresiduals_dparameter(elem_pt,parameter_pt,
7902  element_residuals);
7903  //Now loop over the dofs and assign values to global Vector
7904  for(unsigned l=0;l<n_element_dofs;l++)
7905  {
7906  result[assembly_handler_pt->eqn_number(elem_pt,l)]
7907  += element_residuals[l];
7908  }
7909  }*/
7910 
7911  //for(unsigned n=0;n<n_dof;n++)
7912  // {std::cout << "BLA " << n << " " << result[n] << "\n";}
7913  }
7914  //Otherwise use the finite difference default
7915  else
7916  {
7917  //Get the (global) residuals and store in the result vector
7918  get_residuals(result);
7919 
7920  //Storage for the new residuals
7921  DoubleVector newres;
7922 
7923  //Increase the global parameter
7924  const double FD_step = 1.0e-8;
7925 
7926  //Store the current value of the parameter
7927  double param_value = *parameter_pt;
7928 
7929  //Increase the parameter
7930  *parameter_pt += FD_step;
7931 
7932  //Do any possible updates
7934 
7935  //Get the new residuals
7936  get_residuals(newres);
7937 
7938  //Find the number of local rows
7939  //(I think it's a global vector, so that should be fine)
7940  const unsigned ndof_local = result.nrow_local();
7941 
7942  //Do the finite differencing in the local variables
7943  for(unsigned n=0;n<ndof_local;++n)
7944  {
7945  result[n] = (newres[n] - result[n])/FD_step;
7946  }
7947 
7948  //Reset the value of the parameter
7949  *parameter_pt = param_value;
7950 
7951  //Do any possible updates
7953  }
7954 
7955 }
7956 
7957 
7958 //======================================================================
7959 /// Return the product of the global hessian (derivative of Jacobian
7960 /// matrix with respect to all variables) with
7961 /// an eigenvector, Y, and any number of other specified vectors C
7962 /// (d(J_{ij})/d u_{k}) Y_{j} C_{k}.
7963 /// This function is used in assembling and solving the augmented systems
7964 /// associated with bifurcation tracking.
7965 /// The default implementation is to use finite differences at the global
7966 /// level.
7967 //========================================================================
7969  DoubleVectorWithHaloEntries const &Y,
7972 {
7973  //How many vector products must we construct
7974  const unsigned n_vec = C.size();
7975 
7976  // currently only global (non-distributed) distributions are allowed
7977  //LinearAlgebraDistribution* dist_pt = new
7978  // LinearAlgebraDistribution(Communicator_pt,n_dof,false);
7979 
7980  // Cache the assembly hander
7982 
7983  // Rebuild the results vectors and initialise to zero
7984  // use the same distribution of the vector Y
7985 for(unsigned i=0;i<n_vec;i++)
7986  {
7987  product[i].build(Y.distribution_pt(),0.0);
7988  product[i].initialise(0.0);
7989  }
7990 
7991 //Setup the halo schemes for the result
7992 #ifdef OOMPH_HAS_MPI
7994  {
7995  for(unsigned i=0;i<n_vec;i++)
7996  {
7997  product[i].build_halo_scheme(this->Halo_scheme_pt);
7998  }
7999  }
8000 #endif
8001 
8002  //If we are doing the calculation analytically then call the appropriate
8003  //handler
8004  //A better way to do this is probably to hook into the get_residuals
8005  //framework but with a different member function of the assembly
8006  //handler
8008  {
8009  //Loop over all the elements
8010  unsigned long Element_pt_range = Mesh_pt->nelement();
8011  for(unsigned long e=0;e<Element_pt_range;e++)
8012  {
8013  //Get the pointer to the element
8014  GeneralisedElement* elem_pt = Mesh_pt->element_pt(e);
8015 //Do not loop over halo elements
8016 #ifdef OOMPH_HAS_MPI
8017  if(!elem_pt->is_halo())
8018  {
8019 #endif
8020  //Find number of dofs in the element
8021  unsigned n_var = assembly_handler_pt->ndof(elem_pt);
8022  //Set up a matrix for the input and output
8023  Vector<double> Y_local(n_var);
8024  DenseMatrix<double> C_local(n_vec,n_var);
8025  DenseMatrix<double> product_local(n_vec,n_var);
8026 
8027  //Translate the global input vectors into the local storage
8028  //Probably horribly inefficient, but otherwise things get really messy
8029  //at the elemental level
8030  for(unsigned l=0;l<n_var;l++)
8031  {
8032  //Cache the global equation number
8033  const unsigned long eqn_number =
8034  assembly_handler_pt->eqn_number(elem_pt,l);
8035 
8036  Y_local[l] = Y.global_value(eqn_number);
8037  for(unsigned i=0;i<n_vec;i++)
8038  {
8039  C_local(i,l) = C[i].global_value(eqn_number);
8040  }
8041  }
8042 
8043  //Fill the array
8044  assembly_handler_pt->get_hessian_vector_products(elem_pt,Y_local,
8045  C_local,product_local);
8046 
8047  //Assign the local results to the global vector
8048  for(unsigned l=0;l<n_var;l++)
8049  {
8050  const unsigned long eqn_number =
8051  assembly_handler_pt->eqn_number(elem_pt,l);
8052 
8053  for(unsigned i=0;i<n_vec;i++)
8054  {
8055  product[i].global_value(eqn_number) += product_local(i,l);
8056  //std::cout << "BLA " << e << " " << i << " "
8057  // << l << " " << product_local(i,l) << "\n";
8058  }
8059  }
8060 #ifdef OOMPH_HAS_MPI
8061  }
8062 #endif
8063  }
8064  }
8065  //Otherwise calculate using finite differences by
8066  //perturbing the jacobian along a particular direction
8067  else
8068  {
8069  //Cache the finite difference step
8070  /// Alice: My bifurcation tracking converges better with this FD_step
8071  /// as 1.0e-5. The default value remains at 1.0e-8.
8073 
8074  //We can now construct our multipliers
8075  const unsigned n_dof_local = this->Dof_distribution_pt->nrow_local();
8076  //Prepare to scale
8077  double dof_length=0.0;
8078  Vector<double> C_length(n_vec,0.0);
8079 
8080  for(unsigned n=0;n<n_dof_local;n++)
8081  {
8082  if(std::fabs(this->dof(n)) > dof_length)
8083  {dof_length = std::fabs(this->dof(n));}
8084  }
8085 
8086  //C is assumed to have the same distribution as the dofs
8087  for(unsigned i=0;i<n_vec;i++)
8088  {
8089  for(unsigned n=0;n<n_dof_local;n++)
8090  {
8091  if(std::fabs(C[i][n]) > C_length[i]) {C_length[i] = std::fabs(C[i][n]);}
8092  }
8093  }
8094 
8095  //Now broadcast the information, if distributed
8096 #ifdef OOMPH_HAS_MPI
8098  {
8099  const unsigned n_length = n_vec+1;
8100  double all_length[n_length];
8101  all_length[0] = dof_length;
8102  for(unsigned i=0;i<n_vec;i++) {all_length[i+1] = C_length[i];}
8103 
8104  //Do the MPI call
8105  double all_length_reduce[n_length];
8106  MPI_Allreduce(all_length,all_length_reduce,n_length,MPI_DOUBLE,
8107  MPI_MAX,this->communicator_pt()->mpi_comm());
8108 
8109  //Read out the information
8110  dof_length = all_length_reduce[0];
8111  for(unsigned i=0;i<n_vec;i++) {C_length[i] = all_length_reduce[i+1];}
8112  }
8113 #endif
8114 
8115  //Form the multipliers
8116  Vector<double> C_mult(n_vec,0.0);
8117  for(unsigned i=0;i<n_vec;i++)
8118  {
8119  C_mult[i] = dof_length/C_length[i];
8120  C_mult[i] += FD_step;
8121  C_mult[i] *= FD_step;
8122  }
8123 
8124 
8125  //Dummy vector to stand in the place of the residuals
8126  Vector<double> dummy_res;
8127 
8128  //Calculate the product of the jacobian matrices, etc by looping over the
8129  //elements
8130  const unsigned long n_element = this->mesh_pt()->nelement();
8131  for(unsigned long e = 0;e<n_element;e++)
8132  {
8133  GeneralisedElement *elem_pt = this->mesh_pt()->element_pt(e);
8134  //Ignore halo's of course
8135 #ifdef OOMPH_HAS_MPI
8136  if(!elem_pt->is_halo())
8137  {
8138 #endif
8139  //Loop over the ndofs in each element
8140  unsigned n_var = assembly_handler_pt->ndof(elem_pt);
8141  //Resize the dummy residuals vector
8142  dummy_res.resize(n_var);
8143  //Allocate storage for the unperturbed jacobian matrix
8144  DenseMatrix<double> jac(n_var);
8145  //Get unperturbed jacobian
8146  assembly_handler_pt->get_jacobian(elem_pt,dummy_res,jac);
8147 
8148  //Backup the dofs
8149  Vector<double> dof_bac(n_var);
8150  for(unsigned n=0;n<n_var;n++)
8151  {
8152  unsigned eqn_number = assembly_handler_pt->eqn_number(elem_pt,n);
8153  dof_bac[n] = *this->global_dof_pt(eqn_number);
8154  }
8155 
8156  //Now loop over all vectors C
8157  for(unsigned i=0;i<n_vec;i++)
8158  {
8159  //Perturb the dofs by the appropriate vector
8160  for(unsigned n=0;n<n_var;n++)
8161  {
8162  unsigned eqn_number = assembly_handler_pt->eqn_number(elem_pt,n);
8163  //Perturb by vector C[i]
8164  *this->global_dof_pt(eqn_number) +=
8165  C_mult[i]*C[i].global_value(eqn_number);
8166  }
8168 
8169  //Allocate storage for the perturbed jacobian
8170  DenseMatrix<double> jac_C(n_var);
8171 
8172  //Now get the new jacobian
8173  assembly_handler_pt->get_jacobian(elem_pt,dummy_res,jac_C);
8174 
8175  //Reset the dofs
8176  for(unsigned n=0;n<n_var;n++)
8177  {
8178  unsigned eqn_number = assembly_handler_pt->eqn_number(elem_pt,n);
8179  *this->global_dof_pt(eqn_number) = dof_bac[n];
8180  }
8182 
8183  //Now work out the products
8184  for(unsigned n=0;n<n_var;n++)
8185  {
8186  unsigned eqn_number = assembly_handler_pt->eqn_number(elem_pt,n);
8187  double prod_c=0.0;
8188  for(unsigned m=0;m<n_var;m++)
8189  {
8190  unsigned unknown = assembly_handler_pt->eqn_number(elem_pt,m);
8191  prod_c += (jac_C(n,m) - jac(n,m))*Y.global_value(unknown);
8192  }
8193  //std::cout << "FD " << e << " " << i << " "
8194  // << n << " " << prod_c/C_mult[i] << "\n";
8195  product[i].global_value(eqn_number) += prod_c/C_mult[i];
8196  }
8197  }
8198 #ifdef OOMPH_HAS_MPI
8199  }
8200 #endif
8201  } //End of loop over elements
8202  }
8203 
8204  //If we have a distributed problem then gather all
8205  //values
8206 #ifdef OOMPH_HAS_MPI
8208  {
8209  //Sum all values if distributed
8210  for(unsigned i=0;i<n_vec;i++)
8211  {
8212  product[i].sum_all_halo_and_haloed_values();
8213  }
8214  }
8215 #endif
8216 
8217 
8218 }
8219 
8220 
8221 //================================================================
8222 /// \short Get derivative of an element in the problem wrt a global
8223 /// parameter, to be used in continuation problems
8224 //================================================================
8225 /*void Problem::get_derivative_wrt_global_parameter(
8226  double* const &parameter_pt,
8227  GeneralisedElement* const &elem_pt,
8228  Vector<double> &result)
8229 {
8230 
8231 #ifdef OOMPH_HAS_MPI
8232 
8233  if (Problem_has_been_distributed)
8234  {
8235  OomphLibWarning("This is unlikely to work with a distributed problem",
8236  "Problem::get_derivative_wrt_global_parameter()",
8237  OOMPH_EXCEPTION_LOCATION);
8238  }
8239 #endif
8240 
8241  //Locally cache pointer to assembly handler
8242  AssemblyHandler* const assembly_handler_pt = Assembly_handler_pt;
8243 
8244  //Should definitely give this a more global scope
8245  double FD_Jstep = 1.0e-8;
8246 
8247  //Find the number of variables in the element, e
8248  unsigned nvar = assembly_handler_pt->ndof(elem_pt);
8249  //Create storage for residuals
8250  Vector<double> residuals(nvar), newres(nvar);
8251 
8252  //Get the "original" residuals
8253  assembly_handler_pt->get_residuals(elem_pt,residuals);
8254 
8255  //Save the old value of the global parameter
8256  double old_var = *parameter_pt;
8257 
8258  //Increment the value
8259  *parameter_pt += FD_Jstep;
8260 
8261  //Now do any possible updates
8262  actions_after_change_in_global_parameter();
8263 
8264  //Get the "new" residuals
8265  assembly_handler_pt->get_residuals(elem_pt,newres);
8266 
8267  //Do the finite differences
8268  for(unsigned m=0;m<nvar;m++)
8269  {
8270  result[m] = (newres[m] - residuals[m])/FD_Jstep;
8271  }
8272 
8273  //Reset value of the global parameter
8274  *parameter_pt = old_var;
8275 
8276  //Now do any possible updates
8277  actions_after_change_in_global_parameter();
8278 }*/
8279 
8280 //==================================================================
8281 /// Solve the eigenproblem
8282 //==================================================================
8283 void Problem::solve_eigenproblem(const unsigned &n_eval,
8284  Vector<std::complex<double> > &eigenvalue,
8285  Vector<DoubleVector> &eigenvector,
8286  const bool &steady)
8287 {
8288  //If the boolean flag is steady, then make all the timesteppers steady
8289  //before solving the eigenproblem. This will "switch off" the time-derivative
8290  //terms in the jacobian matrix
8291  if(steady)
8292  {
8293  //Find out how many timesteppers there are
8294  const unsigned n_time_steppers = ntime_stepper();
8295 
8296  // Vector of bools to store the is_steady status of the various
8297  // timesteppers when we came in here
8298  std::vector<bool> was_steady(n_time_steppers);
8299 
8300  //Loop over them all and make them (temporarily) static
8301  for(unsigned i=0;i<n_time_steppers;i++)
8302  {
8303  was_steady[i]=time_stepper_pt(i)->is_steady();
8305  }
8306 
8307  //Call the Eigenproblem for the eigensolver
8308  Eigen_solver_pt->solve_eigenproblem(this,n_eval,eigenvalue,eigenvector);
8309 
8310  // Reset the is_steady status of all timesteppers that
8311  // weren't already steady when we came in here and reset their
8312  // weights
8313  for(unsigned i=0;i<n_time_steppers;i++)
8314  {
8315  if (!was_steady[i])
8316  {
8318  }
8319  }
8320  }
8321  //Otherwise if we don't want to make the problem steady, just
8322  //assemble and solve the eigensystem
8323  else
8324  {
8325  //Call the Eigenproblem for the eigensolver
8326  Eigen_solver_pt->solve_eigenproblem(this,n_eval,eigenvalue,eigenvector);
8327  }
8328 }
8329 
8330 
8331 
8332 
8333 //===================================================================
8334 /// Get the matrices required to solve an eigenproblem
8335 /// WARNING: temporarily this method only works with non-distributed
8336 /// matrices
8337 //===================================================================
8339  CRDoubleMatrix &main_matrix,
8340  const double &shift)
8341 {
8342  // Three different cases again here:
8343  // 1) Compiled with MPI, but run in serial
8344  // 2) Compiled with MPI, but MPI not initialised in driver
8345  // 3) Serial version
8346 
8347 
8348 #ifdef PARANOID
8349  if (mass_matrix.distribution_built() && main_matrix.distribution_built())
8350  {
8351  //Check that the distribution of the mass matrix and jacobian match
8352  if(!(*mass_matrix.distribution_pt() == *main_matrix.distribution_pt()))
8353  {
8354  std::ostringstream error_stream;
8355  error_stream
8356  << "The distributions of the jacobian and mass matrix are\n"
8357  << "not the same and they must be.\n";
8358  throw OomphLibError(error_stream.str(),
8359  OOMPH_CURRENT_FUNCTION,
8360  OOMPH_EXCEPTION_LOCATION);
8361  }
8362 
8363  if (mass_matrix.nrow() != this->ndof())
8364  {
8365  std::ostringstream error_stream;
8366  error_stream
8367  << "mass_matrix has a distribution, but the number of rows is not "
8368  << "equal to the number of degrees of freedom in the problem.";
8369  throw OomphLibError(error_stream.str(),
8370  OOMPH_CURRENT_FUNCTION,
8371  OOMPH_EXCEPTION_LOCATION);
8372  }
8373 
8374  if (main_matrix.nrow() != this->ndof())
8375  {
8376  std::ostringstream error_stream;
8377  error_stream
8378  << "main_matrix has a distribution, but the number of rows is not "
8379  << "equal to the number of degrees of freedom in the problem.";
8380  throw OomphLibError(error_stream.str(),
8381  OOMPH_CURRENT_FUNCTION,
8382  OOMPH_EXCEPTION_LOCATION);
8383  }
8384  }
8385  //If the distributions are not the same, then complain
8386  else if(main_matrix.distribution_built() != mass_matrix.distribution_built())
8387  {
8388  std::ostringstream error_stream;
8389  error_stream << "The distribution of the jacobian and mass matrix must "
8390  << "both be setup or both not setup";
8391  throw OomphLibError(error_stream.str(),
8392  OOMPH_CURRENT_FUNCTION,
8393  OOMPH_EXCEPTION_LOCATION);
8394  }
8395 #endif
8396 
8397  //Store the old assembly handler
8398  AssemblyHandler* old_assembly_handler_pt = Assembly_handler_pt;
8399  //Now setup the eigenproblem handler, pass in the value of the shift
8401 
8402  //Prepare the storage formats.
8403  Vector<int* > column_or_row_index(2);
8404  Vector<int* > row_or_column_start(2);
8405  Vector<double* > value(2);
8406  Vector<unsigned> nnz(2);
8407  //Allocate pointer to residuals, although not used in these problems
8408  Vector<double* > residuals_vectors(0);
8409 
8410  // total number of rows in each matrix
8411  unsigned nrow = this->ndof();
8412 
8413  // determine the distribution for the jacobian (main matrix)
8414  // IF the jacobian has distribution setup then use that
8415  // ELSE determine the distribution based on the
8416  // distributed_matrix_distribution enum
8417  LinearAlgebraDistribution* dist_pt=0;
8418  if (main_matrix.distribution_built())
8419  {
8420  dist_pt = new LinearAlgebraDistribution(main_matrix.distribution_pt());
8421  }
8422  else
8423  {
8424 #ifdef OOMPH_HAS_MPI
8425  // if problem is only one one processor
8426  if (Communicator_pt->nproc() == 1)
8427  {
8428  dist_pt = new LinearAlgebraDistribution(Communicator_pt,nrow,false);
8429  }
8430  // if the problem is not distributed then assemble the matrices with
8431  // a uniform distributed distribution
8432  else if (!Problem_has_been_distributed)
8433  {
8434  dist_pt = new LinearAlgebraDistribution(Communicator_pt,nrow,true);
8435  }
8436  // otherwise the problem is a distributed problem
8437  else
8438  {
8440  {
8442  dist_pt = new LinearAlgebraDistribution(Communicator_pt,nrow,true);
8443  break;
8446  break;
8448  LinearAlgebraDistribution* uniform_dist_pt =
8450  bool use_problem_dist = true;
8451  unsigned nproc = Communicator_pt->nproc();
8452  for (unsigned p = 0; p < nproc; p++)
8453  {
8454  if ((double)Dof_distribution_pt->nrow_local(p) >
8455  ((double)uniform_dist_pt->nrow_local(p))*1.1)
8456  {
8457  use_problem_dist = false;
8458  }
8459  }
8460  if (use_problem_dist)
8461  {
8463  }
8464  else
8465  {
8466  dist_pt = new LinearAlgebraDistribution(uniform_dist_pt);
8467  }
8468  delete uniform_dist_pt;
8469  break;
8470  }
8471  }
8472 #else
8473  dist_pt = new LinearAlgebraDistribution(Communicator_pt,nrow,false);
8474 #endif
8475  }
8476 
8477 
8478  //The matrix is in compressed row format
8479  bool compressed_row_flag=true;
8480 
8481 #ifdef OOMPH_HAS_MPI
8482  //
8483  if (Communicator_pt->nproc() == 1)
8484  {
8485 #endif
8486 
8487  sparse_assemble_row_or_column_compressed(column_or_row_index,
8488  row_or_column_start,
8489  value,
8490  nnz,
8491  residuals_vectors,
8492  compressed_row_flag);
8493 
8494  //The main matrix is the first entry
8495  main_matrix.build(dist_pt);
8496  main_matrix.build_without_copy(dist_pt->nrow(),nnz[0],value[0],
8497  column_or_row_index[0],
8498  row_or_column_start[0]);
8499  //The mass matrix is the second entry
8500  mass_matrix.build(dist_pt);
8501  mass_matrix.build_without_copy(dist_pt->nrow(),nnz[1],value[1],
8502  column_or_row_index[1],
8503  row_or_column_start[1]);
8504 #ifdef OOMPH_HAS_MPI
8505  }
8506  else
8507  {
8508  if (dist_pt->distributed())
8509  {
8510  parallel_sparse_assemble(dist_pt,
8511  column_or_row_index,
8512  row_or_column_start,
8513  value,
8514  nnz,
8515  residuals_vectors);
8516  //The main matrix is the first entry
8517  main_matrix.build(dist_pt);
8518  main_matrix.build_without_copy(dist_pt->nrow(),nnz[0],
8519  value[0],column_or_row_index[0],
8520  row_or_column_start[0]);
8521  //The mass matrix is the second entry
8522  mass_matrix.build(dist_pt);
8523  mass_matrix.build_without_copy(dist_pt->nrow(),nnz[1],value[1],
8524  column_or_row_index[1],
8525  row_or_column_start[1]);
8526 
8527  }
8528  else
8529  {
8530  LinearAlgebraDistribution* temp_dist_pt =
8531  new LinearAlgebraDistribution(Communicator_pt,dist_pt->nrow(),true);
8532  parallel_sparse_assemble(temp_dist_pt,
8533  column_or_row_index,
8534  row_or_column_start,
8535  value,
8536  nnz,
8537  residuals_vectors);
8538  //The main matrix is the first entry
8539  main_matrix.build(temp_dist_pt);
8540  main_matrix.build_without_copy(dist_pt->nrow(),nnz[0],
8541  value[0],column_or_row_index[0],
8542  row_or_column_start[0]);
8543  main_matrix.redistribute(dist_pt);
8544  //The mass matrix is the second entry
8545  mass_matrix.build(temp_dist_pt);
8546  mass_matrix.build_without_copy(dist_pt->nrow(),nnz[1],value[1],
8547  column_or_row_index[1],
8548  row_or_column_start[1]);
8549  mass_matrix.redistribute(dist_pt);
8550  delete temp_dist_pt;
8551  }
8552  }
8553 #endif
8554 
8555  // clean up dist_pt and residuals_vector pt
8556  delete dist_pt;
8557 
8558  //Delete the eigenproblem handler
8559  delete Assembly_handler_pt;
8560  //Reset the assembly handler to the original handler
8561  Assembly_handler_pt = old_assembly_handler_pt;
8562 }
8563 
8564 
8565 //=======================================================================
8566 /// Stored the current values of the dofs
8567 //=======================================================================
8569 {
8570 
8571  //If memory has not been allocated, then allocated memory for the saved
8572  //dofs
8573  if (Saved_dof_pt==0) {Saved_dof_pt = new Vector<double>;}
8574 
8575 #ifdef OOMPH_HAS_MPI
8576  //If the problem is distributed I have to do something different
8578  {
8579  // How many entries do we store locally?
8580  const unsigned n_row_local =
8582 
8583  //Resize the vector
8584  Saved_dof_pt->resize(n_row_local);
8585 
8586  // Back 'em up
8587  for (unsigned i=0;i<n_row_local;i++)
8588  {
8589  (*Saved_dof_pt)[i]=*(this->Dof_pt[i]);
8590  }
8591  }
8592  //Otherwise just store all the dofs
8593  else
8594 #endif
8595  {
8596  //Find the number of dofs
8597  unsigned long n_dof = ndof();
8598 
8599  //Resize the vector
8600  Saved_dof_pt->resize(n_dof);
8601 
8602  //Transfer the values over
8603  for(unsigned long n=0;n<n_dof;n++) {(*Saved_dof_pt)[n] = dof(n);}
8604  }
8605 }
8606 
8607 //====================================================================
8608 /// Restore the saved dofs
8609 //====================================================================
8611 {
8612  //Check that we can do this
8613  if(Saved_dof_pt==0)
8614  {
8615  throw OomphLibError(
8616  "There are no stored values, use store_current_dof_values()\n",
8617  OOMPH_CURRENT_FUNCTION,
8618  OOMPH_EXCEPTION_LOCATION);
8619  }
8620 
8621 
8622 #ifdef OOMPH_HAS_MPI
8623  //If the problem is distributed I have to do something different
8625  {
8626 
8627  // How many entries do we store locally?
8628  const unsigned n_row_local =
8630 
8631  if(Saved_dof_pt->size() != n_row_local)
8632  {
8633  throw OomphLibError(
8634  "The number of stored values is not equal to the current number of dofs\n",
8635 OOMPH_CURRENT_FUNCTION,
8636  OOMPH_EXCEPTION_LOCATION);
8637  }
8638 
8639  //Transfer the values over
8640  for(unsigned long n=0;n<n_row_local;n++)
8641  {
8642  *(this->Dof_pt[n]) = (*Saved_dof_pt)[n];
8643  }
8644  }
8645  //Otherwise just restore all the dofs
8646  else
8647 #endif
8648  {
8649  //Find the number of dofs
8650  unsigned long n_dof = ndof();
8651 
8652  if(Saved_dof_pt->size() != n_dof)
8653  {
8654  throw OomphLibError(
8655  "The number of stored values is not equal to the current number of dofs\n",
8656 OOMPH_CURRENT_FUNCTION,
8657  OOMPH_EXCEPTION_LOCATION);
8658  }
8659 
8660  //Transfer the values over
8661  for(unsigned long n=0;n<n_dof;n++) {dof(n) = (*Saved_dof_pt)[n];}
8662  }
8663 
8664  //Delete the memory
8665  delete Saved_dof_pt;
8666  Saved_dof_pt = 0;
8667 }
8668 
8669 //======================================================================
8670 /// Assign the eigenvector passed to the function to the dofs
8671 //======================================================================
8673 {
8674  unsigned long n_dof = ndof();
8675  //Check that the eigenvector has the correct size
8676  if(eigenvector.nrow() != n_dof)
8677  {
8678  std::ostringstream error_message;
8679  error_message << "Eigenvector has size " << eigenvector.nrow()
8680  << ", not equal to the number of dofs in the problem,"
8681  << n_dof << std::endl;
8682 
8683  throw OomphLibError(error_message.str(),
8684 OOMPH_CURRENT_FUNCTION,
8685  OOMPH_EXCEPTION_LOCATION);
8686  }
8687 
8688  //Loop over the dofs and assign the eigenvector
8689  for(unsigned long n=0;n<eigenvector.nrow_local();n++)
8690  {
8691  dof(n) = eigenvector[n];
8692  }
8693 //Of course we now need to synchronise
8694 #ifdef OOMPH_HAS_MPI
8695  this->synchronise_all_dofs();
8696 #endif
8697 }
8698 
8699 
8700 
8701 //======================================================================
8702 /// Add the eigenvector passed to the function to the dofs with
8703 /// magnitude epsilon
8704 //======================================================================
8705 void Problem::add_eigenvector_to_dofs(const double &epsilon,
8706  const DoubleVector &eigenvector)
8707 {
8708  unsigned long n_dof = ndof();
8709  //Check that the eigenvector has the correct size
8710  if(eigenvector.nrow() != n_dof)
8711  {
8712  std::ostringstream error_message;
8713  error_message << "Eigenvector has size " << eigenvector.nrow()
8714  << ", not equal to the number of dofs in the problem,"
8715  << n_dof << std::endl;
8716 
8717  throw OomphLibError(error_message.str(),
8718 OOMPH_CURRENT_FUNCTION,
8719  OOMPH_EXCEPTION_LOCATION);
8720  }
8721 
8722  //Loop over the dofs and add the eigenvector
8723  //Only use local values
8724  for(unsigned long n=0;n<eigenvector.nrow_local();n++)
8725  {
8726  dof(n) += epsilon*eigenvector[n];
8727  }
8728 //Of course we now need to synchronise
8729 #ifdef OOMPH_HAS_MPI
8730  this->synchronise_all_dofs();
8731 #endif
8732 }
8733 
8734 
8735 //================================================================
8736 /// General Newton solver. Requires only a convergence tolerance.
8737 /// The linear solver takes a pointer to the problem (which defines
8738 /// the Jacobian \b J and the residual Vector \b r) and returns
8739 /// the solution \b x of the system
8740 /// \f[ {\bf J} {\bf x} = - \bf{r} \f].
8741 //================================================================
8743 {
8744 
8745  // Initialise timers
8746  double total_linear_solver_time=0.0;
8747  double t_start = TimingHelpers::timer();
8748  Max_res.clear();
8749 
8750  // Find total number of dofs
8751  unsigned long n_dofs = ndof();
8752 
8753  // Set up the Vector to hold the solution
8754  DoubleVector dx;
8755 
8756  //-----Variables for the globally convergent Newton method------
8757 
8758  // Set up the vector to hold the gradient
8759  DoubleVector gradient;
8760 
8761  // Other variables
8762  double half_residual_squared=0.0;
8763  double max_step=0.0;
8764 
8765  //--------------------------------------------------------------
8766 
8767  // Set the counter
8768  unsigned count=0;
8769  // Set the loop flag
8770  unsigned LOOP_FLAG=1;
8771 
8773  {
8774 #ifdef OOMPH_HAS_MPI
8775  // Break if running in parallel
8777  {
8778  std::ostringstream error_stream;
8779  error_stream << "Globally convergent Newton method has not been "
8780  << "implemented in parallel yet!"
8781  << std::endl;
8782  throw OomphLibError(error_stream.str(),
8783  OOMPH_CURRENT_FUNCTION,
8784  OOMPH_EXCEPTION_LOCATION);
8785  }
8786 #endif
8787 
8788  // Get gradient
8790  // Reset the gradient (clear it), since the number of dofs and
8791  // hence the size of the DoubleVector might have changed
8793  }
8794 
8795  // Update anything that needs updating
8797 
8798  // Reset number of Newton iterations taken
8800 
8801  // Now do the Newton loop
8802  do
8803  {
8804  count++;
8805 
8806  // Do any updates that are required
8808 
8809 
8810  // No degrees of freedom? What are you solving for?
8811  if (n_dofs==0)
8812  {
8813  oomph_info << std::endl << std::endl << std::endl;
8814  oomph_info << "This is a bit bizarre: The problem has no dofs."
8815  << std::endl;
8816  oomph_info
8817  << "I'll just return from the Newton solver without doing anything."
8818  << std::endl;
8819 
8820  // Do any updates that would have been performed
8825 
8826  oomph_info << "I hope this is what you intended me to do..." << std::endl;
8827  oomph_info
8828  << std::endl << "Note: All actions_...() functions were called"
8829  << std::endl;
8830  oomph_info << std::endl << " before returning." << std::endl;
8831  oomph_info << std::endl << std::endl << std::endl;
8832  return;
8833  }
8834 
8835  //Calculate initial residuals
8836  if(count==1)
8837  {
8838  // Is the problem nonlinear? If not ignore the pre-iteration
8839  // convergence check.
8841  {
8842 #ifdef OOMPH_HAS_MPI
8843  // Synchronise the solution on different processors (on each submesh)
8844  this->synchronise_all_dofs();
8845 #endif
8846 
8848  dx.clear();
8849  get_residuals(dx);
8850 
8851  // Get half of squared residual and find maximum step length
8852  // for step length control
8854  {
8855  half_residual_squared=0.0;
8856  double sum=0.0;
8857  for (unsigned i=0;i<n_dofs;i++)
8858  {
8859  sum += (*Dof_pt[i])*(*Dof_pt[i]);
8860  half_residual_squared+=dx[i]*dx[i];
8861  }
8862  half_residual_squared*=0.5;
8863  max_step=100.0*std::max(sqrt(sum),double(n_dofs));
8864  }
8865 
8866  //Get maximum residuals
8867  double maxres = dx.max();
8868  Max_res.push_back(maxres);
8869 
8871  {
8872  // Let's output the residuals
8873  //unsigned n_row_local = dx.distribution_pt()->nrow_local();
8874  //unsigned first_row = dx.distribution_pt()->first_row();
8875  //for(unsigned n=0;n<n_row_local;n++)
8876  //{
8877  //oomph_info << "residual: " << n + first_row << " " << dx[n] << "\n";
8878  //}
8879 
8880  oomph_info << "\nInitial Maximum residuals " << maxres << std::endl;
8881  }
8882 
8883  if((maxres < Newton_solver_tolerance) &&
8884  (!Always_take_one_newton_step)) {LOOP_FLAG=0; continue;}
8885  }
8886  else
8887  {
8889  {
8890  oomph_info
8891  << "Linear problem -- convergence in one iteration assumed."
8892  << std::endl;
8893  }
8894  }
8895  }
8896 
8897 
8898  // Increment number of Newton iterations taken
8900 
8901  // Initialise timer for linear solver
8902  double t_solver_start = TimingHelpers::timer();
8903 
8904  //Now do the linear solve -- recycling Jacobian if requested
8906  {
8908  {
8909  oomph_info << "Not recomputing Jacobian! " << std::endl;
8910  }
8911 
8912  // If we're doing the first iteration and the problem is nonlinear,
8913  // the residuals have already been computed above during the
8914  // initial convergence check. Otherwise compute them here.
8915  if ((count!=1)||(!Problem_is_nonlinear)) get_residuals(dx);
8916 
8917  // Backup residuals
8918  DoubleVector resid(dx);
8919 
8920  // Resolve
8921  Linear_solver_pt->resolve(resid,dx);
8922  }
8923  else
8924  {
8926  {
8928  {
8929  oomph_info << "Enabling resolve" << std::endl;
8930  }
8932  }
8933  Linear_solver_pt->solve(this,dx);
8935  }
8936 
8937  // End of linear solver
8938  double t_solver_end = TimingHelpers::timer();
8939  total_linear_solver_time+=t_solver_end-t_solver_start;
8940 
8942  {
8943  oomph_info << std::endl;
8944  oomph_info << "Time for linear solver ( ndof = "
8945  << n_dofs << " ) [sec]: "
8946  << t_solver_end-t_solver_start
8947  << std::endl << std::endl;
8948  }
8949 
8950  //Subtract the new values from the true dofs
8952  double* dx_pt = dx.values_pt();
8953  unsigned ndof_local = Dof_distribution_pt->nrow_local();
8954 
8956  {
8957  // Get the gradient
8958  Linear_solver_pt->get_gradient(gradient);
8959 
8960  for(unsigned i=0;i<ndof_local;i++)
8961  {
8962  dx_pt[i]*=-1.0;
8963  }
8964 
8965  // Update with steplength control
8966  Vector<double> unknowns_old(ndof_local);
8967 
8968  for(unsigned i=0;i<ndof_local;i++)
8969  {
8970  unknowns_old[i]=*Dof_pt[i];
8971  }
8972 
8973  double half_residual_squared_old=half_residual_squared;
8974  globally_convergent_line_search(unknowns_old,
8975  half_residual_squared_old,
8976  gradient,
8977  dx,
8978  half_residual_squared,
8979  max_step);
8980 
8981  }
8982  // direct Newton update
8983  else
8984  {
8985  for (unsigned l = 0; l < ndof_local; l++)
8986  {
8987  *Dof_pt[l] -= Relaxation_factor*dx_pt[l];
8988  }
8989  }
8990 #ifdef OOMPH_HAS_MPI
8991  // Synchronise the solution on different processors (on each submesh)
8992  this->synchronise_all_dofs();
8993 #endif
8994 
8995  // Do any updates that are required
8998 
8999  // Maximum residuals
9000  double maxres=0.0;
9001  // If the user has declared that the Problem is linear
9002  // we ignore the convergence check
9004  {
9005  //Get the maximum residuals
9006  //maxres = std::fabs(*std::max_element(dx.begin(),dx.end(),
9007  // AbsCmp<double>()));
9008  //oomph_info << "Maxres correction " << maxres << "\n";
9009 
9010  //Calculate the new residuals
9011  dx.clear();
9012  get_residuals(dx);
9013 
9014  //Get the maximum residuals
9015  maxres = dx.max();
9016  Max_res.push_back(maxres);
9017 
9019  {
9020  oomph_info << "Newton Step " << count << ": Maximum residuals "
9021  << maxres << std::endl;
9022  }
9023  }
9024 
9025  //If we have converged jump straight to the test at the end of the loop
9026  if(maxres < Newton_solver_tolerance) {LOOP_FLAG=0; continue;}
9027 
9028  //This section will not be reached if we have converged already
9029  //If the maximum number of residuals is too high or the maximum number
9030  //of iterations has been reached
9031  if((maxres > Max_residuals) || (count == Max_newton_iterations))
9032  {
9033  // Print a warning -- regardless of what the throw does
9034  if (maxres > Max_residuals)
9035  {
9036  oomph_info << "Max. residual (" << Max_residuals
9037  << ") has been exceeded in Newton solver." << std::endl;
9038  }
9039  if (count == Max_newton_iterations)
9040  {
9041  oomph_info << "Reached max. number of iterations ("
9043  << ") in Newton solver." << std::endl;
9044  }
9045  // Now throw...
9046  throw NewtonSolverError(count,maxres);
9047  }
9048 
9049  }
9050  while(LOOP_FLAG);
9051 
9052  //Now update anything that needs updating
9054 
9055  // Finalise/doc timings
9057  {
9058  oomph_info << std::endl;
9059  oomph_info << "Total time for linear solver (ndof="<< n_dofs << ") [sec]: "
9060  << total_linear_solver_time << std::endl;
9061  }
9062 
9063  double t_end = TimingHelpers::timer();
9064  double total_time=t_end-t_start;
9065 
9067  {
9068  oomph_info << "Total time for Newton solver (ndof="<< n_dofs << ") [sec]: "
9069  << total_time << std::endl;
9070  }
9071  if (total_time>0.0)
9072  {
9074  {
9075  oomph_info << "Time outside linear solver : "
9076  << (total_time-total_linear_solver_time)/total_time*100.0
9077  << " %"
9078  << std::endl;
9079  }
9080  }
9081  else
9082  {
9084  {
9085  oomph_info << "Time outside linear solver : "
9086  << "[too fast]"
9087  << std::endl;
9088  }
9089  }
9090  if (!Shut_up_in_newton_solve) oomph_info << std::endl;
9091 }
9092 
9093 //========================================================================
9094 /// Helper function for the globally convergent Newton solver
9095 //========================================================================
9097  const Vector<double>& x_old,
9098  const double& half_residual_squared_old,
9099  DoubleVector& gradient,
9100  DoubleVector& newton_dir,
9101  double& half_residual_squared,
9102  const double& stpmax)
9103 {
9104  const double min_fct_decrease=1.0e-4;
9105  double convergence_tol_on_x=1.0e-16;
9106  double f_aux=0.0;
9107  double lambda_aux=0.0;
9108  double proposed_lambda;
9109  unsigned long n_dof=ndof();
9110  double sum=0.0;
9111  for(unsigned i=0;i<n_dof;i++)
9112  {
9113  sum += newton_dir[i]*newton_dir[i];
9114  }
9115  sum=sqrt(sum);
9116  if(sum > stpmax)
9117  {
9118  for(unsigned i=0;i<n_dof;i++)
9119  {
9120  newton_dir[i] *= stpmax/sum;
9121  }
9122  }
9123  double slope=0.0;
9124  for(unsigned i=0;i<n_dof;i++)
9125  {
9126  slope += gradient[i]*newton_dir[i];
9127  }
9128  if(slope >= 0.0)
9129  {
9130  std::ostringstream warn_message;
9131  warn_message << "WARNING: Non-negative slope, probably due to a "
9132  << " roundoff \nproblem in the linesearch: slope="
9133  << slope << "\n";
9134  OomphLibWarning(warn_message.str(),
9135  "Problem::globally_convergent_line_search()",
9136  OOMPH_EXCEPTION_LOCATION);
9137  }
9138  double test=0.0;
9139  for(unsigned i=0;i<n_dof;i++)
9140  {
9141  double temp=std::fabs(newton_dir[i])/std::max(std::fabs(x_old[i]),1.0);
9142  if(temp > test) test=temp;
9143  }
9144  double lambda_min=convergence_tol_on_x/test;
9145  double lambda=1.0;
9146  while (true)
9147  {
9148  for(unsigned i=0;i<n_dof;i++)
9149  {
9150  *Dof_pt[i]=x_old[i]+lambda*newton_dir[i];
9151  }
9152 
9153  // Evaluate current residuals
9154  DoubleVector residuals;
9155  get_residuals(residuals);
9156  half_residual_squared=0.0;
9157  for(unsigned i=0;i<n_dof;i++)
9158  {
9159  half_residual_squared+=residuals[i]*residuals[i];
9160  }
9161  half_residual_squared*=0.5;
9162 
9163  if (lambda < lambda_min)
9164  {
9165  for(unsigned i=0;i<n_dof;i++) *Dof_pt[i]=x_old[i];
9166 
9167  std::ostringstream warn_message;
9168  warn_message << "WARNING: Line search converged on x only!\n";
9169  OomphLibWarning(warn_message.str(),
9170  "Problem::globally_convergent_line_search()",
9171  OOMPH_EXCEPTION_LOCATION);
9172  return;
9173  }
9174  else if(half_residual_squared <= half_residual_squared_old+
9175  min_fct_decrease*lambda*slope)
9176  {
9177  return;
9178  }
9179  else
9180  {
9181  if(lambda == 1.0)
9182  {
9183  proposed_lambda = -slope/(2.0*(half_residual_squared-
9184  half_residual_squared_old-slope));
9185  }
9186  else
9187  {
9188  double r1=half_residual_squared-half_residual_squared_old-lambda*slope;
9189  double r2=f_aux-half_residual_squared_old-lambda_aux*slope;
9190  double a_poly=(r1/(lambda*lambda)-r2/(lambda_aux*lambda_aux))/
9191  (lambda-lambda_aux);
9192  double b_poly=(-lambda_aux*r1/(lambda*lambda)+lambda*r2/
9193  (lambda_aux*lambda_aux))/(lambda-lambda_aux);
9194  if(a_poly == 0.0)
9195  {
9196  proposed_lambda = -slope/(2.0*b_poly);
9197  }
9198  else
9199  {
9200  double discriminant=b_poly*b_poly-3.0*a_poly*slope;
9201  if(discriminant < 0.0)
9202  {
9203  proposed_lambda=0.5*lambda;
9204  }
9205  else if(b_poly <= 0.0)
9206  {
9207  proposed_lambda=(-b_poly+sqrt(discriminant))/(3.0*a_poly);
9208  }
9209  else
9210  {
9211  proposed_lambda=-slope/(b_poly+sqrt(discriminant));
9212  }
9213  }
9214  if(proposed_lambda>0.5*lambda)
9215  {
9216  proposed_lambda=0.5*lambda;
9217  }
9218  }
9219  }
9220  lambda_aux=lambda;
9221  f_aux = half_residual_squared;
9222  lambda=std::max(proposed_lambda,0.1*lambda);
9223  }
9224 }
9225 
9226 
9227 
9228 //========================================================================
9229 /// Solve a steady problem, in the context of an overall unsteady problem.
9230 /// This is achieved by setting the weights in the timesteppers to be zero
9231 /// which has the effect of rendering them steady timesteppers
9232 /// The optional argument max_adapt specifies the max. number of
9233 /// adaptations of all refineable submeshes are performed to
9234 /// achieve the the error targets specified in the refineable submeshes.
9235 //========================================================================
9236 void Problem::steady_newton_solve(unsigned const &max_adapt)
9237 {
9238  //Find out how many timesteppers there are
9239  unsigned n_time_steppers = ntime_stepper();
9240 
9241  // Vector of bools to store the is_steady status of the various
9242  // timesteppers when we came in here
9243  std::vector<bool> was_steady(n_time_steppers);
9244 
9245  //Loop over them all and make them (temporarily) static
9246  for(unsigned i=0;i<n_time_steppers;i++)
9247  {
9248  was_steady[i]=time_stepper_pt(i)->is_steady();
9250  }
9251 
9252  try
9253  {
9254  //Solve the non-linear problem with Newton's method
9255  if (max_adapt==0)
9256  {
9257  newton_solve();
9258  }
9259  else
9260  {
9261  newton_solve(max_adapt);
9262  }
9263  }
9264  //Catch any exceptions thrown in the Newton solver
9265  catch(NewtonSolverError &error)
9266  {
9267  oomph_info << std::endl << "USER-DEFINED ERROR IN NEWTON SOLVER "
9268  << std::endl;
9269  //Check whether it's the linear solver
9270  if(error.linear_solver_error)
9271  {
9272  oomph_info << "ERROR IN THE LINEAR SOLVER" << std::endl;
9273  }
9274  //Check to see whether we have reached Max_iterations
9275  else if(error.iterations==Max_newton_iterations)
9276  {
9277  oomph_info << "MAXIMUM NUMBER OF ITERATIONS (" << error.iterations <<
9278  ") REACHED WITHOUT CONVERGENCE " << std::endl;
9279  }
9280  //If not, it must be that we have exceeded the maximum residuals
9281  else
9282  {
9283  oomph_info << "MAXIMUM RESIDUALS: " << error.maxres
9284  << " EXCEEDS PREDEFINED MAXIMUM " << Max_residuals
9285  << std::endl;
9286  }
9287 
9288  //Die horribly!!
9289  std::ostringstream error_stream;
9290  error_stream << "Error occured in Newton solver. "
9291  << std::endl;
9292  throw OomphLibError(error_stream.str(),
9293  OOMPH_CURRENT_FUNCTION,
9294  OOMPH_EXCEPTION_LOCATION);
9295  }
9296 
9297 
9298  // Reset the is_steady status of all timesteppers that
9299  // weren't already steady when we came in here and reset their
9300  // weights
9301  for(unsigned i=0;i<n_time_steppers;i++)
9302  {
9303  if (!was_steady[i])
9304  {
9306  }
9307  }
9308 
9309  // Since we performed a steady solve, the history values
9310  // now have to be set as if we had performed an impulsive start from
9311  // the current solution. This ensures that the time-derivatives
9312  // evaluate to zero even now that the timesteppers have been
9313  // reactivated.
9315 
9316 }
9317 
9318 //===========================================================================
9319 ///Perform a basic continuation step using Newton's method. The governing
9320 ///parameter of the problem is passed as a pointer to the routine. The
9321 ///number of Newton steps taken is returned
9322 //==========================================================================
9323 unsigned Problem::
9324 newton_solve_continuation(double* const &parameter_pt)
9325 {
9326  //Set up memory for z
9327  //unsigned long n_dofs = ndof();
9328  //LinearAlgebraDistribution dist(Communicator_pt,n_dofs,false);
9329  //DoubleVector z(&dist,0.0);
9330  DoubleVector z;
9331  //Call the solver
9332  return newton_solve_continuation(parameter_pt,z);
9333 }
9334 
9335 
9336 //===================================================================
9337 /// This function performs a basic continuation step using the Newton method.
9338 /// The number of Newton steps taken is returned, to be used in any
9339 /// external step-size control routines.
9340 /// The governing parameter of the problem is passed as a pointer to the
9341 /// routine, as is the sign of the Jacobian and a Vector in which
9342 /// to store the derivatives wrt the parameter, if required.
9343 //==================================================================
9344 unsigned Problem::
9345 newton_solve_continuation(double* const &parameter_pt,
9346  DoubleVector &z)
9347 {
9348 
9349  //Find the total number of dofs
9350  //unsigned long n_dofs = ndof();
9351 
9352  //Find the local number of dofs
9353  unsigned ndof_local = Dof_distribution_pt->nrow_local();
9354 
9355  // create the distribution (not distributed)
9356  //LinearAlgebraDistribution dist(this->communicator_pt(),n_dofs,false);
9357 
9358  //Assign memory for solutions of the equations
9359  //DoubleVector y(&dist,0.0);
9360  DoubleVector y;
9361 
9362  //Assign memory for the dot products of the uderivatives and y and z
9363  double uderiv_dot_y = 0.0, uderiv_dot_z = 0.0;
9364  //Set and initialise the counter
9365  unsigned count=0;
9366  //Set the loop flag
9367  unsigned LOOP_FLAG=1;
9368 
9369  //Update anything that needs updating
9371 
9372  //Check the arc-length constraint
9373  double arc_length_constraint_residual=0.0;
9374 
9375  //Are we storing the matrix in the linear solve
9376  bool enable_resolve = Linear_solver_pt->is_resolve_enabled();
9377 
9378  //For this problem, we must store the residuals
9380 
9381  //Now do the Newton loop
9382  do
9383  {
9384  count++;
9385 
9386  //Do any updates that are required
9388 
9389  //Calculate initial residuals
9390  if(count==1)
9391  {
9392 #ifdef OOMPH_HAS_MPI
9393  // Synchronise the solution on different processors (on each submesh)
9394  this->synchronise_all_dofs();
9395 #endif
9396 
9398  y.clear();
9399  get_residuals(y);
9400  //Get maximum residuals, using our own abscmp function
9401  double maxres = y.max();
9402 
9403  //Assemble the residuals for the arc-length step
9404  arc_length_constraint_residual = 0.0;
9405  //Add the variables
9406  for(unsigned long l=0;l<ndof_local;l++)
9407  {
9408  arc_length_constraint_residual +=
9409  dof_derivative(l)*(*Dof_pt[l] - dof_current(l));
9410  }
9411 
9412  //Now reduce if we have been distributed
9413 #ifdef OOMPH_HAS_MPI
9414  double arc_length_cons_res2 = arc_length_constraint_residual;
9415  if((Dof_distribution_pt->distributed()) &&
9416  (Dof_distribution_pt->communicator_pt()->nproc() > 1))
9417  {
9418  MPI_Allreduce(&arc_length_constraint_residual,
9419  &arc_length_cons_res2,1,MPI_DOUBLE,MPI_SUM,
9420  Dof_distribution_pt->communicator_pt()->mpi_comm());
9421  }
9422  arc_length_constraint_residual = arc_length_cons_res2;
9423 #endif
9424 
9425  arc_length_constraint_residual *= Theta_squared;
9426  arc_length_constraint_residual +=
9428 
9429  //Is it the max
9430  if(std::fabs(arc_length_constraint_residual) > maxres)
9431  {
9432  maxres = std::fabs(arc_length_constraint_residual);
9433  }
9434 
9435  //Find the max
9437  {
9438  oomph_info << "Initial Maximum residuals " << maxres << std::endl;
9439  }
9440 
9441  //If we are below the Tolerance, then return immediately
9442  if((maxres < Newton_solver_tolerance) &&
9443  (!Always_take_one_newton_step)) {LOOP_FLAG=0; count=0; continue;}
9444  }
9445 
9446  //If it's the block hopf solver we need to solve for both rhs's
9447  //simultaneously. This is because the block decomposition involves
9448  //solves with two different matrices and storing both at once to
9449  //allow general resolves would be more expensive than necessary.
9450  if(dynamic_cast<BlockHopfLinearSolver*>(Linear_solver_pt))
9451  {
9452  //Get the vector dresiduals/dparameter
9453  z.clear();
9454  get_derivative_wrt_global_parameter(parameter_pt,z);
9455 
9456  // Copy rhs vector into local storage so it doesn't get overwritten
9457  // if the linear solver decides to initialise the solution vector, say,
9458  // which it's quite entitled to do!
9459  DoubleVector input_z(z);
9460 
9461  //Solve the system for the two right-hand sides.
9462  dynamic_cast<BlockHopfLinearSolver*>(Linear_solver_pt)->
9463  solve_for_two_rhs(this,y,input_z,z);
9464  }
9465  //Otherwise
9466  else
9467  {
9468  //Solve the standard problem
9469  Linear_solver_pt->solve(this,y);
9470 
9471  //Get the vector dresiduals/dparameter
9472  z.clear();
9473  get_derivative_wrt_global_parameter(parameter_pt,z);
9474 
9475  // Copy rhs vector into local storage so it doesn't get overwritten
9476  // if the linear solver decides to initialise the solution vector, say,
9477  // which it's quite entitled to do!
9478  DoubleVector input_z(z);
9479 
9480  //Redistribute the RHS to match the linear solver
9481  //input_z.redistribute(Linear_solver_pt->distribution_pt());
9482  //Do not clear z because we assume that it has dR/dparam
9483  z.clear();
9484  //Now resolve the system with the new RHS
9485  Linear_solver_pt->resolve(input_z,z);
9486  }
9487 
9488  //Redistribute the results into the natural distribution
9491 
9492  //Now we need to calculate dparam, for which we must calculate the
9493  //dot product of the derivatives and y and z
9494  //Reset these values to zero
9495  uderiv_dot_y = 0.0; uderiv_dot_z=0.0;
9496  //Now calculate the dot products of the derivative and the solutions
9497  //of the linear system
9498  //Cache pointers to the data in the distributed vectors
9499  double* const y_pt = y.values_pt();
9500  double* const z_pt = z.values_pt();
9501  for(unsigned long l=0;l<ndof_local;l++)
9502  {
9503  uderiv_dot_y += dof_derivative(l)*y_pt[l];
9504  uderiv_dot_z += dof_derivative(l)*z_pt[l];
9505  }
9506 
9507  //Now reduce if we have been distributed
9508 #ifdef OOMPH_HAS_MPI
9509  //Create send and receive arrays of size two
9510  double uderiv_dot[2]; double uderiv_dot2[2];
9511  uderiv_dot[0] = uderiv_dot_y; uderiv_dot[1] = uderiv_dot_z;
9512  uderiv_dot2[0] = uderiv_dot_y; uderiv_dot2[1] = uderiv_dot_z;
9513  //Now reduce both together
9514  if((Dof_distribution_pt->distributed()) &&
9515  (Dof_distribution_pt->communicator_pt()->nproc() > 1))
9516  {
9517  MPI_Allreduce(uderiv_dot,
9518  uderiv_dot2,2,MPI_DOUBLE,MPI_SUM,
9519  Dof_distribution_pt->communicator_pt()->mpi_comm());
9520  }
9521  uderiv_dot_y = uderiv_dot2[0];
9522  uderiv_dot_z = uderiv_dot2[1];
9523 #endif
9524 
9525  //Now scale the results
9526  uderiv_dot_y *= Theta_squared;
9527  uderiv_dot_z *= Theta_squared;
9528 
9529  //The set the change in the parameter, given by the pseudo-arclength
9530  //equation. Note that here we are assuming that the arc-length
9531  //equation is always exactly zero,
9532  //which seems to work OK, and saves on some storage.
9533  //In fact, it's more subtle than that. If we include this
9534  //proper residual then we will have to solve the eigenproblem.
9535  //This will make the solver more robust and *should* be done
9536  // ... at some point.
9537  double dparam = (arc_length_constraint_residual - uderiv_dot_y)
9538  /(Parameter_derivative - uderiv_dot_z);
9539 
9540  //Set the new value of the parameter
9541  *parameter_pt -= dparam;
9542 
9543  //Update the values of the other degrees of freedom
9544  for(unsigned long l=0;l<ndof_local;l++)
9545  {
9546  *Dof_pt[l] -= y_pt[l] - dparam*z_pt[l];
9547  }
9548 
9549  //Calculate the new residuals
9550 #ifdef OOMPH_HAS_MPI
9551  // Synchronise the solution on different processors (on each submesh)
9552  this->synchronise_all_dofs();
9553 #endif
9554 
9555  // Do any updates that are required
9558 
9559  y.clear();
9560  get_residuals(y);
9561 
9562  //Get the maximum residuals
9563  double maxres = y.max();
9564 
9565  //Assemble the residuals for the arc-length step
9566  arc_length_constraint_residual = 0.0;
9567  //Add the variables
9568  for(unsigned long l=0;l<ndof_local;l++)
9569  {
9570  arc_length_constraint_residual +=
9571  dof_derivative(l)*(*Dof_pt[l] - dof_current(l));
9572  }
9573 
9574  //Now reduce if we have been distributed
9575 #ifdef OOMPH_HAS_MPI
9576  double arc_length_cons_res2 = arc_length_constraint_residual;
9577  if((Dof_distribution_pt->distributed()) &&
9578  (Dof_distribution_pt->communicator_pt()->nproc() > 1))
9579  {
9580  MPI_Allreduce(&arc_length_constraint_residual,
9581  &arc_length_cons_res2,1,MPI_DOUBLE,MPI_SUM,
9582  Dof_distribution_pt->communicator_pt()->mpi_comm());
9583  }
9584  arc_length_constraint_residual = arc_length_cons_res2;
9585 #endif
9586 
9587  arc_length_constraint_residual *= Theta_squared;
9588  arc_length_constraint_residual +=
9589  Parameter_derivative*(*parameter_pt - Parameter_current)
9590  - Ds_current;
9591 
9592  //Is it the max
9593  if(std::fabs(arc_length_constraint_residual) > maxres)
9594  {
9595  maxres = std::fabs(arc_length_constraint_residual);
9596  }
9597 
9599  {
9600  oomph_info << "Continuation Step " << count << ": Maximum residuals "
9601  << maxres << "\n";
9602  }
9603 
9604  //If we have converged jump straight to the test at the end of the loop
9605  if(maxres < Newton_solver_tolerance) {LOOP_FLAG=0; continue;}
9606 
9607  //This section will not be reached if we have converged already
9608  //If the maximum number of residuals is too high or the maximum number
9609  //of iterations has been reached
9610  if((maxres > Max_residuals) || (count == Max_newton_iterations))
9611  {
9612  throw NewtonSolverError(count,maxres);
9613  }
9614 
9615  }
9616  while(LOOP_FLAG);
9617 
9618  //Now update anything that needs updating
9620 
9621  //Reset the storage of the matrix on the linear solver to what it was
9622  //on entry to this routine
9623  if (enable_resolve)
9624  {
9626  }
9627  else
9628  {
9630  }
9631 
9632  //Return the number of Newton Steps taken
9633  return count;
9634 }
9635 
9636 //=========================================================================
9637 /// A function to calculate the derivatives wrt the arc-length. This version
9638 /// of the function actually does a linear solve so that the derivatives
9639 /// are calculated "exactly" rather than using the values at the Newton
9640 /// step just before convergence. This is only necessary in spatially adaptive
9641 /// problems, in which the number of degrees of freedom changes and so
9642 /// the appropriate derivatives must be calculated for the new variables.
9643 //=========================================================================
9644 void Problem::
9645 calculate_continuation_derivatives(double* const &parameter_pt)
9646 {
9647  //Find the number of degrees of freedom in the problem
9648  const unsigned long n_dofs = ndof();
9649 
9650  // create a non-distributed z vector
9651  LinearAlgebraDistribution dist(Communicator_pt,n_dofs,false);
9652 
9653  //Assign memory for solutions of the equations
9654  DoubleVector z(&dist,0.0);
9655 
9656  //If it's the block hopf solver need to solve for both RHS
9657  //at once, but this would all be alleviated if we have the solve
9658  //for the non-residuals RHS.
9659  if(dynamic_cast<BlockHopfLinearSolver*>(Linear_solver_pt))
9660  {
9661  //Get the vector dresiduals/dparameter
9662  get_derivative_wrt_global_parameter(parameter_pt,z);
9663 
9664  // Copy rhs vector into local storage so it doesn't get overwritten
9665  // if the linear solver decides to initialise the solution vector, say,
9666  // which it's quite entitled to do!
9667  DoubleVector dummy(&dist,0.0), input_z(z);
9668 
9669  //Solve for the two RHSs
9670  dynamic_cast<BlockHopfLinearSolver*>(Linear_solver_pt)->
9671  solve_for_two_rhs(this,dummy,input_z,z);
9672  }
9673  //Otherwise we can use the normal resolve
9674  else
9675  {
9676  //Save the status before entry to this routine
9677  bool enable_resolve = Linear_solver_pt->is_resolve_enabled();
9678 
9679  //We need to do resolves
9681 
9682  //Solve the standard problem, we only want to make sure that
9683  //we factorise the matrix, if it has not been factorised. We shall
9684  //ignore the return value of z.
9685  Linear_solver_pt->solve(this,z);
9686 
9687  //Get the vector dresiduals/dparameter
9688  get_derivative_wrt_global_parameter(parameter_pt,z);
9689 
9690 
9691  // Copy rhs vector into local storage so it doesn't get overwritten
9692  // if the linear solver decides to initialise the solution vector, say,
9693  // which it's quite entitled to do!
9694  DoubleVector input_z(z);
9695 
9696  //Now resolve the system with the new RHS and overwrite the solution
9697  Linear_solver_pt->resolve(input_z,z);
9698 
9699  //Restore the storage status of the linear solver
9700  if (enable_resolve)
9701  {
9703  }
9704  else
9705  {
9707  }
9708  }
9709 
9710  //Now, we can calculate the derivatives, etc
9712 }
9713 
9714 //=======================================================================
9715 /// A function to calculate the derivatives with respect to the arc-length
9716 /// required for continuation. The arguments is the solution of the
9717 /// linear system,
9718 /// Jz = dR/dparameter, that gives du/dparameter and the direction
9719 /// output from the newton_solve_continuation function. The derivatives
9720 /// are stored in the ContinuationParameters namespace.
9721 //===================================================================
9723 {
9724 
9725  //Calculate the continuation derivatives
9727 
9728  //Scale the value of theta if the control flag is set
9729  if(Scale_arc_length)
9730  {
9731  // Don't divide by zero!
9732  if (Parameter_derivative!=1.0)
9733  {
9738 
9739  //Recalculate the continuation derivatives with the new scaled values
9741  }
9742  }
9743 }
9744 
9745 //=======================================================================
9746 /// A function to calculate the derivatives with respect to the arc-length
9747 /// required for continuation using finite differences.
9748 //===================================================================
9750  double* const &parameter_pt)
9751 {
9752 
9753  //Calculate the continuation derivatives
9755 
9756  //Scale the value of theta if the control flag is set
9757  if(Scale_arc_length)
9758  {
9759  // Don't divide by zero!
9760  if (Parameter_derivative!=1.0)
9761  {
9766 
9767  //Recalculate the continuation derivatives with the new scaled values
9769  }
9770  }
9771 }
9772 
9773 //======================================================================
9774 /// Function that returns a boolean flag to indicate whether the pointer
9775 /// parameter_pt refers to memory that is a value in a Data object used
9776 /// within the problem
9777 //======================================================================
9779  double* const &parameter_pt)
9780 {
9781  //Firstly check the global data
9782  const unsigned n_global=Global_data_pt.size();
9783  for (unsigned i=0;i<n_global;++i)
9784  {
9785  //If we find it then return true
9786  if(Global_data_pt[i]->does_pointer_correspond_to_value(parameter_pt))
9787  {return true;}
9788  }
9789 
9790  //If we find the pointer in the mesh data return true
9792  {return true;}
9793 
9794  //Loop over the submeshes to handle the case of spine data
9795  const unsigned n_sub_mesh = this->nsub_mesh();
9796  //If there is only one mesh
9797  if(n_sub_mesh==0)
9798  {
9799  if(SpineMesh* const spine_mesh_pt = dynamic_cast<SpineMesh*>(Mesh_pt))
9800  {
9801  if(spine_mesh_pt->does_pointer_correspond_to_spine_data(parameter_pt))
9802  {return true;}
9803  }
9804  }
9805  //Otherwise loop over the sub meshes
9806  else
9807  {
9808  //Assign global equation numbers first
9809  for(unsigned i=0;i<n_sub_mesh;i++)
9810  {
9811  if(SpineMesh* const spine_mesh_pt =
9812  dynamic_cast<SpineMesh*>(Sub_mesh_pt[i]))
9813  {
9814  if(spine_mesh_pt->does_pointer_correspond_to_spine_data(parameter_pt))
9815  {return true;}
9816  }
9817  }
9818  }
9819 
9820  //If we have got here then the data is not stored in the problem, so return
9821  //false
9822  return false;
9823 }
9824 
9825 
9826 //=======================================================================
9827 /// A private helper function to
9828 /// calculate the derivatives with respect to the arc-length
9829 /// required for continuation. The arguments is the solution of the
9830 /// linear system,
9831 /// Jz = dR/dparameter, that gives du/dparameter and the direction
9832 /// output from the newton_solve_continuation function. The derivatives
9833 /// are stored in the ContinuationParameters namespace.
9834 //===================================================================
9836 {
9837  //Find the number of degrees of freedom in the problem
9838  //unsigned long n_dofs = ndof();
9839  //Find the number of local dofs in the problem
9840  const unsigned long ndof_local = Dof_distribution_pt->nrow_local();
9841 
9842  //Work out the continuation direction
9843  //The idea is that (du/ds)_{old} . (du/ds)_{new} >= 0
9844  //if the direction is to remain the same.
9845  //du/ds_{new} = [dlambda/ds; du/ds] = [dlambda/ds ; - dlambda/ds z]
9846  //so (du/ds)_{new} . (du/ds)_{old}
9847  // = dlambda/ds [1 ; - z] . [ Parameter_derivative ; Dof_derivatives]
9848  // = dlambda/ds (Parameter_derivative - Dof_derivative . z)
9849 
9850  //Create a local copy of z that can be redistributed without breaking
9851  //the constness of z
9852  DoubleVector local_z(z);
9853 
9854  //Redistribute z so that it has the (natural) dof distribution
9856 
9857  //Calculate the local contribution to the Continuation direction
9858  Continuation_direction = 0.0;
9859  //Cache the pointer to z
9860  double* const local_z_pt = local_z.values_pt();
9861  for(unsigned long l=0;l<ndof_local;l++)
9862  {Continuation_direction -= dof_derivative(l)*local_z_pt[l];}
9863 
9864  //Now reduce if we have been distributed
9865 #ifdef OOMPH_HAS_MPI
9866  double cont_dir2 = Continuation_direction;
9867  if((Dof_distribution_pt->distributed()) &&
9868  (Dof_distribution_pt->communicator_pt()->nproc() > 1))
9869  {
9870  MPI_Allreduce(&Continuation_direction,
9871  &cont_dir2,1,MPI_DOUBLE,MPI_SUM,
9872  Dof_distribution_pt->communicator_pt()->mpi_comm());
9873  }
9874  Continuation_direction = cont_dir2;
9875 #endif
9876 
9877  //Add parameter derivative
9878  Continuation_direction += Parameter_derivative;
9879 
9880  //Calculate the magnitude of the du/ds Vector
9881 
9882  //Note that actually, we are usually approximating by using the value at
9883  //newton step just before convergence, which saves one additional
9884  //Newton solve.
9885 
9886  //First calculate the magnitude of du/dparameter, chi
9887  double chi = local_z.dot(local_z);
9888 
9889  //Calculate the current derivative of the parameter wrt the arc-length
9890  Parameter_derivative = 1.0/sqrt(1.0 + Theta_squared*chi);
9891 
9892  //If the dot product of the current derivative wrt the Direction
9893  //is less than zero, switch the sign of the derivative to ensure
9894  //smooth continuation
9895  if(Parameter_derivative*Continuation_direction < 0.0)
9896  {Parameter_derivative*= -1.0;}
9897 
9898  //Resize the derivatives array, if necessary
9900  {
9901  if(Dof_derivative.size() != ndof_local)
9902  {Dof_derivative.resize(ndof_local,0.0);}
9903  }
9904  //Calculate the new derivatives wrt the arc-length
9905  for(unsigned long l=0;l<ndof_local;l++)
9906  {
9907  //This comes from the formulation J u_dot + dr/dlambda lambda_dot = 0
9908  //on the curve and then it follows that.
9909  dof_derivative(l) = -Parameter_derivative*local_z_pt[l];
9910  }
9911 }
9912 
9913 //=======================================================================
9914 /// A private helper function to
9915 /// calculate the derivatives with respect to the arc-length
9916 /// required for continuation using finite differences.
9917 //===================================================================
9919 double* const &parameter_pt)
9920 {
9921  //Find the number of values
9922  //const unsigned long n_dofs = this->ndof();
9923  //Find the number of local dofs in the problem
9924  const unsigned long ndof_local = Dof_distribution_pt->nrow_local();
9925 
9926  //Temporary storage for the finite-difference approximation to the helper
9927  Vector<double> z(ndof_local);
9928  double length=0.0;
9929  //Calculate the change in values and contribution to total length
9930  for(unsigned long l=0;l<ndof_local;l++)
9931  {
9932  z[l] = (*Dof_pt[l] - Dof_current[l])/Ds_current;
9933  length += Theta_squared*z[l]*z[l];
9934  }
9935 
9936  //Reduce if parallel
9937 #ifdef OOMPH_HAS_MPI
9938  double length2 = length;
9939  if((Dof_distribution_pt->distributed()) &&
9940  (Dof_distribution_pt->communicator_pt()->nproc() > 1))
9941  {
9942  MPI_Allreduce(&length,
9943  &length2,1,MPI_DOUBLE,MPI_SUM,
9944  Dof_distribution_pt->communicator_pt()->mpi_comm());
9945  }
9946  length = length2;
9947 #endif
9948 
9949  //Calculate change in parameter
9950  double Z = (*parameter_pt - Parameter_current)/Ds_current;
9951  length += Z*Z;
9952 
9953  //Scale the approximations to the derivatives
9954  length = sqrt(length);
9955  for(unsigned long l=0;l<ndof_local;l++)
9956  {
9957  dof_derivative(l) = z[l]/length;
9958  }
9959  Parameter_derivative = Z/length;
9960 }
9961 
9962 
9963 /// \short Virtual function that is used to symmetrise the problem so that
9964 /// the current solution exactly satisfies any symmetries within the system.
9965 /// Used when adpativly solving pitchfork detection problems when small
9966 /// asymmetries in the coarse solution can be magnified
9967 /// leading to very inaccurate answers on the fine mesh.
9968 /// This is always problem-specific and must be filled in by the user
9969 /// The default issues a warning
9971 {
9972  std::ostringstream warn_message;
9973  warn_message
9974  << "Warning: This function is called after spatially adapting the\n"
9975  << "eigenfunction associated with a pitchfork bifurcation and should\n"
9976  << "ensure that the exact (anti-)symmetries of problem are enforced\n"
9977  << "within that eigenfunction. It is problem specific and must be\n"
9978  << "filled in by the user if required.\n"
9979  << "A sign of problems is if the slack paramter gets too large and\n"
9980  << "if the solution at the Pitchfork is not symmetric.\n";
9982  warn_message.str(),
9983  "Problem::symmetrise_eigenfunction_for_adaptive_pitchfork_tracking()",
9984  OOMPH_EXCEPTION_LOCATION);
9985 }
9986 
9987 //====================================================================
9988 ///Return pointer to the parameter that is used in the
9989 /// bifurcation detection. If we are not tracking a bifurcation then
9990 /// an error will be thrown by the AssemblyHandler
9991 //====================================================================
9994 
9995 //====================================================================
9996 /// Return the eigenfunction calculated as part of a
9997 /// bifurcation tracking process. If we are not tracking a bifurcation
9998 /// then an error will be thrown by the AssemblyHandler
9999 //======================================================================
10001  Vector<DoubleVector> &eigenfunction)
10002 {
10003  //Simply call the appropriate assembly handler function
10004  Assembly_handler_pt->get_eigenfunction(eigenfunction);
10005 }
10006 
10007 //============================================================
10008 /// Activate the fold tracking system by changing the assembly
10009 /// handler and initialising it using the parameter addressed
10010 /// by parameter_pt.
10011 //============================================================
10012 void Problem::activate_fold_tracking(double* const &parameter_pt,
10013  const bool &block_solve)
10014 {
10015  //Reset the assembly handler to default
10017  //Set the new assembly handler. Note that the constructor actually
10018  //solves the original problem to get some initial conditions, but
10019  //this is OK because the RHS is always evaluated before assignment.
10020  Assembly_handler_pt = new FoldHandler(this,parameter_pt);
10021 
10022  //If we are using a block solver, we must set the linear solver pointer
10023  //to the block fold solver. The present linear solver is
10024  //used by the block solver and so must be passed as an argument.
10025  //The destructor of the Fold handler returns the linear
10026  //solver to the original non-block version.
10027  if(block_solve)
10028  {
10030  }
10031 }
10032 
10033  //===============================================================
10034 /// Activate the generic bifurcation ///tracking system by changing the assembly
10035 /// handler and initialising it using the parameter addressed
10036 /// by parameter_pt.
10037 //============================================================
10039  double* const &parameter_pt,
10040  const DoubleVector &eigenvector,
10041  const bool &block_solve)
10042 {
10043  //Reset the assembly handler to default
10045  //Set the new assembly handler. Note that the constructor actually
10046  //solves the original problem to get some initial conditions, but
10047  //this is OK because the RHS is always evaluated before assignment.
10048  Assembly_handler_pt = new FoldHandler(this,parameter_pt,
10049  eigenvector);
10050 
10051  //If we are using a block solver, we must set the linear solver pointer
10052  //to the block fold solver. The present linear solver is
10053  //used by the block solver and so must be passed as an argument.
10054  //The destructor of the Fold handler returns the linear
10055  //solver to the original non-block version.
10056  if(block_solve)
10057  {
10059  }
10060 }
10061 
10062 
10063  //===============================================================
10064 /// Activate the generic bifurcation ///tracking system by changing the assembly
10065 /// handler and initialising it using the parameter addressed
10066 /// by parameter_pt.
10067 //============================================================
10069  double* const &parameter_pt,
10070  const DoubleVector &eigenvector,
10071  const DoubleVector &normalisation,
10072  const bool &block_solve)
10073 {
10074  //Reset the assembly handler to default
10076  //Set the new assembly handler. Note that the constructor actually
10077  //solves the original problem to get some initial conditions, but
10078  //this is OK because the RHS is always evaluated before assignment.
10079  Assembly_handler_pt = new FoldHandler(this,parameter_pt,
10080  eigenvector,normalisation);
10081 
10082  //If we are using a block solver, we must set the linear solver pointer
10083  //to the block fold solver. The present linear solver is
10084  //used by the block solver and so must be passed as an argument.
10085  //The destructor of the Fold handler returns the linear
10086  //solver to the original non-block version.
10087  if(block_solve)
10088  {
10090  }
10091 }
10092 
10093 
10094 
10095 
10096 
10097 //==================================================================
10098 /// Activate the pitchfork tracking system by changing the assembly
10099 /// handler and initialising it using the parameter addressed
10100 /// by parameter_pt and a symmetry vector. The boolean flag is
10101 /// used to specify whether a block solver is used, default is true.
10102 //===================================================================
10104  double* const &parameter_pt,
10105  const DoubleVector &symmetry_vector,const bool &block_solve)
10106 {
10107  //Reset the assembly handler to default
10109 
10110  //Set the new assembly handler. Note that the constructor actually
10111  //solves the original problem to get some initial conditions, but
10112  //this is OK because the RHS is always evaluated before assignment.
10114  parameter_pt,
10115  symmetry_vector);
10116 
10117  //If we are using a block solver, we must set the linear solver pointer
10118  //to the block pitchfork solver. The present linear solver is
10119  //used by the block solver and so must be passed as an argument.
10120  //The destructor of the PitchFork handler returns the linear
10121  //solver to the original non-block version.
10122  if(block_solve)
10123  {
10126  }
10127 }
10128 
10129 
10130 
10131 //============================================================
10132 /// Activate the hopf tracking system by changing the assembly
10133 /// handler and initialising it using the parameter addressed
10134 /// by parameter_pt.
10135 //============================================================
10137  double* const &parameter_pt, const bool &block_solve)
10138 {
10139  //Reset the assembly handler to default
10141  //Set the new assembly handler. Note that the constructor actually
10142  //solves the original problem to get some initial conditions, but
10143  //this is OK because the RHS is always evaluated before assignment.
10144  Assembly_handler_pt = new HopfHandler(this,parameter_pt);
10145 
10146  //If we are using a block solver, we must set the linear solver pointer
10147  //to the block hopf solver. The present linear solver is
10148  //used by the block solver and so must be passed as an argument.
10149  //The destructor of the Hopf handler returns the linear
10150  //solver to the original non-block version.
10151  if(block_solve)
10152  {
10154  }
10155 }
10156 
10157 
10158 //============================================================
10159 /// Activate the hopf tracking system by changing the assembly
10160 /// handler and initialising it using the parameter addressed
10161 /// by parameter_pt and the frequency and null vectors
10162 /// specified.
10163 //============================================================
10165  double* const &parameter_pt, const double &omega,
10166  const DoubleVector &null_real, const DoubleVector &null_imag,
10167  const bool &block_solve)
10168 {
10169  //Reset the assembly handler to default
10171  //Set the new assembly handler. Note that the constructor actually
10172  //solves the original problem to get some initial conditions, but
10173  //this is OK because the RHS is always evaluated before assignment.
10174  Assembly_handler_pt = new HopfHandler(this,parameter_pt,omega,
10175  null_real,null_imag);
10176 
10177  //If we are using a block solver, we must set the linear solver pointer
10178  //to the block hopf solver. The present linear solver is
10179  //used by the block solver and so must be passed as an argument.
10180  //The destructor of the Hopf handler returns the linear
10181  //solver to the original non-block version.
10182  if(block_solve)
10183  {
10185  }
10186 }
10187 
10188 
10189 //===============================================================
10190 ///Reset the assembly handler to default
10191 //===============================================================
10193 {
10194  //If we have a non-default handler
10196  {
10197  //Delete the current assembly handler
10198  delete Assembly_handler_pt;
10199  //Reset the assembly handler
10201  }
10202 }
10203 
10204 //===================================================================
10205 /// This function takes one step of length ds in pseudo-arclength.The
10206 /// argument parameter_pt is a pointer to the parameter (global variable)
10207 /// that is being traded for arc-length. The function returns the next desired
10208 /// arc-length according to criteria based upon the desired number of Newton
10209 /// Iterations per solve.
10210 //=====================================================================
10211 double Problem::arc_length_step_solve(double* const &parameter_pt,
10212  const double &ds,
10213  const unsigned &max_adapt)
10214 {
10215  //First check that we shouldn't use the other interface
10216  //by checking that the parameter isn't already stored as data
10217  if(does_pointer_correspond_to_problem_data(parameter_pt))
10218  {
10219  std::ostringstream error_message;
10220  error_message << "The parameter addressed by " << parameter_pt
10221  << " with the value " << *parameter_pt
10222  << "\n is supposed to be used for arc-length contiunation,\n"
10223  << " but it is stored in a Data object used by the problem.\n\n"
10224  << "This is bad for two reasons:\n"
10225  <<
10226  "1. If it's a variable in the problem, it must already have an\n"
10227  "associated equation, so it can't be used for continuation;\n"
10228  <<
10229  "2. The problem data will be reorganised in memory during continuation,\n"
10230  <<
10231  " which means that the pointer will become invalid.\n\n"
10232  <<
10233  "If you are sure that this is what you want to do you must:\n"
10234  <<
10235  "A. Ensure that the value is pinned (don't worry we'll shout again if not)\n"
10236  <<
10237  "B. Use the alternative interface\n"
10238  <<
10239  " Problem::arc_length_step_solve(Data*,unsigned,...)\n"
10240  <<
10241  " which uses a pointer to the data object and not the raw double pointer."
10242  << std::endl;
10243  throw OomphLibError(error_message.str(),
10244  OOMPH_CURRENT_FUNCTION,
10245  OOMPH_EXCEPTION_LOCATION);
10246  }
10247 
10248 
10249  //If we are using the continuation timestepper
10251  {
10252  //Has the timestepper already been added to the problem
10253  bool continuation_time_stepper_added = false;
10254  const unsigned n_time_steppers = this->ntime_stepper();
10255  for(unsigned i=0;i<n_time_steppers;i++)
10256  {
10258  {
10259  continuation_time_stepper_added = true;
10260  break;
10261  }
10262  }
10263 
10264  //If not add it
10265  if(!continuation_time_stepper_added)
10266  {
10267  oomph_info << "Adding the continuation time stepper\n";
10269  }
10270 
10271  //Need to treat case of eigenproblems and bifurcation detection/tracking
10272  //here
10273 
10274  //Backup the current timesteppers for each mesh!
10275 
10276 
10277  //If an arc length step has not been taken then set the timestepper
10279  {
10280  //Set the continuation timestepper for all data in the problem
10281  std::cout <<
10283  <<
10284  " equation numbers allocated for continuation\n";
10285  }
10286 
10287  } //End of continuation time stepper case
10288 
10289 
10290  //Just call the helper function (parameter is not from data)
10291  return arc_length_step_solve_helper(parameter_pt,ds,max_adapt);
10292 }
10293 
10294 
10295 //===================================================================
10296 /// This function takes one step of length ds in pseudo-arclength.The
10297 /// argument data_pt is a pointer to the data that holds the
10298 /// parameter (global variable)
10299 /// that is being traded for arc-length. The exact value is located at
10300 /// the location given by data_index.
10301 /// The function returns the next desired
10302 /// arc-length according to criteria based upon the desired number of Newton
10303 /// Iterations per solve.
10304 //=====================================================================
10305  double Problem::arc_length_step_solve(Data* const &data_pt,
10306  const unsigned &data_index,
10307  const double &ds,
10308  const unsigned &max_adapt)
10309  {
10310  //Firstly check that the data is pinned
10311  if(!data_pt->is_pinned(data_index))
10312  {
10313  std::ostringstream error_stream;
10314  error_stream << "The value at index " << data_index
10315  << " in the data object to be used for continuation\n"
10316  << "is not pinned, which means that it is already a\n"
10317  << "variable in the problem "
10318  << "and cannot be used for continuation.\n\n"
10319  << "Please correct your formulation by either:\n"
10320  << "A. Pinning the value" << "\n or \n"
10321  << "B. Using a different parameter for continuation"
10322  << std::endl;
10323  throw OomphLibError(error_stream.str(),
10324  OOMPH_CURRENT_FUNCTION,
10325  OOMPH_EXCEPTION_LOCATION);
10326  }
10327 
10328 
10329  //If we are using the continuation timestepper
10331  {
10332  //Has the timestepper already been added to the problem
10333  bool continuation_time_stepper_added = false;
10334  const unsigned n_time_steppers = this->ntime_stepper();
10335  for(unsigned i=0;i<n_time_steppers;i++)
10336  {
10338  {
10339  continuation_time_stepper_added = true;
10340  break;
10341  }
10342  }
10343 
10344  //If not add it
10345  if(!continuation_time_stepper_added)
10346  {
10347  oomph_info << "Adding the continuation time stepper\n";
10349  }
10350 
10351  //Need to treat case of eigenproblems and bifurcation detection/tracking
10352  //here
10353 
10354 
10355  //Backup the current timesteppers for each mesh!
10356 
10357 
10358  //If an arc length step has not been taken then set the timestepper
10360  {
10361  //Set the continuation timestepper for all data in the problem
10362  std::cout <<
10364  <<
10365  " equation numbers allocated for continuation\n";
10366  }
10367 
10368 
10369  } //End of continuation time stepper case
10370 
10371 
10372  //Now make a pointer to the (newly allocated) data object
10373  double* parameter_pt = data_pt->value_pt(data_index);
10374  //Call the helper function, this will change the parameter_pt if
10375  //the data storage is changed (if the timestepper has to be changed,
10376  //which happens if this is the first time that a continuation step is
10377  //taken)
10378  //ALH: Don't think this is true because it has happened above....
10379  return arc_length_step_solve_helper(parameter_pt,ds,max_adapt);
10380  }
10381 
10382 //======================================================================
10383 /// \short Private helper function that is used to set the appropriate
10384 /// pinned values for continuation. If the data is pinned, the its
10385 /// current value is always the same as the original value and
10386 /// the derivative is always zero. If these are not set properly
10387 /// then interpolation and projection in spatial adaptivity will
10388 /// not give the best answers.
10389 //=====================================================================
10391 {
10392  //Set the consistent values for the global mesh
10395 
10396  // Deal with the spine meshes additional numbering separately
10397  const unsigned n_sub_mesh = this->nsub_mesh();
10398  //If there is only one mesh
10399  if(n_sub_mesh==0)
10400  {
10401  if(SpineMesh* const spine_mesh_pt = dynamic_cast<SpineMesh*>(Mesh_pt))
10402  {
10403  spine_mesh_pt->set_consistent_pinned_spine_values_for_continuation(
10405  }
10406  //If it's a triangle mesh the we need to set the
10407 
10408  }
10409  //Otherwise loop over the sub meshes
10410  else
10411  {
10412  //Assign global equation numbers first
10413  for(unsigned i=0;i<n_sub_mesh;i++)
10414  {
10415  if(SpineMesh* const spine_mesh_pt =
10416  dynamic_cast<SpineMesh*>(Sub_mesh_pt[i]))
10417  {
10418  spine_mesh_pt->set_consistent_pinned_spine_values_for_continuation(
10420  }
10421  }
10422  }
10423 
10424  //Also set time stepper for global data
10425  const unsigned n_global=Global_data_pt.size();
10426  for (unsigned i=0;i<n_global;++i)
10427  {
10429  }
10430 }
10431 
10432 
10433 //===================================================================
10434 /// This function takes one step of length ds in pseudo-arclength.The
10435 /// argument parameter_pt is a pointer to the parameter (global variable)
10436 /// that is being traded for arc-length. The function returns the next desired
10437 /// arc-length according to criteria based upon the desired number of Newton
10438 /// Iterations per solve.
10439 //=====================================================================
10440 double Problem::arc_length_step_solve_helper(double* const &parameter_pt,
10441  const double &ds,
10442  const unsigned &max_adapt)
10443 {
10444 
10445  //----------------------MAKE THE PROBLEM STEADY-----------------------
10446  //Loop over the timesteppers and make them (temporarily) steady.
10447  //We can only do continuation for steady problems!
10448  unsigned n_time_steppers = ntime_stepper();
10449  // Vector of bools to store the is_steady status of the various
10450  // timesteppers when we came in here
10451  std::vector<bool> was_steady(n_time_steppers);
10452 
10453  //Loop over them all and make them (temporarily) static
10454  for(unsigned i=0;i<n_time_steppers;i++)
10455  {
10456  was_steady[i]=time_stepper_pt(i)->is_steady();
10458  }
10459 
10460 
10461  // Max number of solves
10462  unsigned max_solve = max_adapt+1;
10463  //Storage for newton steps in each adaptation
10464  unsigned max_count_in_adapt_loop=0;
10465 
10466 
10467  //----SET UP MEMORY FOR QUANTITIES THAT ARE REQUIRED OUTSIDE THE LOOP----
10468 
10469  //Assign memory for solutions of the equations Jz = du/dparameter
10470  //This is needed here (outside the loop), so that we can save on
10471  //one linear solve when calculating the derivatives wrt the arc-length
10472  DoubleVector z;
10473 
10474 
10475  //Store sign of the Jacobian, used for bifurcation detection
10476  //If this is the first time that we are calling the arc-length solver,
10477  //this should not be used.
10478  int previous_sign = Sign_of_jacobian;
10479 
10480 //Flag to indicate a sign change
10481  bool SIGN_CHANGE=false;
10482 
10483 
10484  //Adaptation loop
10485  for(unsigned isolve=0;isolve<max_solve;++isolve)
10486  {
10487  //Only adapt after the first solve has been done
10488  if(isolve > 0)
10489  {
10490  unsigned n_refined;
10491  unsigned n_unrefined;
10492 
10493  // Adapt problem
10494  adapt(n_refined,n_unrefined);
10495 
10496 #ifdef OOMPH_HAS_MPI
10497  // Adaptation only converges if ALL the processes have no
10498  // refinement or unrefinement to perform
10499  unsigned total_refined=0;
10500  unsigned total_unrefined=0;
10502  {
10503  MPI_Allreduce(&n_refined,&total_refined,1,MPI_UNSIGNED,MPI_SUM,
10504  this->communicator_pt()->mpi_comm());
10505  n_refined=total_refined;
10506  MPI_Allreduce(&n_unrefined,&total_unrefined,1,MPI_UNSIGNED,MPI_SUM,
10507  this->communicator_pt()->mpi_comm());
10508  n_unrefined=total_unrefined;
10509  }
10510 #endif
10511 
10512  oomph_info << "---> " << n_refined << " elements were refined, and "
10513  << n_unrefined
10514  << " were unrefined"
10515 #ifdef OOMPH_HAS_MPI
10516  << ", in total (over all processors).\n";
10517 #else
10518  << ".\n";
10519 #endif
10520 
10521 
10522  // Check convergence of adaptation cycle
10523  if ((n_refined==0)&&(n_unrefined==0))
10524  {
10525  oomph_info << "\n \n Solution is fully converged in "
10526  << "Problem::newton_solver(). \n \n ";
10527  break;
10528  }
10529  }
10530 
10531  //----------SAVE THE INITIAL VALUES, IN CASE THE STEP FAILS-----------
10532 
10533  //Find the number of local dofs
10534  unsigned ndof_local = Dof_distribution_pt->nrow_local();
10535 
10536  //Only need to do this in the first loop
10537  if(isolve==0)
10538  {
10540  {
10541  //Safety check, set up the array of dof derivatives, if necessary
10542  //The distribution is the same as the (natural) distribution of the dofs
10543  if(Dof_derivative.size() != ndof_local)
10544  {Dof_derivative.resize(ndof_local,0.0);}
10545 
10546  //Safety check, set up the array of curren values, if necessary
10547  //Again the distribution reflects the (natural) distribution of the dofs
10548  if(Dof_current.size() != ndof_local) {Dof_current.resize(ndof_local);}
10549  }
10550 
10551  //Save the current value of the parameter
10552  Parameter_current = *parameter_pt;
10553 
10554  //Save the current values of the degrees of freedom
10555  for(unsigned long l=0;l<ndof_local;l++) {dof_current(l) = *Dof_pt[l];}
10556 
10557  //Set the value of ds_current
10558  Ds_current = ds;
10559  }
10560 
10561  //Counter for the number of newton steps
10562  unsigned count=0;
10563 
10564  //Flag to indicate a successful step
10565  bool STEP_REJECTED=false;
10566 
10567 
10568  //Set the appropriate initial conditions for the pinned data
10570  {
10572  }
10573 
10574  //Loop around the step in arc-length
10575  do
10576  {
10577  //Check that the step has not fallen below the minimum tolerance
10578  if(std::fabs(Ds_current) < Minimum_ds)
10579  {
10580  std::ostringstream error_message;
10581  error_message << "DESIRED ARC-LENGTH STEP " << Ds_current
10582  << " HAS FALLEN BELOW MINIMUM TOLERANCE, "
10583  << Minimum_ds << std::endl;
10584 
10585  throw OomphLibError(error_message.str(),
10586  OOMPH_CURRENT_FUNCTION,
10587  OOMPH_EXCEPTION_LOCATION);
10588  }
10589 
10590  //Assume that we shall accept the step
10591  STEP_REJECTED=false;
10592 
10593  //Set initial value of the parameter
10595 
10596  // Perform any actions...
10597  actions_after_parameter_increase(parameter_pt);
10598 
10599  Ds_current = (*parameter_pt - Parameter_current)/Parameter_derivative;
10600 
10601  //Loop over the (local) variables and set their initial values
10602  for(unsigned long l=0;l<ndof_local;l++)
10603  {
10605  }
10606 
10607  //Actually do the newton solve stage for the continuation problem
10608  try
10609  {
10610  count = newton_solve_continuation(parameter_pt,z);
10611  }
10612  //Catch any exceptions thrown in the Newton solver
10613  catch(NewtonSolverError &error)
10614  {
10615  //Check whether it's the linear solver
10616  if(error.linear_solver_error)
10617  {
10618  std::ostringstream error_stream;
10619  error_stream << std::endl
10620  << "USER-DEFINED ERROR IN NEWTON SOLVER " << std::endl;
10621  oomph_info << "ERROR IN THE LINEAR SOLVER" << std::endl;
10622  throw OomphLibError(error_stream.str(),
10623  OOMPH_CURRENT_FUNCTION,
10624  OOMPH_EXCEPTION_LOCATION);
10625  }
10626  //Otherwise mark the step as having failed
10627  else
10628  {
10629  oomph_info << "STEP REJECTED --- TRYING AGAIN" << std::endl;
10630  STEP_REJECTED=true;
10631  //Let's take a smaller step
10632  Ds_current *= (2.0/3.0);
10633  }
10634  }
10635  }
10636  while(STEP_REJECTED); //continue until a step is accepted
10637 
10638  //Set the maximum count
10639  if(count > max_count_in_adapt_loop) {max_count_in_adapt_loop = count;}
10640  } /// end of adaptation loop
10641 
10642  //Only recalculate the derivatives if there has been a Newton solve
10643  //If not, the previous values should be close enough
10644  if(max_count_in_adapt_loop>0)
10645  {
10646 
10647  //--------------------CHECK FOR POTENTIAL BIFURCATIONS-------------
10649  {
10650  //If the sign of the jacobian is zero issue a warning
10651  if(Sign_of_jacobian == 0)
10652  {
10653  std::string error_message =
10654  "The sign of the jacobian is zero after a linear solve\n";
10655  error_message +=
10656  "Either the matrix is singular (unlikely),\n";
10657  error_message +=
10658  "or the linear solver cannot compute the determinant of the matrix;\n";
10659  error_message += "e.g. an iterative linear solver.\n";
10660  error_message +=
10661  "If the latter, bifurcation detection must be via an eigensolver\n";
10662  OomphLibWarning(error_message,
10663  "Problem::arc_length_step_solve",
10664  OOMPH_EXCEPTION_LOCATION);
10665  }
10666  //If this is the first step, we cannot rely on the previous value
10667  //of the jacobian so set the previous sign to the present sign
10668  if(!Arc_length_step_taken) {previous_sign = Sign_of_jacobian;}
10669  //If we have detected a sign change in the last converged Jacobian,
10670  //it must be a turning point or bifurcation
10671  if(Sign_of_jacobian != previous_sign)
10672  {
10673 
10674  //There has been, at least, one sign change
10676 
10677  //The sign has changed this time
10678  SIGN_CHANGE=true;
10679 
10680  //Calculate the dot product of the approximate null vector
10681  //of the Jacobian matrix ((badly) approximated by z)
10682  //and the vectors of derivatives of the residuals wrt the
10683  //global parameter
10684  //If this is small it is a bifurcation rather than a turning point.
10685  //Get the derivative wrt global parameter
10686  //DoubleVector dparam;
10687  //get_derivative_wrt_global_parameter(parameter_pt,dparam);
10688  //Calculate the dot product
10689  //double dot=0.0;
10690  //for(unsigned long n=0;n<n_dofs;++n) {dot += dparam[n]*z[n];}
10691  //z.dot(dparam);
10692 
10693  //Write the output message
10694  std::ostringstream message;
10695  message << "-----------------------------------------------------------";
10696  message << std::endl << "SIGN CHANGE IN DETERMINANT OF JACOBIAN: "
10697  << std::endl;
10698  message << "BIFURCATION OR TURNING POINT DETECTED BETWEEN "
10699  << Parameter_current << " AND " << *parameter_pt << std::endl;
10700  //message << "APPROXIMATE DOT PRODUCT : " << dot << "," << std::endl;
10701  //message << "IF CLOSE TO ZERO WE HAVE A BIFURCATION; ";
10702  //message << "OTHERWISE A TURNING POINT" << std::endl;
10703  message << "-----------------------------------------------------------"
10704  << std::endl;
10705 
10706  //Write the message to standard output
10707  oomph_info << message.str();
10708 
10709  //Open the information file for appending
10710  std::ofstream bifurcation_info("bifurcation_info",std::ios_base::app);
10711  //Write the message to the file
10712  bifurcation_info << message.str();
10713  bifurcation_info.close();
10714  }
10715  }
10716 
10717  //Calculate the derivatives required for the next stage of continuation
10718  //In this we pass the last value of z (i.e. approximation)
10720  {
10722  }
10723  //Or use finite differences
10724  else
10725  {
10727  }
10728 
10729  //If it's the first step then the value of the next step should
10730  //be the change in parameter divided by the parameter derivative
10731  //to obtain approximately the same parameter change
10733  {
10735  }
10736 
10737  //We have taken our first step
10738  Arc_length_step_taken = true;
10739  }
10740  //If there has not been a newton step then we still need to estimate
10741  //the derivatives in the arc length direction
10742  else
10743  {
10744  //Default is to calculate the continuation derivatives by solving the linear
10745  //system. We must do this to ensure that the derivatives are in sync
10746  //It could lead to problems near turning points when we should really be
10747  //solving an eigenproblem, but seems OK so far!
10748 
10749  //Save the current sign of the jacobian
10750  int temp_sign=Sign_of_jacobian;
10751 
10752  //Calculate the continuation derivatives, which includes a solve
10753  //of the linear system if not using finite differences
10755  {
10756  calculate_continuation_derivatives(parameter_pt);
10757  }
10758  //Otherwise use finite differences
10759  else
10760  {
10762  }
10763 
10764  //Reset the sign of the jacobian, just in case the sign has changed when
10765  //solving the continuation derivatives. The sign change will be picked
10766  //up on the next continuation step.
10767  Sign_of_jacobian = temp_sign;
10768  }
10769 
10770  // Reset the is_steady status of all timesteppers that
10771  // weren't already steady when we came in here and reset their
10772  // weights
10773  for(unsigned i=0;i<n_time_steppers;i++)
10774  {
10775  if (!was_steady[i])
10776  {
10778  }
10779  }
10780 
10781  //If we are trying to find a bifurcation and the first sign change
10782  //has occured, use bisection
10783  if((Bifurcation_detection) &&
10785  {
10786  //If there has been a sign change we need to half the step size
10787  //and reverse the direction
10788  if(SIGN_CHANGE) {Ds_current *= -0.5;}
10789  //Otherwise
10790  else
10791  {
10792  //The size of the bracketed interval is always
10793  //2ds - Ds_current (this will work even if the original step failed)
10794  //We want our new step size to be half this
10795  Ds_current = ds - 0.5*Ds_current;
10796  }
10797  //Return the desired value of the step
10798  return Ds_current;
10799  }
10800 
10801  //If fewer than the desired number of Newton Iterations, increase the step
10802  if(max_count_in_adapt_loop < Desired_newton_iterations_ds)
10803  {return Ds_current*1.5;}
10804  //If more than the desired number of Newton Iterations, reduce the step
10805  if(max_count_in_adapt_loop > Desired_newton_iterations_ds)
10806  {return Ds_current*(2.0/3.0);}
10807  //Otherwise return the step just taken
10808  return Ds_current;
10809 }
10810 
10811 
10812 //=======================================================================
10813 /// Take an explicit timestep of size dt
10814 //======================================================================
10815 void Problem::explicit_timestep(const double &dt, const bool &shift_values)
10816 {
10817 #ifdef PARANOID
10818  if(this->explicit_time_stepper_pt() == 0)
10819  {
10820  throw OomphLibError("Explicit time stepper pointer is null in problem.",
10821  OOMPH_EXCEPTION_LOCATION,
10822  OOMPH_CURRENT_FUNCTION);
10823  }
10824 #endif
10825 
10826  //Firstly we shift the time values
10827  if(shift_values) {shift_time_values();}
10828  //Set the current value of dt, if we can
10829  if(time_pt()->ndt() > 0) {time_pt()->dt() = dt;}
10830 
10831  //Take the explicit step
10832  this->explicit_time_stepper_pt()->timestep(this,dt);
10833 }
10834 
10835 
10836 //========================================================================
10837 /// Do one timestep of size dt using Newton's method with the specified
10838 /// tolerance and linear solver defined as member data of the Problem class.
10839 /// This will be the most commonly used version
10840 /// of unsteady_newton_solve, in which the time values are always shifted
10841 /// This does not include any kind of adaptativity. If the solution fails to
10842 /// converge the program will end.
10843 //========================================================================
10844 void Problem::unsteady_newton_solve(const double &dt)
10845 {
10846  //We shift the values, so shift_values is true
10847  unsteady_newton_solve(dt,true);
10848 }
10849 
10850 //========================================================================
10851 /// Do one timestep forward of size dt using Newton's method with the
10852 /// specified tolerance and linear solver defined via member data of the
10853 /// Problem class.
10854 /// The boolean flag shift_values is used to control whether the time values
10855 /// should be shifted or not.
10856 //========================================================================
10857 void Problem::unsteady_newton_solve(const double &dt, const bool &shift_values)
10858 {
10859  //Shift the time values and the dts, according to the control flag
10860  if(shift_values) {shift_time_values();}
10861 
10862  // Advance global time and set current value of dt
10863  time_pt()->time()+=dt;
10864  time_pt()->dt()=dt;
10865 
10866  //Find out how many timesteppers there are
10867  unsigned n_time_steppers = ntime_stepper();
10868 
10869  //Loop over them all and set the weights
10870  for(unsigned i=0;i<n_time_steppers;i++)
10871  {
10873  }
10874 
10875  // Run the individual timesteppers actions before timestep. These need to
10876  // be before the problem's actions_before_implicit_timestep so that the
10877  // boundary conditions are set consistently.
10878  for(unsigned i=0;i<n_time_steppers;i++)
10880 
10881  //Now update anything that needs updating before the timestep
10882  //This could be time-dependent boundary conditions, for example.
10884 
10885  try
10886  {
10887  //Solve the non-linear problem for this timestep with Newton's method
10888  newton_solve();
10889  }
10890  //Catch any exceptions thrown in the Newton solver
10891  catch(NewtonSolverError &error)
10892  {
10893  oomph_info << std::endl << "USER-DEFINED ERROR IN NEWTON SOLVER "
10894  << std::endl;
10895  //Check whether it's the linear solver
10896  if(error.linear_solver_error)
10897  {
10898  oomph_info << "ERROR IN THE LINEAR SOLVER" << std::endl;
10899  }
10900  //Check to see whether we have reached Max_iterations
10901  else if(error.iterations==Max_newton_iterations)
10902  {
10903  oomph_info << "MAXIMUM NUMBER OF ITERATIONS (" << error.iterations
10904  << ") REACHED WITHOUT CONVERGENCE " << std::endl;
10905  }
10906  //If not, it must be that we have exceeded the maximum residuals
10907  else
10908  {
10909  oomph_info << "MAXIMUM RESIDUALS: " << error.maxres
10910  << " EXCEEDS PREDEFINED MAXIMUM " << Max_residuals
10911  << std::endl;
10912  }
10913  //Die horribly!!
10914  std::ostringstream error_stream;
10915  error_stream << "Error occured in unsteady Newton solver. "
10916  << std::endl;
10917  throw OomphLibError(error_stream.str(),
10918  OOMPH_CURRENT_FUNCTION,
10919  OOMPH_EXCEPTION_LOCATION);
10920  }
10921 
10922  // Run the individual timesteppers actions, these need to be before the
10923  // problem's actions_after_implicit_timestep so that the time step is
10924  // finished before the problem does any auxiliary calculations (e.g. in
10925  // semi-implicit micromagnetics the calculation of magnetostatic field).
10926  for(unsigned i=0;i<n_time_steppers;i++)
10928 
10929 
10930  //Now update anything that needs updating after the timestep
10933 }
10934 
10935 //=======================================================================
10936 /// Attempt to take one timestep forward using dt_desired. The error control
10937 /// parameter, epsilon, is used to specify the desired approximate value of the
10938 /// global error norm per timestep. The routine returns the value an estimate
10939 /// of the next value of dt that should be taken.
10940 //=======================================================================
10941 double Problem::
10942 adaptive_unsteady_newton_solve(const double &dt_desired,
10943  const double &epsilon)
10944 {
10945  //We always want to shift the time values
10946  return adaptive_unsteady_newton_solve(dt_desired,epsilon,true);
10947 }
10948 
10949 
10950 //=======================================================================
10951 /// Attempt to take one timestep forward using the dt_desired.
10952 /// This is the driver for a number of adaptive solvers. If the solution
10953 /// fails to converge at a given timestep, the routine will automatically
10954 /// halve the time step and try again, until the time step falls below the
10955 /// specified minimum value. The routine returns the value an estimate
10956 /// of the next value of dt that should be taken.
10957 /// Timestep is also rejected if the error estimate post-solve
10958 /// (computed by global_temporal_error_norm()) exceeds epsilon.
10959 /// This behaviour can be over-ruled by setting the protected
10960 /// boolean Problem::Keep_temporal_error_below_tolerance to false.
10961 //========================================================================
10962 double Problem::
10963 adaptive_unsteady_newton_solve(const double &dt_desired,
10964  const double &epsilon,
10965  const bool &shift_values)
10966 {
10967  //First, we need to backup the existing dofs, in case the timestep is
10968  //rejected
10969 
10970  //Find total number of dofs on current processor
10971  unsigned n_dof_local = dof_distribution_pt()->nrow_local();
10972 
10973  //Now set up a Vector to hold current values
10974  Vector<double> dofs_current(n_dof_local);
10975 
10976  //Load values into dofs_current
10977  for(unsigned i=0;i<n_dof_local;i++) dofs_current[i] = dof(i);
10978 
10979  //Store the time
10980  double time_current = time_pt()->time();
10981 
10982  //Flag to detect whether the timestep has been rejected or not
10983  bool reject_timestep=0;
10984 
10985  //Flag to detect whether any of the timesteppers are adaptive
10986  unsigned adaptive_flag=0;
10987 
10988 //The value of the actual timestep, by default the same as desired timestep
10989  double dt_actual=dt_desired;
10990 
10991  //Find out whether any of the timesteppers are adaptive
10992  unsigned n_time_steppers = ntime_stepper();
10993  for(unsigned i=0;i<n_time_steppers;i++)
10994  {
10995  if(time_stepper_pt(i)->adaptive_flag())
10996  {
10997  adaptive_flag=1;
10998  break;
10999  }
11000  }
11001 
11002  //Shift the time_values according to the control flag
11003  if(shift_values) {shift_time_values();}
11004 
11005  //This loop surrounds the adaptive time-stepping and will not be broken
11006  //until a timestep is accepted
11007  do
11008  {
11009  // Initially we assume that this step will succeed and that this dt
11010  // value is ok.
11011  reject_timestep=0;
11012  double dt_rescaling_factor = 1.0;
11013 
11014  //Set the new time and value of dt
11015  time_pt()->time() += dt_actual;
11016  time_pt()->dt() = dt_actual;
11017 
11018  //Loop over all timesteppers and set the weights and predictor weights
11019  for(unsigned i=0;i<n_time_steppers;i++)
11020  {
11021  //If the time_stepper is non-adaptive, this will be zero
11024  }
11025 
11026  //Now calculate the predicted values for the all data and all positions
11028 
11029  // Run the individual timesteppers actions before timestep. These need to
11030  // be before the problem's actions_before_implicit_timestep so that the
11031  // boundary conditions are set consistently.
11032  for(unsigned i=0;i<n_time_steppers;i++)
11034 
11035  //Do any updates/boundary conditions changes here
11037 
11038  //Attempt to solve the non-linear system
11039  try
11040  {
11041  //Solve the non-linear problem at this timestep
11042  newton_solve();
11043  }
11044  //Catch any exceptions thrown
11045  catch(NewtonSolverError &error)
11046  {
11047  //If it's a solver error then die
11049  {
11050  std::string error_message =
11051  "USER-DEFINED ERROR IN NEWTON SOLVER\n";
11052  error_message += "ERROR IN THE LINEAR SOLVER\n";
11053 
11054  //Die
11055  throw OomphLibError(error_message,
11056  OOMPH_CURRENT_FUNCTION,
11057  OOMPH_EXCEPTION_LOCATION);
11058  }
11059  else
11060  {
11061  //Reject the timestep, if we have an exception
11062  oomph_info << "TIMESTEP REJECTED" << std::endl;
11063  reject_timestep=1;
11064 
11065  // Half the time step
11066  dt_rescaling_factor = Timestep_reduction_factor_after_nonconvergence;
11067  }
11068  }
11069 
11070  // Run the individual timesteppers actions, these need to be before the
11071  // problem's actions_after_implicit_timestep so that the time step is
11072  // finished before the problem does any auxiliary calculations (e.g. in
11073  // semi-implicit micromagnetics the calculation of magnetostatic field).
11074  for(unsigned i=0;i<n_time_steppers;i++)
11076 
11077  //Update anything that needs updating after the timestep
11079 
11080  // If we have an adapative timestepper (and we haven't already failed)
11081  // then calculate the error estimate and rescaling factor.
11082  if(adaptive_flag && !reject_timestep)
11083  {
11084  //Once timestep has been accepted can do fancy error processing
11085  //Set the error weights
11086  for(unsigned i=0;i<n_time_steppers;i++)
11087  {
11089  }
11090 
11091  // Get a global error norm to use in adaptivity (as specified by the
11092  // problem sub-class writer). Prevent a divide by zero if the solution
11093  // gives very close to zero error. Error norm should never be negative
11094  // but use absolute value just in case.
11095  double error = std::max(std::abs(global_temporal_error_norm()),
11096  1e-12);
11097 
11098  //Calculate the scaling factor
11099  dt_rescaling_factor =
11100  std::pow((epsilon/error), (1.0/(1.0+time_stepper_pt()->order())));
11101 
11102  oomph_info << "Timestep scaling factor is "
11103  << dt_rescaling_factor << std::endl;
11104  oomph_info << "Estimated timestepping error is " << error << std::endl;
11105 
11106 
11107  // Do we have to do it again?
11108  if (error>epsilon)
11109  {
11110  oomph_info
11111  << "Estimated timestepping error "
11112  << error << " exceeds tolerance "
11113  << epsilon << " ";
11115  {
11116  oomph_info << " --> rejecting timestep."
11117  << std::endl;
11118  reject_timestep=1;
11119  }
11120  else
11121  {
11122  oomph_info << " ...but we're not rejecting the timestep"
11123  << std::endl;
11124  }
11125  oomph_info
11126  << "Note: This behaviour can be adjusted by changing the protected "
11127  << "boolean" << std::endl << std::endl
11128  << " Problem::Keep_temporal_error_below_tolerance"
11129  << std::endl;
11130  }
11131 
11132 
11133  } //End of if adaptive flag
11134 
11135 
11136 
11137  // Calculate the next time step size and check it's ok
11138  // ============================================================
11139 
11140  // Calculate the possible next time step, if no error conditions
11141  // trigger.
11142  double new_dt_candidate = dt_rescaling_factor * dt_actual;
11143 
11144  // Check that the scaling factor is within the allowed range
11145  if(dt_rescaling_factor > DTSF_max_increase)
11146  {
11147  oomph_info << "Tried to increase dt by the ratio " << dt_rescaling_factor
11148  << " which is above the maximum (" << DTSF_max_increase
11149  << "). Attempting to increase by the maximum ratio instead."
11150  << std::endl;
11151  new_dt_candidate = DTSF_max_increase * dt_actual;
11152  }
11153  // If we have already rejected the timestep then don't do this check
11154  // because DTSF will definitely be too small.
11155  else if((!reject_timestep) && (dt_rescaling_factor <= DTSF_min_decrease))
11156  {
11157  // Handle this special case where we want to continue anyway (usually
11158  // Minimum_dt_but_still_proceed = -1 so this has no effect).
11159  if(new_dt_candidate < Minimum_dt_but_still_proceed)
11160  {
11161  oomph_info
11162  << "Warning: Adaptation of timestep to ensure satisfaction\n"
11163  << " of error bounds during adaptive timestepping\n"
11164  << " would lower dt below \n"
11165  << " Problem::Minimum_dt_but_still_proceed="
11166  << Minimum_dt_but_still_proceed << "\n"
11167  << " ---> We're continuing with present timestep.\n"
11168  << std::endl;
11169  dt_rescaling_factor=1.0;
11170  // ??ds shouldn't we set new_dt_candidate =
11171  // Minimum_dt_but_still_proceed here, rather than not changing dt at
11172  // all?
11173  }
11174  else
11175  {
11176  // Otherwise reject
11177  oomph_info << "Timestep would decrease by " << dt_rescaling_factor
11178  << " which is less than the minimum scaling factor "
11179  << DTSF_min_decrease << std::endl;
11180  oomph_info << "TIMESTEP REJECTED" << std::endl;
11181  reject_timestep=1;
11182  }
11183 
11184  }
11185 
11186  // Now check that the new dt is within the allowed range
11187  if(new_dt_candidate > Maximum_dt)
11188  {
11189  oomph_info << "Tried to increase dt to " << new_dt_candidate
11190  << " which is above the maximum (" << Maximum_dt
11191  << "). I increased it to the maximum value instead.";
11192  dt_actual = Maximum_dt;
11193  }
11194  else if(new_dt_candidate < Minimum_dt)
11195  {
11196  std::ostringstream err;
11197  err << "Tried to reduce dt to " << new_dt_candidate
11198  << " which is less than the minimum dt (" << Minimum_dt
11199  << ")." << std::endl;
11200  throw OomphLibError(err.str(), OOMPH_EXCEPTION_LOCATION,
11201  OOMPH_CURRENT_FUNCTION);
11202  }
11203  else
11204  {
11205  dt_actual = new_dt_candidate;
11206  }
11207 
11208 
11210 
11211 
11212  // If we are rejecting this attempt then revert the dofs etc.
11213  if(reject_timestep)
11214  {
11215  //Reset the time
11216  time_pt()->time() = time_current;
11217 
11218  //Reload the dofs
11219  unsigned ni = dofs_current.size();
11220  for(unsigned i=0; i<ni; i++) {dof(i) = dofs_current[i];}
11221 
11222 #ifdef OOMPH_HAS_MPI
11223  // Synchronise the solution on different processors (on each submesh)
11224  this->synchronise_all_dofs();
11225 #endif
11226 
11227  //Call all "after" actions, e.g. to handle mesh updates
11233  }
11234 
11235  }
11236  //Keep this loop going until we accept the timestep
11237  while(reject_timestep);
11238 
11239  // Once the timestep has been accepted, return the time step that should be
11240  // used next time.
11241  return dt_actual;
11242 }
11243 
11244 
11245 
11246 //=======================================================================
11247 /// Private helper function to perform
11248 /// unsteady "doubly" adaptive Newton solve: Does temporal
11249 /// adaptation first, i.e. we try to do a timestep with an increment
11250 /// of dt, and adjusting dt until the solution on the given mesh satisfies
11251 /// the temporal error measure with tolerance epsilon. Following
11252 /// this, we do up to max_adapt spatial adaptions (without
11253 /// re-examining the temporal error). If first==true, the initial conditions
11254 /// are re-assigned after the mesh adaptations.
11255 /// Shifting of time can be suppressed by overwriting the
11256 /// default value of shift (true). [Shifting must be done
11257 /// if first_timestep==true because we're constantly re-assigning
11258 /// the initial conditions; if first_timestep==true and shift==false
11259 /// shifting is performed anyway and a warning is issued.
11260  /// Pseudo-Boolean flag suppress_resolve_after_spatial_adapt [0: false;
11261  /// 1: true] does what it says.]
11262 //========================================================================
11264 (const double &dt_desired,
11265  const double &epsilon,
11266  const unsigned &max_adapt,
11267  const unsigned& suppress_resolve_after_spatial_adapt_flag,
11268  const bool &first,
11269  const bool &shift_values)
11270 {
11271  //Store the initial time
11272  double initial_time = time_pt()->time();
11273 
11274  // Take adaptive timestep, adjusting dt until tolerance is satisfied
11275  double new_dt=adaptive_unsteady_newton_solve(dt_desired,
11276  epsilon,
11277  shift_values);
11278  double dt_taken=time_pt()->dt();
11279  oomph_info << "Accepted solution taken with timestep: "
11280  << dt_taken << std::endl;
11281 
11282 
11283  // Bail out straightaway if no spatial adaptation allowed
11284  if (max_adapt==0)
11285  {
11286  oomph_info << "No spatial refinement allowed; max_adapt=0\n";
11287  return new_dt;
11288  }
11289 
11290  // Adapt problem/mesh
11291  unsigned n_refined=0;
11292  unsigned n_unrefined=0;
11293  adapt(n_refined,n_unrefined);
11294 
11295  // Check if mesh has been adapted on other processors
11296  Vector<int> total_ref_count(2);
11297  total_ref_count[0]=n_refined;
11298  total_ref_count[1]=n_unrefined;
11299 
11300 
11301 #ifdef OOMPH_HAS_MPI
11303  {
11304  // Sum n_refine across all processors
11305  Vector<int> ref_count(2);
11306  ref_count[0]=n_refined;
11307  ref_count[1]=n_unrefined;
11308  MPI_Allreduce(&ref_count[0],&total_ref_count[0],2,MPI_INT,MPI_SUM,
11309  communicator_pt()->mpi_comm());
11310  }
11311 #endif
11312 
11313 
11314  // Re-solve the problem if the adaptation has changed anything
11315  if ((total_ref_count[0]!=0)||
11316  (total_ref_count[1]!=0))
11317  {
11318  if (suppress_resolve_after_spatial_adapt_flag==1)
11319  {
11320  oomph_info << "Mesh was adapted but re-solve has been suppressed."
11321  << std::endl;
11322  }
11323  else
11324  {
11325  oomph_info << "Mesh was adapted --> we'll re-solve for current timestep."
11326  << std::endl;
11327 
11328  // Reset time to what it was when we entered here
11329  // because it will be incremented again by dt_taken.
11330  time_pt()->time()=initial_time;
11331 
11332  // Shift the timesteps? No! They've been shifted already when we
11333  // called the solve with pure temporal adaptivity...
11334  bool shift=false;
11335 
11336  // Reset the inital condition on refined meshes
11337  if (first)
11338  {
11339  // Reset default set_initial_condition has been called flag to false
11341 
11342  //Reset the initial conditions
11343  oomph_info << "Re-assigning initial condition at time="
11344  << time_pt()->time()<< std::endl;
11346 
11347  // This is the first timestep so shifting
11348  // has to be done following the assignment of initial conditions,
11349  // providing the default set_initial_condition function has not
11350  // been called.
11351  // In fact, unsteady_newton_solve(...) does that automatically.
11352  // We're changing the flag here to avoid warning messages.
11353  if(!Default_set_initial_condition_called) { shift=true; }
11354  }
11355 
11356  // Now take the step again on the refined mesh, using the same
11357  // timestep as used before.
11358  unsteady_newton_solve(dt_taken,max_adapt,first,shift);
11359  }
11360  }
11361  else
11362  {
11363  oomph_info << "Mesh wasn't adapted --> we'll accept spatial refinement."
11364  << std::endl;
11365  }
11366 
11367  return new_dt;
11368 
11369 }
11370 
11371 
11372 
11373 
11374 //========================================================================
11375 /// \short Initialise the previous values of the variables for time stepping
11376 /// corresponding to an impulsive start. Previous history for all data
11377 /// is generated by the appropriate timesteppers. Previous nodal
11378 /// positions are simply copied backwards.
11379 //========================================================================
11381 {
11382  //Assign the impulsive values in the "master" mesh
11384 
11385  // Loop over global data
11386  unsigned Nglobal=Global_data_pt.size();
11387  for (unsigned iglobal=0;iglobal<Nglobal;iglobal++)
11388  {
11389  Global_data_pt[iglobal]->time_stepper_pt()->
11391  }
11392 }
11393 
11394 
11395 //=======================================================================
11396 ///Assign the values for an impulsive start and also set the initial
11397 ///values of the previous dts to both be dt
11398 //======================================================================
11400 {
11401  //First initialise the dts and set the weights
11402  initialise_dt(dt);
11403  //Now call assign_initial_values_impulsive
11405 }
11406 
11407 //=======================================================================
11408 /// Return the current value of continuous time. If not Time object
11409 /// has been assigned, then throw an error
11410 //======================================================================
11411 double& Problem::time()
11412 {
11413  if(Time_pt==0)
11414  {
11415  throw OomphLibError("Time object has not been set",
11416  OOMPH_CURRENT_FUNCTION,
11417  OOMPH_EXCEPTION_LOCATION);
11418  }
11419  else {return Time_pt->time();}
11420 }
11421 
11422 //=======================================================================
11423 /// Return the current value of continuous time. If not Time object
11424 /// has been assigned, then throw an error. Const version.
11425 //======================================================================
11426 double Problem::time() const
11427 {
11428  if(Time_pt==0)
11429  {
11430  throw OomphLibError("Time object has not been set",
11431  OOMPH_CURRENT_FUNCTION,
11432  OOMPH_EXCEPTION_LOCATION);
11433  }
11434  else {return Time_pt->time();}
11435 }
11436 
11437 
11438 //=======================================================================
11439 /// Set all problem data to have the same timestepper (timestepper_pt).
11440 /// This is mainly used in continuation and bifurcation detection problems
11441 /// in which case the total number of unknowns may change and the changes
11442 /// to the underlying memory layout means that the Dof_pt must be
11443 /// reallocated. Thus, the function calls assign_eqn_numbers() and returns
11444 /// the number of new equation numbers.
11445 //=========================================================================
11447  TimeStepper* const &time_stepper_pt, const bool &preserve_existing_data)
11448 {
11449  //Set the timestepper for the master mesh's nodal and elemental data
11450  //to be the
11451  //continuation time stepper. This will wipe all storage other than
11452  //the 0th (present time) value at all the data objects
11454  preserve_existing_data);
11455 
11456  // Deal with the any additional mesh level timestepper data separately
11457  const unsigned n_sub_mesh = this->nsub_mesh();
11458  //If there is only one mesh
11459  if(n_sub_mesh==0)
11460  {
11461  Mesh_pt->set_mesh_level_time_stepper(time_stepper_pt,preserve_existing_data);
11462  }
11463  //Otherwise loop over the sub meshes
11464  else
11465  {
11466  //Assign global equation numbers first
11467  for(unsigned i=0;i<n_sub_mesh;i++)
11468  {
11469  this->Sub_mesh_pt[i]->set_mesh_level_time_stepper(time_stepper_pt,
11470  preserve_existing_data);
11471  }
11472  }
11473 
11474  //Also set time stepper for global data
11475  const unsigned n_global=Global_data_pt.size();
11476  for (unsigned i=0;i<n_global;++i)
11477  {
11478  Global_data_pt[i]->set_time_stepper(time_stepper_pt,preserve_existing_data);
11479  }
11480 
11481  //We now need to reassign equations numbers because the Dof pointer
11482  //will be inappropriate because memory has been reallocated
11483 
11484 #ifdef OOMPH_HAS_MPI
11486  {
11487  std::ostringstream warning_stream;
11488  warning_stream <<
11489  "This has not been comprehensively tested for distributed problems.\n"
11490  <<
11491  "I'm sure that I need to worry about external halo and external elements."
11492  << std::endl;
11493  OomphLibWarning(warning_stream.str(),
11494  OOMPH_CURRENT_FUNCTION,
11495  OOMPH_EXCEPTION_LOCATION);
11496  }
11497 
11498 #endif
11499 
11500  return (this->assign_eqn_numbers());
11501 }
11502 
11503 
11504 //========================================================================
11505 /// Shift all time-dependent data along for next timestep.
11506 //========================================================================
11508 {
11509  //Move the values of dt in the Time object
11510  Time_pt->shift_dt();
11511 
11512  //Only shift time values in the "master" mesh, otherwise things will
11513  //get shifted twice in complex problems
11515 
11516  // Shift global data with their own timesteppers
11517  unsigned Nglobal=Global_data_pt.size();
11518  for (unsigned iglobal=0;iglobal<Nglobal;iglobal++)
11519  {
11520  Global_data_pt[iglobal]->time_stepper_pt()->
11522  }
11523 
11524 }
11525 
11526 
11527 //========================================================================
11528 /// Calculate the predictions of all variables in problem
11529 //========================================================================
11531 {
11532 
11533 // Check that if we have multiple time steppers none of them want to
11534 // predict by calling an explicit timestepper (as opposed to doing
11535 // something like an explicit step by combining known history values, as
11536 // done in BDF).
11537 #ifdef PARANOID
11538  if(Time_stepper_pt.size()!=1)
11539  {
11540  for(unsigned j=0; j<Time_stepper_pt.size(); j++)
11541  {
11543  {
11544  std::string err =
11545  "Prediction by explicit step only works for problems with a simple time";
11546  err += "stepper. I think implementing anything more general will";
11547  err += "require a rewrite of explicit time steppers. - David";
11548  throw OomphLibError(err, OOMPH_EXCEPTION_LOCATION,
11549  OOMPH_CURRENT_FUNCTION);
11550  }
11551  }
11552  }
11553 #endif
11554 
11555 
11556  // Predict using an explicit timestepper (don't do it if adaptive = false
11557  // because pointers probably aren't set up).
11558  if(time_stepper_pt()->predict_by_explicit_step() &&
11559  time_stepper_pt()->adaptive_flag())
11560  {
11561  // Copy the time stepper's predictor pt into problem's explicit time
11562  // stepper pt (unless problem already has its own explicit time
11563  // stepper).
11565 #ifdef PARANOID
11566  if(ets_pt == 0)
11567  {
11568  std::string err = "Requested predictions by explicit step but explicit";
11569  err += " predictor pt is null.";
11570  throw OomphLibError(err, OOMPH_CURRENT_FUNCTION,
11571  OOMPH_EXCEPTION_LOCATION);
11572  }
11573 
11574  if((explicit_time_stepper_pt() != ets_pt)
11575  && (explicit_time_stepper_pt() != 0))
11576  {
11577  throw OomphLibError("Problem has explicit time stepper other than predictor, not sure how to handle this yet ??ds",
11578  OOMPH_EXCEPTION_LOCATION,
11579  OOMPH_CURRENT_FUNCTION);
11580  }
11581 #endif
11582  explicit_time_stepper_pt() = ets_pt;
11583 
11584  // Backup dofs and time
11586 
11587 #ifdef PARANOID
11588  double backup_time = time();
11589 #endif
11590 
11591  // Move time back so that we are at the start of the timestep (as
11592  // explicit_timestep functions expect). This is needed because the
11593  // predictor calculations are done after unsteady newton solve has
11594  // started, and it has already moved time forwards.
11595  double dt = time_pt()->dt();
11596  time() -= dt;
11597 
11598  // Explicit step
11599  this->explicit_timestep(dt, false);
11600 
11601  // Copy predicted dofs and time to their storage slots.
11602  set_dofs(time_stepper_pt()->predictor_storage_index(), Dof_pt);
11604 
11605  // Check we got the times right
11606 #ifdef PARANOID
11607  if(std::abs(time() - backup_time) > 1e-12)
11608  {
11609  using namespace StringConversion;
11610  std::string err = "Predictor landed at the wrong time!";
11611  err += " Expected time " + to_string(backup_time, 14) + " but got ";
11612  err += to_string(time(), 14);
11613  throw OomphLibError(err, OOMPH_EXCEPTION_LOCATION,
11614  OOMPH_CURRENT_FUNCTION);
11615  }
11616 #endif
11617 
11618  // Restore dofs and time
11620  }
11621 
11622  // Otherwise we can do predictions in a more object oriented way using
11623  // whatever timestepper the data provides (this is the normal case).
11624  else
11625  {
11626  //Calculate all predictions in the "master" mesh
11628 
11629  //Calculate predictions for global data with their own timesteppers
11630  unsigned Nglobal=Global_data_pt.size();
11631  for (unsigned iglobal=0;iglobal<Nglobal;iglobal++)
11632  {
11633  Global_data_pt[iglobal]->time_stepper_pt()->
11634  calculate_predicted_values(Global_data_pt[iglobal]);
11635  }
11636  }
11637 
11638  // If requested then copy the predicted value into the current time data
11639  // slots, ready for the newton solver to use as an initial guess.
11641  {
11642 
11643  // Not sure I know enough about distributed problems to implement
11644  // this. Probably you just need to loop over ndof_local or something,
11645  // but I can't really test it...
11646 #ifdef OOMPH_HAS_MPI
11647  if(distributed())
11648  {
11649  throw OomphLibError("Not yet implemented for distributed problems",
11650  OOMPH_EXCEPTION_LOCATION, OOMPH_CURRENT_FUNCTION);
11651  }
11652 #endif
11653 
11654  // With multiple time steppers this is much more complex becuase you
11655  // need to check the time stepper for each data to get the
11656  // predictor_storage_index(). Do-able if you need it though.
11657  if(Time_stepper_pt.size() != 1)
11658  {
11659  std::string err = "Not implemented for multiple time steppers";
11660  throw OomphLibError(err, OOMPH_EXCEPTION_LOCATION,
11661  OOMPH_CURRENT_FUNCTION);
11662  }
11663 
11664  // Get predicted values
11665  DoubleVector predicted_dofs;
11666  get_dofs(time_stepper_pt()->predictor_storage_index(), predicted_dofs);
11667 
11668  // Update dofs at current step
11669  for(unsigned i=0; i<ndof(); i++)
11670  {
11671  dof(i) = predicted_dofs[i];
11672  }
11673 
11674  }
11675 }
11676 
11677 //======================================================================
11678 ///\short Enable recycling of the mass matrix in explicit timestepping
11679 ///schemes. Useful for timestepping on fixed meshes when you want
11680 ///to avoid the linear solve phase.
11681 //=====================================================================
11683 {
11686 
11687  //If we have a discontinuous formulation set the elements to reuse
11688  //their own mass matrices
11690  {
11691  const unsigned n_element = Problem::mesh_pt()->nelement();
11692  //Loop over the other elements
11693  for(unsigned e=0;e<n_element;e++)
11694  {
11695  //Cache the element
11696  DGElement* const elem_pt =
11697  dynamic_cast<DGElement*>(Problem::mesh_pt()->element_pt(e));
11698  elem_pt->enable_mass_matrix_reuse();
11699  }
11700  }
11701 }
11702 
11703 //======================================================================
11704 /// \short Turn off the recyling of the mass matrix in explicit
11705 /// time-stepping schemes
11706 //======================================================================
11708 {
11711 
11712  //If we have a discontinuous formulation set the element-level
11713  //function
11715  {
11716  const unsigned n_element = Problem::mesh_pt()->nelement();
11717  //Loop over the other elements
11718  for(unsigned e=0;e<n_element;e++)
11719  {
11720  //Cache the element
11721  DGElement* const elem_pt =
11722  dynamic_cast<DGElement*>(Problem::mesh_pt()->element_pt(e));
11723  elem_pt->disable_mass_matrix_reuse();
11724  }
11725  }
11726 }
11727 
11728 
11729 //=========================================================================
11730 /// Copy Data values, nodal positions etc from specified problem.
11731 /// Note: This is not a copy constructor. We assume that the current
11732 /// and the "original" problem have both been created by calling
11733 /// the same problem constructor so that all Data objects,
11734 /// time steppers etc. in the two problems are completely independent.
11735 /// This function copies the nodal, internal and global values
11736 /// and the time parameters from the original problem into "this"
11737 /// one. This functionality is required, e.g. for
11738 /// multigrid computations.
11739 //=========================================================================
11740 void Problem::copy(Problem* orig_problem_pt)
11741 {
11742 
11743  // Copy time
11744  //----------
11745 
11746  // Flag to indicate that orig problem is unsteady problem
11747  bool unsteady_flag=(orig_problem_pt->time_pt()!=0);
11748 
11749  // Copy current time and previous time increments for proper unsteady run
11750  if (unsteady_flag)
11751  {
11752  oomph_info << "Copying an unsteady problem." << std::endl;
11753  // Current time
11754  this->time_pt()->time()=orig_problem_pt->time_pt()->time();
11755  // Timesteps
11756  unsigned n_dt=orig_problem_pt->time_pt()->ndt();
11757  time_pt()->resize(n_dt);
11758  for (unsigned i=0;i<n_dt;i++)
11759  {
11760  time_pt()->dt(i)=orig_problem_pt->time_pt()->dt(i);
11761  }
11762 
11763  //Find out how many timesteppers there are
11764  unsigned n_time_steppers = ntime_stepper();
11765 
11766  //Loop over them all and set the weights
11767  for(unsigned i=0;i<n_time_steppers;i++)
11768  {
11770  }
11771 
11772 
11773  }
11774 
11775  // Copy nodes
11776  //-----------
11777 
11778  // Loop over submeshes:
11779  unsigned nmesh=nsub_mesh();
11780  if (nmesh==0) nmesh=1;
11781  for (unsigned m=0;m<nmesh;m++)
11782  {
11783  // Find number of nodes in present mesh
11784  unsigned long n_node = mesh_pt(m)->nnode();
11785 
11786  // Check # of nodes:
11787  unsigned long n_node_orig=orig_problem_pt->mesh_pt(m)->nnode();
11788  if (n_node!=n_node_orig)
11789  {
11790  std::ostringstream error_message;
11791  error_message << "Number of nodes in copy " << n_node
11792  << " not equal to the number in the original "
11793  << n_node_orig << std::endl;
11794 
11795  throw OomphLibError(error_message.str(),
11796  OOMPH_CURRENT_FUNCTION,
11797  OOMPH_EXCEPTION_LOCATION);
11798  }
11799 
11800  //Loop over the nodes
11801  for(unsigned long i=0;i<n_node;i++)
11802  {
11803  // Try to cast to elastic node
11804  SolidNode* el_node_pt=dynamic_cast<SolidNode*>(mesh_pt(m)->node_pt(i));
11805  if (el_node_pt!=0)
11806  {
11807  SolidNode* el_node_orig_pt=
11808  dynamic_cast<SolidNode*>(orig_problem_pt->mesh_pt(m)->node_pt(i));
11809  el_node_pt->copy(el_node_orig_pt);
11810  }
11811  else
11812  {
11813  mesh_pt(m)->node_pt(i)->copy(orig_problem_pt->mesh_pt(m)->node_pt(i));
11814  }
11815  }
11816  }
11817 
11818 
11819  // Copy global data:
11820  //------------------
11821 
11822  // Number of global data
11823  unsigned n_global=Global_data_pt.size();
11824 
11825  // Check # of nodes in orig problem
11826  unsigned long n_global_orig=orig_problem_pt->nglobal_data();
11827  if (n_global!=n_global_orig)
11828  {
11829  std::ostringstream error_message;
11830  error_message << "Number of global data in copy " << n_global
11831  << " not equal to the number in the original "
11832  << n_global_orig << std::endl;
11833 
11834  throw OomphLibError(error_message.str(),
11835  OOMPH_CURRENT_FUNCTION,
11836  OOMPH_EXCEPTION_LOCATION);
11837  }
11838 
11839  for (unsigned iglobal=0;iglobal<n_global;iglobal++)
11840  {
11841  Global_data_pt[iglobal]->copy(orig_problem_pt->global_data_pt(iglobal));
11842  }
11843 
11844 
11845  // Copy internal data of elements:
11846  //--------------------------------
11847 
11848  // Loop over submeshes:
11849  for (unsigned m=0;m<nmesh;m++)
11850  {
11851  // Loop over elements and deal with internal data
11852  unsigned n_element=mesh_pt(m)->nelement();
11853  for (unsigned e=0;e<n_element;e++)
11854  {
11855  GeneralisedElement* el_pt=mesh_pt(m)->element_pt(e);
11856  unsigned n_internal=el_pt->ninternal_data();
11857  if (n_internal>0)
11858  {
11859  // Check # of internals :
11860  unsigned long n_internal_orig=orig_problem_pt->
11862  if (n_internal!=n_internal_orig)
11863  {
11864  std::ostringstream error_message;
11865  error_message << "Number of internal data in copy " << n_internal
11866  << " not equal to the number in the original "
11867  << n_internal_orig << std::endl;
11868 
11869  throw OomphLibError(error_message.str(),
11870  OOMPH_CURRENT_FUNCTION,
11871  OOMPH_EXCEPTION_LOCATION);
11872  }
11873  for (unsigned i=0;i<n_internal;i++)
11874  {
11875  el_pt->internal_data_pt(i)->copy(orig_problem_pt->
11876  mesh_pt(m)->element_pt(e)->
11877  internal_data_pt(i));
11878  }
11879  }
11880  }
11881  }
11882 
11883 }
11884 
11885 //=========================================================================
11886 /// Make and return a pointer to the copy of the problem. A virtual
11887 /// function that must be filled in by the user is they wish to perform
11888 /// adaptive refinement in bifurcation tracking or in multigrid problems.
11889 /// ALH: WILL NOT BE NECESSARY IN BIFURCATION TRACKING IN LONG RUN...
11890 //=========================================================================
11892 {
11893  std::ostringstream error_stream;
11894  error_stream
11895  << "This function must be overloaded in your specific problem, and must\n"
11896  << "create an exact copy of your problem. Usually this will be achieved\n"
11897  << "by a call to the constructor with exactly the same arguments as used\n";
11898 
11899  throw OomphLibError(error_stream.str(),
11900  OOMPH_CURRENT_FUNCTION,
11901  OOMPH_EXCEPTION_LOCATION);
11902 }
11903 
11904 
11905 
11906 //=========================================================================
11907 /// Dump refinement pattern of all refineable meshes and all generic
11908 /// Problem data to file for restart.
11909 //=========================================================================
11910 void Problem::dump(std::ofstream& dump_file) const
11911 {
11912 
11913  // Number of submeshes?
11914  unsigned n_mesh=nsub_mesh();
11915 
11916  dump_file << std::max(unsigned(1),n_mesh)
11917  << " # number of (sub)meshes " << std::endl;
11918 
11919  // Single mesh:
11920  //------------
11921  if(n_mesh==0)
11922  {
11923  // Dump level of refinement before pruning
11924  if(TreeBasedRefineableMeshBase* mmesh_pt =
11925  dynamic_cast<TreeBasedRefineableMeshBase*>(mesh_pt(0)))
11926  {
11927  dump_file << mmesh_pt->uniform_refinement_level_when_pruned()
11928  << " # uniform refinement when pruned " << std::endl;
11929  }
11930  else
11931  {
11932  dump_file << 0
11933  << " # (fake) uniform refinement when pruned " << std::endl;
11934  }
11935  dump_file << 9999 << " # test flag for end of sub-meshes " << std::endl;
11936  }
11937 
11938  //Multiple submeshes
11939  //------------------
11940  else
11941  {
11942  // Loop over submeshes to dump level of refinement before pruning
11943  for (unsigned imesh=0;imesh<n_mesh;imesh++)
11944  {
11945  if(TreeBasedRefineableMeshBase* mmesh_pt =
11946  dynamic_cast<TreeBasedRefineableMeshBase*>(mesh_pt(imesh)))
11947  {
11948  dump_file << mmesh_pt->uniform_refinement_level_when_pruned()
11949  << " # uniform refinement when pruned " << std::endl;
11950  }
11951  else
11952  {
11953  dump_file << 0
11954  << " # (fake) uniform refinement when pruned " << std::endl;
11955  }
11956  }
11957  dump_file << 9999 << " # test flag for end of sub-meshes " << std::endl;
11958  }
11959 
11960 #ifdef OOMPH_HAS_MPI
11961 
11962  const int my_rank=this->communicator_pt()->my_rank();
11963 
11964  // Record destination of all base elements
11965  unsigned n=Base_mesh_element_pt.size();
11966  Vector<int> local_base_element_processor(n,-1);
11967  Vector<int> base_element_processor(n,-1);
11968  for (unsigned e=0;e<n;e++)
11969  {
11971  if (el_pt!=0)
11972  {
11973  if (!el_pt->is_halo())
11974  {
11975  local_base_element_processor[e]=my_rank;
11976  }
11977  }
11978  }
11979 
11980 
11981 
11982  // Get target for all base elements by reduction
11984  {
11985  // Check that the base elements have been associated to a processor
11986  // (the Base_mesh_elemen_pt is only used for structured meshes,
11987  // therefore, if there are no ustructured meshes as part of the
11988  // problem this container will be empty)
11989  if (n > 0)
11990  {
11991  MPI_Allreduce(&local_base_element_processor[0],
11992  &base_element_processor[0],
11993  n,MPI_INT,MPI_MAX,
11994  this->communicator_pt()->mpi_comm());
11995  }
11996  }
11997  else
11998  {
11999  // All the same...
12000  base_element_processor=local_base_element_processor;
12001  }
12002 
12003 
12004  dump_file << n << " # Number of base elements; partitioning follows.\n";
12005  for (unsigned e=0;e<n;e++)
12006  {
12007  dump_file << base_element_processor[e] << "\n";
12008  }
12009  dump_file << "8888 #test flag for end of base element distribution\n";
12010 
12011 #endif
12012 
12013  // Single mesh:
12014  //------------
12015  if(n_mesh==0)
12016  {
12017  // Dump single mesh refinement pattern (if mesh is refineable)
12018  if(TreeBasedRefineableMeshBase* mmesh_pt =
12019  dynamic_cast<TreeBasedRefineableMeshBase*>(mesh_pt(0)))
12020  {
12021  mmesh_pt->dump_refinement(dump_file);
12022  }
12023 #ifdef OOMPH_HAS_TRIANGLE_LIB
12024  // Dump triangle mesh TriangulateIO which represents mesh topology
12025  TriangleMeshBase* mmesh_pt = dynamic_cast<TriangleMeshBase*>(mesh_pt(0));
12026  if(mmesh_pt != 0 && mmesh_pt->use_triangulateio_restart())
12027  {
12028 #ifdef OOMPH_HAS_MPI
12029  // Check if the mesh is distributed, if that is the case then
12030  // additional info. needs to be saved
12031  if (mmesh_pt->is_mesh_distributed())
12032  {
12033  // Dump the info. related with the distribution of the mesh
12034  mmesh_pt->dump_distributed_info_for_restart(dump_file);
12035  }
12036 #endif
12037  mmesh_pt->dump_triangulateio(dump_file);
12038  }
12039 #endif
12040 
12041  }
12042 
12043  //Multiple submeshes
12044  //------------------
12045  else
12046  {
12047  // Loop over submeshes
12048  for (unsigned imesh=0;imesh<n_mesh;imesh++)
12049  {
12050  // Dump single mesh refinement pattern (if mesh is refineable)
12051  if(TreeBasedRefineableMeshBase* mmesh_pt =
12052  dynamic_cast<TreeBasedRefineableMeshBase*>(mesh_pt(imesh)))
12053  {
12054  mmesh_pt->dump_refinement(dump_file);
12055  }
12056 #ifdef OOMPH_HAS_TRIANGLE_LIB
12057  // Dump triangle mesh TriangulateIO which represents mesh topology
12058  TriangleMeshBase* mmesh_pt =
12059  dynamic_cast<TriangleMeshBase*>(mesh_pt(imesh));
12060  if(mmesh_pt != 0 && mmesh_pt->use_triangulateio_restart())
12061  {
12062 #ifdef OOMPH_HAS_MPI
12063  // Check if the mesh is distributed, if that is the case then
12064  // additional info. needs to be saved
12065  if (mmesh_pt->is_mesh_distributed())
12066  {
12067  // Dump the info. related with the distribution of the mesh
12068  mmesh_pt->dump_distributed_info_for_restart(dump_file);
12069  }
12070 #endif
12071  mmesh_pt->dump_triangulateio(dump_file);
12072  }
12073 #endif
12074  } // End of loop over submeshes
12075  }
12076 
12077 
12078  // Dump time
12079  // ---------
12080 
12081  // Flag to indicate unsteady run
12082  bool unsteady_flag=(time_pt()!=0);
12083  dump_file << unsteady_flag << " # bool flag for unsteady" << std::endl;
12084 
12085  // Current time and previous time increments for proper unsteady run
12086  if (unsteady_flag)
12087  {
12088  // Current time
12089  dump_file << time_pt()->time() << " # Time " << std::endl;
12090  // Timesteps
12091  unsigned n_dt=time_pt()->ndt();
12092  dump_file << n_dt << " # Number of timesteps " << std::endl;
12093  for (unsigned i=0;i<n_dt;i++)
12094  {
12095  dump_file << time_pt()->dt(i) << " # dt " << std::endl;
12096  }
12097  }
12098  // Dummy time and previous time increments for steady run
12099  else
12100  {
12101  // Current time
12102  dump_file << "0.0 # Dummy time from steady run " << std::endl;
12103  // Timesteps
12104  dump_file << "0 # Dummy number of timesteps from steady run" << std::endl;
12105  }
12106 
12107  // Loop over submeshes and dump their data
12108  unsigned nmesh=nsub_mesh();
12109  if (nmesh==0) nmesh=1;
12110  for(unsigned m=0;m<nmesh;m++)
12111  {
12112  mesh_pt(m)->dump(dump_file);
12113  }
12114 
12115  // Dump global data
12116 
12117  // Loop over global data
12118  unsigned Nglobal=Global_data_pt.size();
12119  dump_file << Nglobal << " # number of global Data items " << std::endl;
12120  for (unsigned iglobal=0;iglobal<Nglobal;iglobal++)
12121  {
12122  Global_data_pt[iglobal]->dump(dump_file);
12123  dump_file << std::endl;
12124  }
12125 
12126 }
12127 
12128 //=========================================================================
12129 /// Read refinement pattern of all refineable meshes and refine them
12130 /// accordingly, then read all Data and nodal position info from
12131 /// file for restart. Return flag to indicate if the restart was from
12132 /// steady or unsteady solution.
12133 //=========================================================================
12134 void Problem::read(std::ifstream& restart_file, bool& unsteady_restart)
12135 {
12136 
12137  // Check if the file is actually open as it won't be if it doesn't
12138  // exist! In that case we're almost certainly restarting the run on
12139  // a larger number of processors than the restart data was produced.
12140  // Say so and return
12141  bool restart_file_is_open=true;
12142  if (!restart_file.is_open())
12143  {
12144  std::ostringstream warn_message;
12145  warn_message << "Restart file isn't open -- I'm assuming that this is\n";
12146  warn_message << "because we're restarting on a larger number of\n";
12147  warn_message << "processor than were in use when the restart data was \n";
12148  warn_message << "dumped.\n";
12149  OomphLibWarning(warn_message.str(),
12150  "Problem::read()",
12151  OOMPH_EXCEPTION_LOCATION);
12152  restart_file_is_open=false;
12153  }
12154 
12155  // Number of (sub)meshes?
12156  unsigned n_mesh=std::max(unsigned(1),nsub_mesh());
12157 
12158  std::string input_string;
12159 
12160  // Read line up to termination sign
12161  getline(restart_file,input_string,'#');
12162 
12163  // Ignore rest of line
12164  restart_file.ignore(80,'\n');
12165 
12166  // Read in number of sub-meshes
12167  unsigned n_submesh_read;
12168  n_submesh_read=std::atoi(input_string.c_str());
12169 
12170 #ifdef PARANOID
12171  if (restart_file_is_open)
12172  {
12173  if (n_submesh_read!=n_mesh)
12174  {
12175  std::ostringstream error_message;
12176  error_message
12177  << "Number of sub-meshes specified in restart file, "
12178  << n_submesh_read << " doesn't \n match the my number of sub-meshes,"
12179  << n_mesh << std::endl
12180  << "Make sure all sub-meshes have been added to the global mesh\n"
12181  << "when calling the Problem::dump() function.\n";
12182  throw OomphLibError(error_message.str(),
12183  OOMPH_CURRENT_FUNCTION,
12184  OOMPH_EXCEPTION_LOCATION);
12185  }
12186  }
12187 #else
12188  // Suppress comiler warnings about non-used variable
12189  n_submesh_read++;
12190  n_submesh_read--;
12191 #endif
12192 
12193 
12194  // Read levels of refinement before pruning
12195 #ifdef OOMPH_HAS_MPI
12196  bool refine_and_prune_required=false;
12197 #endif
12198  Vector<unsigned> nrefinement_for_mesh(n_mesh);
12199  for (unsigned i=0;i<n_mesh;i++)
12200  {
12201  // Read line up to termination sign
12202  getline(restart_file,input_string,'#');
12203 
12204  // Ignore rest of line
12205  restart_file.ignore(80,'\n');
12206 
12207  // Convert
12208  nrefinement_for_mesh[i]=std::atoi(input_string.c_str());
12209 
12210  // Get pointer to sub-mesh in incarnation as tree-based refineable mesh
12211  TreeBasedRefineableMeshBase* ref_mesh_pt=
12212  dynamic_cast<TreeBasedRefineableMeshBase*>(mesh_pt(i));
12213 
12214  // If it's not a tree-based refineable mesh, ignore the following
12215  if (ref_mesh_pt==0)
12216  {
12217  if (nrefinement_for_mesh[i]!=0)
12218  {
12219  std::ostringstream error_stream;
12220  error_stream << "Nonzero uniform-refinement-when-pruned specified\n"
12221  << "even though mesh is not tree-based. Odd. May want\n"
12222  << "to check this carefully before disabling this \n"
12223  << "warning/error -- most likely if/when we start to\n"
12224  << "prune unstructured meshes [though I can't see why\n"
12225  << "we would want to do this, given that they are \n"
12226  << "currently totally re-generated...]\n";
12227  throw OomphLibError(error_stream.str(),
12228  OOMPH_CURRENT_FUNCTION,
12229  OOMPH_EXCEPTION_LOCATION);
12230  }
12231  }
12232  else
12233  {
12234  // Get min and max refinement level
12235  unsigned local_min_ref=0;
12236  unsigned local_max_ref=0;
12237  ref_mesh_pt->get_refinement_levels(local_min_ref,local_max_ref);
12238 
12239  // Overall min refinement level over all meshes
12240  unsigned min_ref=local_min_ref;
12241 
12242 #ifdef OOMPH_HAS_MPI
12244  {
12245  // Reconcile between processors: If (e.g. following
12246  // distribution/pruning) the mesh has no elements on this
12247  // processor) then ignore its contribution to the poll of
12248  // max/min refinement levels
12249  int int_local_min_ref=local_min_ref;
12250  if (ref_mesh_pt->nelement()==0)
12251  {
12252  int_local_min_ref=INT_MAX;
12253  }
12254  int int_min_ref=0;
12255  MPI_Allreduce(&int_local_min_ref,&int_min_ref,1,
12256  MPI_INT,MPI_MIN,
12257  Communicator_pt->mpi_comm());
12258 
12259  // Overall min refinement level over all meshes
12260  min_ref=unsigned(int_min_ref);
12261  }
12262 #endif
12263 
12264  // Need to refine less
12265  if (nrefinement_for_mesh[i]>=min_ref)
12266  {
12267  nrefinement_for_mesh[i]-=min_ref;
12268  }
12269  }
12270 
12271 #ifdef OOMPH_HAS_MPI
12272  if (nrefinement_for_mesh[i]>0)
12273  {
12274  refine_and_prune_required=true;
12275  }
12276 #endif
12277 
12278  }
12279 
12280 
12281  // Reconcile overall need to refine and prune (even empty
12282  // processors have to participate in some communication!)
12283 #ifdef OOMPH_HAS_MPI
12285  {
12286  unsigned local_req_flag=0;
12287  unsigned req_flag=0;
12288  if (refine_and_prune_required)
12289  {
12290  local_req_flag=1;
12291  }
12292  MPI_Allreduce(&local_req_flag,&req_flag,1,
12293  MPI_UNSIGNED,MPI_MAX,
12294  Communicator_pt->mpi_comm());
12295  refine_and_prune_required=false;
12296  if (req_flag==1)
12297  {
12298  refine_and_prune_required=true;
12299  }
12300 
12301  // If refine and prune is required make number of uniform
12302  // refinements for each mesh consistent otherwise code
12303  // hangs on "empty" processors for which no restart file exists
12304  if (refine_and_prune_required)
12305  {
12306  // This is what we have locally
12307  Vector<unsigned> local_nrefinement_for_mesh(nrefinement_for_mesh);
12308  // Synchronise over all processors with max operation
12309  MPI_Allreduce(&local_nrefinement_for_mesh[0],
12310  &nrefinement_for_mesh[0],
12311  n_mesh,
12312  MPI_UNSIGNED,MPI_MAX,
12313  Communicator_pt->mpi_comm());
12314 
12315 #ifdef PARANOID
12316  // Check it: Reconciliation should only be required for
12317  // for processors on which no restart file was opened and
12318  // for which the meshes are therefore empty
12319  bool fail=false;
12320  std::ostringstream error_message;
12321  error_message << "Number of uniform refinements was not consistent \n"
12322  << "for following meshes during restart on processor \n"
12323  << "on which restart file could be opened:\n";
12324  for (unsigned i=0;i<n_mesh;i++)
12325  {
12326  if ((local_nrefinement_for_mesh[i]!=nrefinement_for_mesh[i])&&
12327  restart_file_is_open)
12328  {
12329  fail=true;
12330  error_message << "Sub-mesh: " << i
12331  << "; local nrefinement: "
12332  << local_nrefinement_for_mesh[i] << " "
12333  << "; global/synced nrefinement: "
12334  << nrefinement_for_mesh[i] << "\n";
12335  }
12336  }
12337  if (fail)
12338  {
12339  OomphLibWarning(error_message.str(),
12340  "Problem::read()",
12341  OOMPH_EXCEPTION_LOCATION);
12342  }
12343 #endif
12344  }
12345  }
12346 #endif
12347 
12348  // Read line up to termination sign
12349  getline(restart_file,input_string,'#');
12350 
12351  // Ignore rest of line
12352  restart_file.ignore(80,'\n');
12353 
12354  // Check flag that indicates that we've read the final data
12355  unsigned tmp;
12356  tmp=std::atoi(input_string.c_str());
12357 
12358 #ifdef PARANOID
12359  if (restart_file_is_open)
12360  {
12361  if (tmp!=9999)
12362  {
12363  std::ostringstream error_message;
12364  error_message
12365  << "Error in reading restart data: Uniform refinement when pruned \n"
12366  << "flags should be followed by 9999.\n";
12367  throw OomphLibError(error_message.str(),
12368  OOMPH_CURRENT_FUNCTION,
12369  OOMPH_EXCEPTION_LOCATION);
12370  }
12371  }
12372 
12373 #else
12374  // Suppress comiler warnings about non-used variable
12375  tmp++;
12376  tmp--;
12377 #endif
12378 
12379 
12380 #ifdef OOMPH_HAS_MPI
12381 
12382  // Refine and prune if required
12383  if (refine_and_prune_required)
12384  {
12385  refine_uniformly(nrefinement_for_mesh);
12387  }
12388 
12389  // target_domain_for_local_non_halo_element[e] contains the number
12390  // of the domain [0,1,...,nproc-1] to which non-halo element e on THE
12391  // CURRENT PROCESSOR ONLY has been assigned. The order of the non-halo
12392  // elements is the same as in the Problem's mesh, with the halo
12393  // elements being skipped.
12394  Vector<unsigned> target_domain_for_local_non_halo_element;
12395 
12396  // Read line up to termination sign
12397  getline(restart_file,input_string,'#');
12398 
12399  // Ignore rest of line
12400  restart_file.ignore(80,'\n');
12401 
12402  // Get number of base elements as recorded
12403  unsigned n_base_element_read_in=atoi(input_string.c_str());
12404  unsigned nbase=Base_mesh_element_pt.size();
12405  if (restart_file_is_open)
12406  {
12407  if (n_base_element_read_in!=nbase)
12408  {
12409  std::ostringstream error_message;
12410  error_message
12411  << "About to read " << n_base_element_read_in << " base elements \n"
12412  << "though we only have " << nbase << " base elements in mesh.\n";
12413  throw OomphLibError(error_message.str(),
12414  OOMPH_CURRENT_FUNCTION,
12415  OOMPH_EXCEPTION_LOCATION);
12416  }
12417  }
12418 
12419  // Read in target_domain_for_base_element[e] for all base elements
12420  Vector<unsigned> target_domain_for_base_element(nbase);
12421  for (unsigned e=0;e<nbase;e++)
12422  {
12423  // Read line
12424  getline(restart_file,input_string);
12425 
12426  // Get target domain
12427  target_domain_for_base_element[e]=atoi(input_string.c_str());
12428  }
12429 
12430  // Read line up to termination sign
12431  getline(restart_file,input_string,'#');
12432 
12433  // Ignore rest of line
12434  restart_file.ignore(80,'\n');
12435 
12436  // Check flag that indicates that we've read the final data
12437  tmp=std::atoi(input_string.c_str());
12438 
12439 
12440 #ifdef PARANOID
12441  if (restart_file_is_open)
12442  {
12443  if (tmp!=8888)
12444  {
12445  std::ostringstream error_message;
12446  error_message
12447  << "Error in reading restart data: Target proc for base elements \n"
12448  << "should be followed by 8888.\n";
12449  throw OomphLibError(error_message.str(),
12450  OOMPH_CURRENT_FUNCTION,
12451  OOMPH_EXCEPTION_LOCATION);
12452  }
12453  }
12454 #endif
12455 
12456 
12457  // Loop over all elements (incl. any FaceElements) and assign
12458  // target domain for all local non-halo elements and check if
12459  // load balancing is required -- no need to do this if problem is
12460  // not distributed.
12461  unsigned load_balance_required_flag=0;
12463  {
12464  // Working with TreeBasedRefineableMeshBase mesh
12465  unsigned local_load_balance_required_flag=0;
12466  if(dynamic_cast<TreeBasedRefineableMeshBase*>(mesh_pt(0)))
12467  {
12468  const int my_rank=this->communicator_pt()->my_rank();
12469  unsigned nel=mesh_pt()->nelement();
12470  for (unsigned e=0;e<nel;e++)
12471  {
12473  if (!el_pt->is_halo())
12474  {
12475  // Get element number (plus one) in base element enumeration
12476  unsigned el_number_in_base_mesh_plus_one=
12478 
12479  // If it's zero then we haven't found it, it may be a FaceElement
12480  // (in which case we move it to the same processor as its bulk
12481  // element
12482  if (el_number_in_base_mesh_plus_one==0)
12483  {
12484  FaceElement* face_el_pt=dynamic_cast<FaceElement*>(el_pt);
12485  if (face_el_pt!=0)
12486  {
12487  // Get corresponding bulk element
12488  FiniteElement* bulk_el_pt=face_el_pt->bulk_element_pt();
12489 
12490  // Use its element number (plus one) in base element enumeration
12491  el_number_in_base_mesh_plus_one=
12493 
12494  // If this is zero too we have a problem
12495  if (el_number_in_base_mesh_plus_one==0)
12496  {
12497  throw OomphLibError("el_number_in_base_mesh_plus_one=0 for bulk",
12498  "Problem::read()",
12499  OOMPH_EXCEPTION_LOCATION);
12500  }
12501  }
12502  }
12503 
12504  // If we've made it here then we're not dealing with a
12505  // FaceElement but with an element that doesn't exist locally
12506  // --> WTF?
12507  if (el_number_in_base_mesh_plus_one==0)
12508  {
12509  throw OomphLibError("el_number_in_base_mesh_plus_one=0",
12510  OOMPH_CURRENT_FUNCTION,
12511  OOMPH_EXCEPTION_LOCATION);
12512  }
12513 
12514  // Assign target domain for next local non-halo element in
12515  // the order in which it's encountered in the global mesh
12516  target_domain_for_local_non_halo_element.push_back(
12517  target_domain_for_base_element[el_number_in_base_mesh_plus_one-1]);
12518 
12519  // Do elements on this processor to be moved elsewhere?
12520  if (int(target_domain_for_base_element
12521  [el_number_in_base_mesh_plus_one-1])
12522  !=my_rank)
12523  {
12524  local_load_balance_required_flag=1;
12525  }
12526  }
12527  }
12528 
12529  } // if (working with TreeBasedRefineableMeshBase mesh)
12530 
12531  // Get overall need to load balance by max
12532  MPI_Allreduce(&local_load_balance_required_flag,
12533  &load_balance_required_flag,1,
12534  MPI_UNSIGNED,MPI_MAX,this->communicator_pt()->mpi_comm());
12535 
12536  }
12537 
12538  // Do we need to load balance?
12539  if (load_balance_required_flag==1)
12540  {
12541  oomph_info << "Doing load balancing after pruning\n";
12542  DocInfo doc_info;
12543  doc_info.disable_doc();
12544  bool report_stats=false;
12545  load_balance(doc_info,report_stats,
12546  target_domain_for_local_non_halo_element);
12547  oomph_info << "Done load balancing after pruning\n";
12548  }
12549  else
12550  {
12551  oomph_info << "No need for load balancing after pruning\n";
12552  }
12553 
12554 #endif
12555 
12556 
12557  // Boolean to record if any unstructured bulk meshes have
12558  // been read in (and therefore completely re-generated, with new
12559  // elements and nodes) from disk
12560  bool have_read_unstructured_mesh=false;
12561 
12562  //Call the actions before adaptation
12564 
12565  // If there are unstructured meshes in the problem we need
12566  // to strip out any face elements that are attached to them
12567  // because restart of unstructured meshes re-creates their elements
12568  // and nodes from scratch, leading to dangling pointers from the
12569  // face elements to the old elements and nodes. This function is
12570  // virtual and (practically) empty in the Problem base class
12571  // but toggles a flag to indicate that it has been called. We can then
12572  // issue a warning below, prompting the user to consider overloading it
12573  // if the problem is found to contain unstructured bulk meshes.
12574  // Warning can be ignored if the bulk mesh is not associated with any
12575  // face elements.
12578 
12579  // Update number of submeshes
12580  n_mesh=nsub_mesh();
12581 
12582  // Single mesh:
12583  //------------
12584  if(n_mesh==0)
12585  {
12586  // Refine single mesh (if it's refineable)
12587  if(TreeBasedRefineableMeshBase* mmesh_pt =
12588  dynamic_cast<TreeBasedRefineableMeshBase*>(mesh_pt(0)))
12589  {
12590  // When we get in here the problem has been constructed
12591  // by the constructor and the mesh is its original unrefined
12592  // form.
12593  // RefineableMeshBase::refine(...) reads the refinement pattern from the
12594  // specified file and performs refinements until the mesh has
12595  // reached the same level of refinement as the mesh that existed
12596  // when the problem was dumped to disk.
12597  mmesh_pt->refine(restart_file);
12598  }
12599 #ifdef OOMPH_HAS_TRIANGLE_LIB
12600  // Regenerate mesh from triangulate IO if it's a triangular mesh
12601  TriangleMeshBase* mmesh_pt = dynamic_cast<TriangleMeshBase*>(mesh_pt(0));
12602  if(mmesh_pt != 0 && mmesh_pt->use_triangulateio_restart())
12603  {
12604 #ifdef OOMPH_HAS_MPI
12605  // Check if the mesh is distributed, if that is the case then
12606  // additional info. needs to be read
12607  if (mmesh_pt->is_mesh_distributed())
12608  {
12609  // Dump the info. related with the distribution of the mesh
12610  mmesh_pt->read_distributed_info_for_restart(restart_file);
12611  }
12612 #endif
12613  //The function reads the TriangulateIO data structure from the dump
12614  //file and then completely regenerates the mesh using the
12615  //data structure
12616  mmesh_pt->remesh_from_triangulateio(restart_file);
12617  have_read_unstructured_mesh=true;
12618 #ifdef OOMPH_HAS_MPI
12619  // Check if the mesh is distributed, if that is the case then we
12620  // need to re-establish the halo/haloed scheme (similar as in the
12621  // RefineableTriangleMesh::adapt() method)
12622  if (mmesh_pt->is_mesh_distributed())
12623  {
12625  this->communicator_pt(), restart_file);
12626  }
12627 #endif
12628  // Still left to update the polylines representation, that is performed
12629  // later since the nodes positions may still change when reading info.
12630  // for the mesh, see below
12631 
12632  }
12633 #endif
12634 
12635 
12636  }
12637 
12638  //Multiple submeshes
12639  //------------------
12640  else
12641  {
12642  // Loop over submeshes
12643  for (unsigned imesh=0;imesh<n_mesh;imesh++)
12644  {
12645  // Refine single mesh (if its refineable)
12646  if(TreeBasedRefineableMeshBase* mmesh_pt
12647  =dynamic_cast<TreeBasedRefineableMeshBase*>(mesh_pt(imesh)))
12648  {
12649  // When we get in here the problem has been constructed
12650  // by the constructor and the mesh is its original unrefined
12651  // form.
12652  // RefineableMeshBase::refine(...) reads the refinement pattern from
12653  // the specified file and performs refinements until the mesh has
12654  // reached the same level of refinement as the mesh that existed
12655  // when the problem was dumped to disk.
12656  mmesh_pt->refine(restart_file);
12657  }
12658 #ifdef OOMPH_HAS_TRIANGLE_LIB
12659  // Regenerate mesh from triangulate IO if it's a triangular mesh
12660  TriangleMeshBase* mmesh_pt =
12661  dynamic_cast<TriangleMeshBase*>(mesh_pt(imesh));
12662  if(mmesh_pt != 0 && mmesh_pt->use_triangulateio_restart())
12663  {
12664 #ifdef OOMPH_HAS_MPI
12665  // Check if the mesh is distributed, if that is the case then
12666  // additional info. needs to be read
12667  if (mmesh_pt->is_mesh_distributed())
12668  {
12669  // Dump the info. related with the distribution of the mesh
12670  mmesh_pt->read_distributed_info_for_restart(restart_file);
12671  }
12672 #endif
12673  //The function reads the TriangulateIO data structure from the dump
12674  //file and then completely regenerates the mesh using the
12675  //data structure
12676  mmesh_pt->remesh_from_triangulateio(restart_file);
12677  have_read_unstructured_mesh=true;
12678 
12679 #ifdef OOMPH_HAS_MPI
12680  // Check if the mesh is distributed, if that is the case then we
12681  // need to re-establish the halo/haloed scheme (similar as in the
12682  // RefineableTriangleMesh::adapt() method)
12683  if (mmesh_pt->is_mesh_distributed())
12684  {
12686  this->communicator_pt(), restart_file);
12687  }
12688 #endif
12689  // Still left to update the polylines representation, that is performed
12690  // later since the nodes positions may still change when reading info.
12691  // for the mesh, see below
12692 
12693  }
12694 #endif
12695  } // End of loop over submeshes
12696 
12697 
12698  // Rebuild the global mesh
12700  }
12701 
12702  //Any actions after adapt
12704 
12705  // Re-attach face elements (or whatever else needs to be done
12706  // following the total re-generation of the unstructured meshes
12709 
12710 
12711  // Issue warning:
12713  {
12714  if (have_read_unstructured_mesh)
12715  {
12718  {
12719  std::ostringstream warn_message;
12720  warn_message
12721  << "I've just read in some unstructured meshes and have, in\n"
12722  << "the process, totally re-generated their nodes and elements.\n"
12723  << "This may create dangling pointers that still point to the\n"
12724  << "old nodes and elements, e.g. because FaceElements were\n"
12725  << "attached to these meshes or pointers to nodes and elements\n"
12726  << "were stored somewhere. FaceElements should therefore be\n"
12727  << "removed before reading in these meshes, using an overloaded\n"
12728  << "version of the function\n\n"
12729  << " Problem::actions_before_read_unstructured_meshes()\n\n"
12730  << "and then re-attached using an overloaded version of\n\n"
12731  << " Problem::actions_after_read_unstructured_meshes().\n\n"
12732  << "The required content of these functions is likely to be similar\n"
12733  << "to the Problem::actions_before_adapt() and \n"
12734  << "Problem::actions_after_adapt() that would be required in\n"
12735  << "a spatially adaptive computation. If these functions already\n"
12736  << "exist and perform the required actions, the \n"
12737  << "actions_before/after_read_unstructured_meshes() functions\n"
12738  << "can remain empty because the former are called automatically.\n"
12739  << "In this case, this warning my be suppressed by setting the\n"
12740  << "public boolean\n\n"
12741  << " Problem::Suppress_warning_about_actions_before_read_unstructured_meshes\n\n"
12742  << "to true."
12743  << std::endl;
12744  OomphLibWarning(warn_message.str(),
12745  OOMPH_CURRENT_FUNCTION,
12746  OOMPH_EXCEPTION_LOCATION);
12747  }
12748  }
12749  }
12750 
12751  // Setup equation numbering scheme
12752  oomph_info <<"\nNumber of equations in Problem::read(): "
12753  << assign_eqn_numbers()
12754  << std::endl<< std::endl;
12755  // Read time info
12756  //---------------
12757  unsigned local_unsteady_restart_flag=0;
12758  double local_time=-DBL_MAX;
12759  unsigned local_n_dt=0;
12760 #ifdef OOMPH_HAS_MPI
12761  unsigned local_sync_needed_flag=0;
12762 #endif
12763  Vector<double> local_dt;
12764 
12765  if (restart_file.is_open())
12766  {
12767  oomph_info <<"Restart file exists" << std::endl;
12768 #ifdef OOMPH_HAS_MPI
12769  local_sync_needed_flag=0;
12770 #endif
12771  // Read line up to termination sign
12772  getline(restart_file,input_string,'#');
12773 
12774  // Ignore rest of line
12775  restart_file.ignore(80,'\n');
12776 
12777  // Is the restart data from an unsteady run?
12778  local_unsteady_restart_flag=atoi(input_string.c_str());
12779 
12780  // Read line up to termination sign
12781  getline(restart_file,input_string,'#');
12782 
12783  // Ignore rest of line
12784  restart_file.ignore(80,'\n');
12785 
12786  // Read in initial time and set
12787  local_time=atof(input_string.c_str());
12788 
12789  // Read line up to termination sign
12790  getline(restart_file,input_string,'#');
12791 
12792  // Ignore rest of line
12793  restart_file.ignore(80,'\n');
12794 
12795  // Read & set number of timesteps
12796  local_n_dt=atoi(input_string.c_str());
12797  local_dt.resize(local_n_dt);
12798 
12799  // Read in timesteps:
12800  for (unsigned i=0;i<local_n_dt;i++)
12801  {
12802  // Read line up to termination sign
12803  getline(restart_file,input_string,'#');
12804 
12805  // Ignore rest of line
12806  restart_file.ignore(80,'\n');
12807 
12808  // Read in initial time and set
12809  double prev_dt=atof(input_string.c_str());
12810  local_dt[i]=prev_dt;
12811  }
12812  }
12813  else
12814  {
12815  oomph_info <<"Restart file does not exist" << std::endl;
12816 #ifdef OOMPH_HAS_MPI
12817  local_sync_needed_flag=1;
12818 #endif
12819  }
12820 
12821 
12822  // No prepare global values, possibly via sync
12823  Vector<double> dt;
12824 
12825  // Do we need to sync?
12826  unsigned sync_needed_flag=0;
12827 
12828 #ifdef OOMPH_HAS_MPI
12830  {
12831  // Get need to sync by max
12832  MPI_Allreduce(&local_sync_needed_flag,&sync_needed_flag,1,
12833  MPI_UNSIGNED,MPI_MAX,this->communicator_pt()->mpi_comm());
12834  }
12835 #endif
12836 
12837  // Synchronise
12838  if (sync_needed_flag==1)
12839  {
12840 
12841 #ifdef OOMPH_HAS_MPI
12842 
12843 
12844 #ifdef PARANOID
12846  {
12847  std::ostringstream error_message;
12848  error_message
12849  << "Synchronisation of temporal restart data \n"
12850  << "required even though Problem hasn't been distributed -- very odd!\n";
12851  throw OomphLibError(error_message.str(),
12852  OOMPH_CURRENT_FUNCTION,
12853  OOMPH_EXCEPTION_LOCATION);
12854  }
12855 #endif
12856 
12857  // Get unsteady restart flag by max-based reduction
12858  unsigned unsteady_restart_flag=0;
12859  MPI_Allreduce(&local_unsteady_restart_flag,&unsteady_restart_flag,1,
12860  MPI_UNSIGNED,MPI_MAX,this->communicator_pt()->mpi_comm());
12861 
12862  // So, is it an unsteady restart?
12863  unsteady_restart=false;
12864  if (unsteady_restart_flag==1)
12865  {
12866  unsteady_restart=true;
12867 
12868  // Get time by max
12869  double time=-DBL_MAX;
12870  MPI_Allreduce(&local_time,&time,1,
12871  MPI_DOUBLE,MPI_MAX,this->communicator_pt()->mpi_comm());
12872  time_pt()->time()=time;
12873 
12874  // Get number of timesteps by max-based reduction
12875  unsigned n_dt=0;
12876  MPI_Allreduce(&local_n_dt,&n_dt,1,
12877  MPI_UNSIGNED,MPI_MAX,this->communicator_pt()->mpi_comm());
12878 
12879  // Resize whatever needs resizing
12880  time_pt()->resize(n_dt);
12881  dt.resize(n_dt);
12882  if (local_dt.size()==0)
12883  {
12884  local_dt.resize(n_dt,-DBL_MAX);
12885  }
12886 
12887  // Get timesteps increments by max-based reduction
12888  MPI_Allreduce(&local_dt[0],&dt[0],n_dt,
12889  MPI_DOUBLE,MPI_MAX,this->communicator_pt()->mpi_comm());
12890  }
12891 
12892 #else
12893 
12894  std::ostringstream error_message;
12895  error_message
12896  << "Synchronisation of temporal restart data \n"
12897  << "required even though we don't have mpi support -- very odd!\n";
12898  throw OomphLibError(error_message.str(),
12899  OOMPH_CURRENT_FUNCTION,
12900  OOMPH_EXCEPTION_LOCATION);
12901 
12902 #endif
12903 
12904  }
12905  // No sync needed -- just copy across
12906  else
12907  {
12908  unsteady_restart=false;
12909  if (local_unsteady_restart_flag==1)
12910  {
12911  unsteady_restart=true;
12912  time_pt()->time()=local_time;
12913  time_pt()->resize(local_n_dt);
12914  dt.resize(local_n_dt);
12915  for (unsigned i=0;i<local_n_dt;i++)
12916  {
12917  dt[i]=local_dt[i];
12918  }
12919  }
12920  }
12921 
12922  // Initialise timestep -- also sets the weights for all timesteppers
12923  // in the problem.
12924  if (unsteady_restart) initialise_dt(dt);
12925 
12926  // Loop over submeshes:
12927  unsigned nmesh=nsub_mesh();
12928  if (nmesh==0) nmesh=1;
12929  for (unsigned m=0;m<nmesh;m++)
12930  {
12931 
12932 
12933 // //---------------------------------------------------------
12934 // // Keep this commented out code around to debug restarts
12935 // //---------------------------------------------------------
12936 // std::ofstream some_file;
12937 // char filename[100];
12938 // sprintf(filename,"read_mesh%i_on_proc%i.dat",m,
12939 // this->communicator_pt()->my_rank());
12940 // some_file.open(filename);
12941 // mesh_pt(m)->output(some_file);
12942 // some_file.close();
12943 
12944 // sprintf(filename,"read_mesh%i_with_haloes_on_proc%i.dat",m,
12945 // this->communicator_pt()->my_rank());
12946 // mesh_pt(m)->enable_output_of_halo_elements();
12947 // some_file.open(filename);
12948 // mesh_pt(m)->output(some_file);
12949 // mesh_pt(m)->disable_output_of_halo_elements();
12950 // some_file.close();
12951 // oomph_info << "Doced mesh " << m << " before reading\n";
12952 
12953 // sprintf(filename,"read_nodes_mesh%i_on_proc%i.dat",m,
12954 // this->communicator_pt()->my_rank());
12955 // some_file.open(filename);
12956 // unsigned nnod=mesh_pt(m)->nnode();
12957 // for (unsigned j=0;j<nnod;j++)
12958 // {
12959 // Node* nod_pt=mesh_pt(m)->node_pt(j);
12960 // unsigned n=nod_pt->ndim();
12961 // for (unsigned i=0;i<n;i++)
12962 // {
12963 // some_file << nod_pt->x(i) << " ";
12964 // }
12965 // some_file << nod_pt->is_halo() << " "
12966 // << nod_pt->nvalue() << " "
12967 // << nod_pt->hang_code() << "\n";
12968 // }
12969 // some_file.close();
12970 // oomph_info << "Doced mesh " << m << " before reading\n";
12971 // //---------------------------------------------------------
12972 // // End keep this commented out code around to debug restarts
12973 // //---------------------------------------------------------
12974 
12975  mesh_pt(m)->read(restart_file);
12976 
12977 #ifdef OOMPH_HAS_TRIANGLE_LIB
12978  // Here update the polyline representation if working with
12979  // triangle base meshes
12980  if(TriangleMeshBase* mmesh_pt =
12981  dynamic_cast<TriangleMeshBase*>(mesh_pt(m)))
12982  {
12983  // In charge of updating the polylines representation to the
12984  // current refinement/unrefinement level after restart, it
12985  // also update the shared boundaries in case of working with a
12986  // distributed mesh
12987  mmesh_pt->update_polyline_representation_from_restart();
12988  }
12989 #endif // #ifdef OOMPH_HAS_TRIANGLE_LIB
12990 
12991  }
12992 
12993  // Read global data:
12994  //------------------
12995 
12996  // Number of global data
12997  unsigned Nglobal=Global_data_pt.size();
12998 
12999  // Read line up to termination sign
13000  getline(restart_file,input_string,'#');
13001 
13002  // Ignore rest of line
13003  restart_file.ignore(80,'\n');
13004 
13005  // Check # of nodes:
13006  unsigned long check_nglobal=atoi(input_string.c_str());
13007 
13008 
13009  if (restart_file_is_open)
13010  {
13011  if (check_nglobal!=Nglobal)
13012  {
13013  std::ostringstream error_message;
13014  error_message << "The number of global data " << Nglobal
13015  << " is not equal to that specified in the input file "
13016  << check_nglobal << std::endl;
13017 
13018  throw OomphLibError(error_message.str(),
13019  OOMPH_CURRENT_FUNCTION,
13020  OOMPH_EXCEPTION_LOCATION);
13021  }
13022  }
13023 
13024  for (unsigned iglobal=0;iglobal<Nglobal;iglobal++)
13025  {
13026  Global_data_pt[iglobal]->read(restart_file);
13027  }
13028 
13029 }
13030 
13031 //===================================================================
13032 /// Set all timesteps to the same value, dt, and assign
13033 /// weights for all timesteppers in the problem.
13034 //===================================================================
13035 void Problem::initialise_dt(const double& dt)
13036 {
13037  // Initialise the timesteps in the Problem's time object
13038  Time_pt->initialise_dt(dt);
13039 
13040  //Find out how many timesteppers there are
13041  unsigned n_time_steppers = ntime_stepper();
13042 
13043  //Loop over them all and set the weights
13044  for(unsigned i=0;i<n_time_steppers;i++)
13045  {
13047  if (time_stepper_pt(i)->adaptive_flag())
13048  {
13050  }
13051  }
13052 }
13053 
13054 //=========================================================================
13055 /// Set the value of the timesteps to be equal to the values passed in
13056 /// a vector and assign weights for all timesteppers in the problem
13057 //========================================================================
13059 {
13060  // Initialise the timesteps in the Problem's time object
13061  Time_pt->initialise_dt(dt);
13062 
13063  //Find out how many timesteppers there are
13064  unsigned n_time_steppers = ntime_stepper();
13065 
13066  //Loop over them all and set the weights
13067  for(unsigned i=0;i<n_time_steppers;i++)
13068  {
13070  if (time_stepper_pt(i)->adaptive_flag())
13071  {
13073  }
13074  }
13075 }
13076 
13077 //========================================================
13078 /// Self-test: Check meshes and global data. Return 0 for OK
13079 //========================================================
13081 {
13082  // Initialise
13083  bool passed=true;
13084 
13085  // Are there any submeshes?
13086  unsigned Nmesh=nsub_mesh();
13087 
13088  // Just one mesh: Check it
13089  if (Nmesh==0)
13090  {
13091  if (mesh_pt()->self_test()!=0)
13092  {
13093  passed=false;
13094  oomph_info
13095  << "\n ERROR: Failed Mesh::self_test() for single mesh in problem"
13096  << std::endl;
13097  }
13098  }
13099  // Loop over all submeshes and check them
13100  else
13101  {
13102  for (unsigned imesh=0;imesh<Nmesh;imesh++)
13103  {
13104  if (mesh_pt(imesh)->self_test()!=0)
13105  {
13106  passed=false;
13107  oomph_info << "\n ERROR: Failed Mesh::self_test() for mesh imesh"
13108  << imesh << std::endl;
13109  }
13110  }
13111  }
13112 
13113 
13114  // Check global data
13115  unsigned Nglobal=Global_data_pt.size();
13116  for (unsigned iglobal=0;iglobal<Nglobal;iglobal++)
13117  {
13118  if (Global_data_pt[iglobal]->self_test()!=0)
13119  {
13120  passed=false;
13121  oomph_info
13122  << "\n ERROR: Failed Data::self_test() for global data iglobal"
13123  << iglobal << std::endl;
13124  }
13125  }
13126 
13127 
13128 #ifdef OOMPH_HAS_MPI
13129 
13131  {
13132  // Note: This throws an error if it fails so no return is required.
13133  DocInfo tmp_doc_info;
13134  tmp_doc_info.disable_doc();
13135  check_halo_schemes(tmp_doc_info);
13136  }
13137 
13138 #endif
13139 
13140  // Return verdict
13141  if (passed) {return 0;}
13142  else {return 1;}
13143 
13144 }
13145 
13146 //====================================================================
13147 /// A function that is used to adapt a bifurcation-tracking
13148 /// problem, which requires separate interpolation of the
13149 /// associated eigenfunction. The error measure is chosen to be
13150 /// a suitable combination of the errors in the base flow and the
13151 /// eigenfunction. The bifurcation type is passed as an argument
13152 //=====================================================================
13154  unsigned &n_refined, unsigned &n_unrefined,
13155  const unsigned &bifurcation_type, const bool &actually_adapt)
13156 {
13157  //Storage for eigenfunction from the problem
13158  Vector<DoubleVector> eigenfunction;
13159  //Get the eigenfunction from the problem
13160  this->get_bifurcation_eigenfunction(eigenfunction);
13161 
13162  //Get the bifurcation parameter
13163  double *parameter_pt = this->bifurcation_parameter_pt();
13164 
13165  //Get the frequency parameter if tracking a Hopf bifurcation
13166  double omega = 0.0;
13167  //If we're tracking a Hopf then also get the frequency
13168  if(bifurcation_type==3)
13169  {
13170  omega = dynamic_cast<HopfHandler*>(assembly_handler_pt())->omega();
13171  }
13172 
13173  //If we're tracking a Pitchfork get the slack parameter (Hack)
13174  double sigma = 0.0;
13175  if(bifurcation_type==2)
13176  {
13177  sigma = this->dof(this->ndof()-1);
13178  }
13179 
13180  //We can now deactivate the bifurcation tracking in the problem
13181  //to restore the degrees of freedom to the unaugmented value
13183 
13184  //Next, we create copies of the present problem
13185  //The number of copies depends on the number of eigenfunctions
13186  //One copy for each eigenfunction
13187  const unsigned n_copies = eigenfunction.size();
13188  Copy_of_problem_pt.resize(n_copies);
13189 
13190  //Loop over the number of copies
13191  for(unsigned c=0;c<n_copies;c++)
13192  {
13193  //If we don't already have a copy
13194  if(Copy_of_problem_pt[c]==0)
13195  {
13196  //Create the copy
13197  Copy_of_problem_pt[c] = this->make_copy();
13198 
13199  //Refine the copy to the same level as the current problem
13200 
13201  //Find number of submeshes
13202  const unsigned N_mesh = Copy_of_problem_pt[c]->nsub_mesh();
13203  //If there is only one mesh
13204  if(N_mesh==0)
13205  {
13206  //Can we refine the mesh
13207  if(TreeBasedRefineableMeshBase* mmesh_pt =
13208  dynamic_cast<TreeBasedRefineableMeshBase*>(
13209  Copy_of_problem_pt[c]->mesh_pt(0)))
13210  {
13211  //Is the adapt flag set
13212  if(mmesh_pt->is_adaptation_enabled())
13213  {
13214  //Now get the original problem's mesh if it's refineable
13215  if(TreeBasedRefineableMeshBase* original_mesh_pt
13216  = dynamic_cast<TreeBasedRefineableMeshBase*>(this->mesh_pt(0)))
13217  {
13218  mmesh_pt->refine_base_mesh_as_in_reference_mesh(original_mesh_pt);
13219  }
13220  else
13221  {
13222  oomph_info
13223  <<
13224  "Info/Warning: Mesh in orginal problem is not refineable."
13225  << std::endl;
13226  }
13227  }
13228  else
13229  {
13230  oomph_info << "Info/Warning: Mesh adaptation is disabled in copy."
13231  << std::endl;
13232  }
13233  }
13234  else
13235  {
13236  oomph_info << "Info/Warning: Mesh cannot be adapted in copy."
13237  << std::endl;
13238  }
13239  } //End of single mesh case
13240  //Otherwise loop over the submeshes
13241  else
13242  {
13243  for(unsigned m=0;m<N_mesh;m++)
13244  {
13245  //Can we refine the submesh
13246  if(TreeBasedRefineableMeshBase* mmesh_pt =
13247  dynamic_cast<TreeBasedRefineableMeshBase*>(
13248  Copy_of_problem_pt[c]->mesh_pt(m)))
13249  {
13250  //Is the adapt flag set
13251  if(mmesh_pt->is_adaptation_enabled())
13252  {
13253  //Now get the original problem's mesh
13254  if(TreeBasedRefineableMeshBase* original_mesh_pt
13255  = dynamic_cast<TreeBasedRefineableMeshBase*>(this->mesh_pt(m)))
13256  {
13257  mmesh_pt->
13258  refine_base_mesh_as_in_reference_mesh(original_mesh_pt);
13259  }
13260  else
13261  {
13262  oomph_info
13263  <<
13264  "Info/Warning: Mesh in orginal problem is not refineable."
13265  << std::endl;
13266  }
13267  }
13268  else
13269  {
13270  oomph_info <<
13271  "Info/Warning: Mesh adaptation is disabled in copy."
13272  << std::endl;
13273  }
13274  }
13275  else
13276  {
13277  oomph_info << "Info/Warning: Mesh cannot be adapted in copy."
13278  << std::endl;
13279  }
13280  }
13281  //rebuild the global mesh in the copy
13282  Copy_of_problem_pt[c]->rebuild_global_mesh();
13283 
13284  } //End of multiple mesh case
13285 
13286  //Must call actions after adapt
13287  Copy_of_problem_pt[c]->actions_after_adapt();
13288 
13289  //Assign the equation numbers to the copy (quietly)
13291  }
13292  } //End of creation of copies
13293 
13294 
13295  //Now check some numbers
13296  for(unsigned c=0;c<n_copies;c++)
13297  {
13298  //Check that the dofs match for each copy
13299 #ifdef PARANOID
13300  //If the problems don't match then complain
13301  if(Copy_of_problem_pt[c]->ndof() != this->ndof())
13302  {
13303  std::ostringstream error_stream;
13304  error_stream
13305  <<
13306  "Number of unknowns in the problem copy " << c << " "
13307  << "not equal to number in the original:\n"
13308  << this->ndof() << " (original) " << Copy_of_problem_pt[c]->ndof()
13309  << " (copy)\n";
13310 
13311  throw OomphLibError(error_stream.str(),
13312  OOMPH_CURRENT_FUNCTION,
13313  OOMPH_EXCEPTION_LOCATION);
13314  }
13315 #endif
13316 
13317  //Assign the eigenfunction(s) to the copied problems
13318  Copy_of_problem_pt[c]->assign_eigenvector_to_dofs(eigenfunction[c]);
13319  //Set all pinned values to zero
13320  Copy_of_problem_pt[c]->set_pinned_values_to_zero();
13321  }
13322 
13323  //Symmetrise the problem if we are solving a pitchfork
13324  if(bifurcation_type==2)
13325  {Copy_of_problem_pt[0]->
13327 
13328  //Find error estimates based on current problem and eigenproblem
13329  //Now we need to get the error estimates for both problems.
13330  Vector<Vector<double> > base_error, eigenfunction_error;
13331  this->get_all_error_estimates(base_error);
13332  //Loop over the copies
13333  for(unsigned c=0;c<n_copies;c++)
13334  {
13335  //Get the error estimates for the copy
13336  Copy_of_problem_pt[c]->get_all_error_estimates(eigenfunction_error);
13337 
13338  //Find the number of meshes
13339  unsigned n_mesh = base_error.size();
13340 
13341 #ifdef PARANOID
13342  if(n_mesh != eigenfunction_error.size())
13343  {
13344  std::ostringstream error_stream;
13345  error_stream <<
13346  "Problems do not have the same number of meshes\n"
13347  << "Base : " << n_mesh
13348  << " : Eigenproblem : "
13349  << eigenfunction_error.size() << "\n";
13350  throw OomphLibError(error_stream.str(),
13351  OOMPH_CURRENT_FUNCTION,
13352  OOMPH_EXCEPTION_LOCATION);
13353  }
13354 #endif
13355 
13356  for(unsigned m=0;m<n_mesh;m++)
13357  {
13358  //Check the number of elements is the same
13359  unsigned n_element = base_error[m].size();
13360 #ifdef PARANOID
13361  if(n_element != eigenfunction_error[m].size())
13362  {
13363  std::ostringstream error_stream;
13364  error_stream << "Mesh " << m <<
13365  " does not have the same number of elements in the two problems:\n"
13366  << "Base: " << n_element << " : Eigenproblem: "
13367  << eigenfunction_error[m].size() << "\n";
13368  throw OomphLibError(error_stream.str(),
13369  OOMPH_CURRENT_FUNCTION,
13370  OOMPH_EXCEPTION_LOCATION);
13371  }
13372 #endif
13373  //Now add all the error esimates together
13374  for(unsigned e=0;e<n_element;e++)
13375  {
13376  //Add the error estimates (lazy)
13377  base_error[m][e] += eigenfunction_error[m][e];
13378  }
13379  }
13380  } //End of loop over copies
13381 
13382  //Then refine all problems based on the combined measure
13383  //if we are actually adapting (not just estimating the errors)
13384  if(actually_adapt)
13385  {
13386  this->adapt_based_on_error_estimates(n_refined,n_unrefined,base_error);
13387  for(unsigned c=0;c<n_copies;c++)
13388  {
13389  Copy_of_problem_pt[c]->
13390  adapt_based_on_error_estimates(n_refined,n_unrefined,base_error);
13391  }
13392  //Symmetrise the problem (again) if we are solving for a pitchfork
13393  if(bifurcation_type==2)
13394  {Copy_of_problem_pt[0]->
13396 
13397  //Now get the refined guess for the eigenvector
13398  for(unsigned c=0;c<n_copies;c++)
13399  {
13400  Copy_of_problem_pt[c]->get_dofs(eigenfunction[c]);
13401  }
13402  }
13403 
13404  //Reactivate the tracking
13405  switch(bifurcation_type)
13406  {
13407  //Fold tracking
13408  case 1:
13409  this->activate_fold_tracking(parameter_pt);
13410  break;
13411 
13412  //Pitchfork
13413  case 2:
13414  this->activate_pitchfork_tracking(parameter_pt,eigenfunction[0]);
13415  //reset the slack parameter
13416  this->dof(this->ndof()-1) = sigma;
13417  break;
13418 
13419  //Hopf
13420  case 3:
13421  this->activate_hopf_tracking(parameter_pt,omega,
13422  eigenfunction[0],eigenfunction[1]);
13423  break;
13424 
13425  default:
13426  std::ostringstream error_stream;
13427  error_stream << "Bifurcation type " << bifurcation_type << " not known\n"
13428  << "1: Fold, 2: Pitchfork, 3: Hopf\n";
13429  throw OomphLibError(error_stream.str(),
13430  OOMPH_CURRENT_FUNCTION,
13431  OOMPH_EXCEPTION_LOCATION);
13432  }
13433 }
13434 
13435 
13436 //====================================================================
13437 /// A function that is used to document the errors when
13438 /// adapting a bifurcation-tracking
13439 /// problem, which requires separate interpolation of the
13440 /// associated eigenfunction. The error measure is chosen to be
13441 /// a suitable combination of the errors in the base flow and the
13442 /// eigenfunction. The bifurcation type is passed as an argument
13443 //=====================================================================
13444 void Problem::bifurcation_adapt_doc_errors(const unsigned &bifurcation_type)
13445 {
13446  //Dummy arguments
13447  unsigned n_refined, n_unrefined;
13448  //Just call the bifurcation helper without actually adapting
13449  bifurcation_adapt_helper(n_refined,n_unrefined,bifurcation_type,false);
13450 }
13451 
13452 
13453 
13454 //========================================================================
13455 /// Adapt problem:
13456 /// Perform mesh adaptation for (all) refineable (sub)mesh(es),
13457 /// based on their own error estimates and the target errors specified
13458 /// in the mesh(es). Following mesh adaptation,
13459 /// update global mesh, and re-assign equation numbers.
13460 /// Return # of refined/unrefined elements. On return from this
13461 /// function, Problem can immediately be solved again.
13462 //======================================================================
13463 void Problem::adapt(unsigned &n_refined, unsigned &n_unrefined)
13464 {
13465 
13466  double t_start_total = 0.0;
13468  {
13469  t_start_total=TimingHelpers::timer();
13470  }
13471 
13472  //Get the bifurcation type
13473  int bifurcation_type = this->Assembly_handler_pt->bifurcation_type();
13474 
13475  bool continuation_problem = false;
13476 
13477  //If we have continuation data then we need to project that across to the
13478  //new mesh
13480  {
13481  if(Dof_derivative.size() != 0) {continuation_problem = true;}
13482  }
13483 
13484  //If we are tracking a bifurcation then call the bifurcation adapt function
13485  if(bifurcation_type!=0)
13486  {
13487  this->bifurcation_adapt_helper(n_refined,n_unrefined,bifurcation_type);
13488  //Return immediately
13489  return;
13490  }
13491 
13492  if(continuation_problem)
13493  {
13494  //Create a copy of the problem
13495  Copy_of_problem_pt.resize(2);
13496  //If we don't already have a copy
13497  for(unsigned c=0;c<2;c++)
13498  {
13499  if(Copy_of_problem_pt[c]==0)
13500  {
13501  //Create the copy
13502  Copy_of_problem_pt[c] = this->make_copy();
13503 
13504  //Refine the copy to the same level as the current problem
13505  //Must call actions before adapt
13506  Copy_of_problem_pt[c]->actions_before_adapt();
13507 
13508  //Find number of submeshes
13509  const unsigned N_mesh = Copy_of_problem_pt[c]->nsub_mesh();
13510 
13511  //If there is only one mesh
13512  if(N_mesh==0)
13513  {
13514  //Can we refine the mesh
13515  if(TreeBasedRefineableMeshBase* mmesh_pt =
13516  dynamic_cast<TreeBasedRefineableMeshBase*>(
13517  Copy_of_problem_pt[c]->mesh_pt(0)))
13518  {
13519  //Is the adapt flag set
13520  if(mmesh_pt->is_adaptation_enabled())
13521  {
13522  //Now get the original problem's mesh if it's refineable
13523  if(TreeBasedRefineableMeshBase* original_mesh_pt
13524  = dynamic_cast<TreeBasedRefineableMeshBase*>(this->mesh_pt(0)))
13525  {
13526  if(dynamic_cast<SolidMesh*>(original_mesh_pt)!=0)
13527  {
13528  oomph_info
13529  << "Info/Warning: Adaptive Continuation is broken in "
13530  << "SolidElement" << std::endl;
13531  }
13532  mmesh_pt->
13533  refine_base_mesh_as_in_reference_mesh(original_mesh_pt);
13534  }
13535  else
13536  {
13537  oomph_info
13538  <<
13539  "Info/Warning: Mesh in orginal problem is not refineable."
13540  << std::endl;
13541  }
13542  }
13543  else
13544  {
13545  oomph_info << "Info/Warning: Mesh adaptation is disabled in copy."
13546  << std::endl;
13547  }
13548  }
13549  else if(TriangleMeshBase* tmesh_pt =
13550  dynamic_cast<TriangleMeshBase*>(
13551  Copy_of_problem_pt[c]->mesh_pt(0)))
13552  {
13553  if(TriangleMeshBase* original_mesh_pt =
13554  dynamic_cast<TriangleMeshBase*>(
13555  this->mesh_pt(0)))
13556  {
13557  if(dynamic_cast<SolidMesh*>(original_mesh_pt)!=0)
13558  {
13559  oomph_info
13560  << "Info/Warning: Adaptive Continuation is broken in "
13561  << "SolidElement" << std::endl;
13562  }
13563 
13564  //Remesh using the triangulateIO of the base mesh
13565  //Done via a file, so a bit hacky but this will be
13566  //superseded very soon
13567  std::ofstream tri_dump("triangle_mesh.dmp");
13568  original_mesh_pt->dump_triangulateio(tri_dump);
13569  tri_dump.close();
13570  std::ifstream tri_read("triangle_mesh.dmp");
13571  tmesh_pt->remesh_from_triangulateio(tri_read);
13572  tri_read.close();
13573 
13574 
13575  //Set the nodes to be at the same positions
13576  //as the original just in case the
13577  //triangulatio is out of sync with the real data
13578  const unsigned n_node = original_mesh_pt->nnode();
13579  for(unsigned n=0;n<n_node;++n)
13580  {
13581  Node* const nod_pt = original_mesh_pt->node_pt(n);
13582  Node* const new_node_pt = tmesh_pt->node_pt(n);
13583  unsigned n_dim = nod_pt->ndim();
13584  for(unsigned i=0;i<n_dim;++i)
13585  {
13586  new_node_pt->x(i) = nod_pt->x(i);
13587  }
13588  }
13589  }
13590  else
13591  {
13592  oomph_info
13593  << "Info/warning: Original Mesh is not TriangleBased\n"
13594  << "... but the copy is!" << std::endl;
13595  }
13596  }
13597  else
13598  {
13599  oomph_info << "Info/Warning: Mesh cannot be adapted in copy."
13600  << std::endl;
13601  }
13602  } //End of single mesh case
13603  //Otherwise loop over the submeshes
13604  else
13605  {
13606  for(unsigned m=0;m<N_mesh;m++)
13607  {
13608  //Can we refine the submesh
13609  if(TreeBasedRefineableMeshBase* mmesh_pt =
13610  dynamic_cast<TreeBasedRefineableMeshBase*>(
13611  Copy_of_problem_pt[c]->mesh_pt(m)))
13612  {
13613  //Is the adapt flag set
13614  if(mmesh_pt->is_adaptation_enabled())
13615  {
13616  //Now get the original problem's mesh
13617  if(TreeBasedRefineableMeshBase* original_mesh_pt
13618  = dynamic_cast<TreeBasedRefineableMeshBase*>(this->mesh_pt(m)))
13619  {
13620  if(dynamic_cast<SolidMesh*>(original_mesh_pt)!=0)
13621  {
13622  oomph_info
13623  << "Info/Warning: Adaptive Continuation is broken in "
13624  << "SolidElement" << std::endl;
13625  }
13626 
13627  mmesh_pt->
13628  refine_base_mesh_as_in_reference_mesh(original_mesh_pt);
13629  }
13630  else
13631  {
13632  oomph_info
13633  <<
13634  "Info/Warning: Mesh in orginal problem is not refineable."
13635  << std::endl;
13636  }
13637  }
13638  else
13639  {
13640  oomph_info <<
13641  "Info/Warning: Mesh adaptation is disabled in copy."
13642  << std::endl;
13643  }
13644  }
13645  else if(TriangleMeshBase* tmesh_pt =
13646  dynamic_cast<TriangleMeshBase*>(
13647  Copy_of_problem_pt[c]->mesh_pt(m)))
13648  {
13649  if(TriangleMeshBase* original_mesh_pt =
13650  dynamic_cast<TriangleMeshBase*>(
13651  this->mesh_pt(m)))
13652  {
13653  if(dynamic_cast<SolidMesh*>(original_mesh_pt)!=0)
13654  {
13655  oomph_info
13656  << "Info/Warning: Adaptive Continuation is broken in "
13657  << "SolidElement" << std::endl;
13658  }
13659 
13660  //Remesh using the triangulateIO of the base mesh
13661  //Done via a file, so a bit hacky but this will be
13662  //superseded very soon
13663  std::ofstream tri_dump("triangle_mesh.dmp");
13664  original_mesh_pt->dump_triangulateio(tri_dump);
13665  tri_dump.close();
13666  std::ifstream tri_read("triangle_mesh.dmp");
13667  tmesh_pt->remesh_from_triangulateio(tri_read);
13668  tri_read.close();
13669 
13670  //Set the nodes to be at the same positions
13671  //as the original just in case the
13672  //triangulatio is out of sync with the real data
13673  const unsigned n_node = original_mesh_pt->nnode();
13674  for(unsigned n=0;n<n_node;++n)
13675  {
13676  Node* const nod_pt = original_mesh_pt->node_pt(n);
13677  Node* const new_node_pt = tmesh_pt->node_pt(n);
13678  unsigned n_dim = nod_pt->ndim();
13679  for(unsigned i=0;i<n_dim;++i)
13680  {
13681  new_node_pt->x(i) = nod_pt->x(i);
13682  }
13683  }
13684  }
13685  else
13686  {
13687  oomph_info
13688  << "Info/warning: Original Mesh is not TriangleBased\n"
13689  << "... but the copy is!" << std::endl;
13690  }
13691  }
13692  else
13693  {
13694  oomph_info << "Info/Warning: Mesh cannot be adapted in copy."
13695  << std::endl;
13696  }
13697  }
13698 
13699 
13700  //Must call actions after adapt
13701  Copy_of_problem_pt[c]->actions_after_adapt();
13702 
13703  //rebuild the global mesh in the copy
13704  Copy_of_problem_pt[c]->rebuild_global_mesh();
13705 
13706  } //End of multiple mesh case
13707 
13708  //Must call actions after adapt
13709  Copy_of_problem_pt[c]->actions_after_adapt();
13710 
13711  //Assign the equation numbers to the copy (quietly)
13713  }
13714 
13715  //Check that the dofs match for each copy
13716 #ifdef PARANOID
13717  //If the problems don't match then complain
13718  if(Copy_of_problem_pt[c]->ndof() != this->ndof())
13719  {
13720  std::ostringstream error_stream;
13721  error_stream
13722  <<
13723  "Number of unknowns in the problem copy "
13724  << c << " "
13725  << "not equal to number in the original:\n"
13726  << this->ndof() << " (original) " << Copy_of_problem_pt[c]->ndof()
13727  << " (copy)\n";
13728 
13729  throw OomphLibError(error_stream.str(),
13730  OOMPH_CURRENT_FUNCTION,
13731  OOMPH_EXCEPTION_LOCATION);
13732  }
13733 #endif
13734  }
13735 
13736  //Need to set the Dof derivatives to the copied problem
13737  //Assign the eigenfunction(s) to the copied problems
13738  unsigned ndof_local = Dof_distribution_pt->nrow_local();
13739  for(unsigned i=0;i<ndof_local;i++)
13740  {
13741  Copy_of_problem_pt[0]->dof(i) = this->dof_derivative(i);
13742  Copy_of_problem_pt[1]->dof(i) = this->dof_current(i);
13743  }
13744  //Set all pinned values to zero
13745  Copy_of_problem_pt[0]->set_pinned_values_to_zero();
13746  //Don't need to for the current dofs that are actuall the dofs
13747 
13748  //Now adapt
13749  Vector<Vector<double> > base_error;
13750  this->get_all_error_estimates(base_error);
13751  this->adapt_based_on_error_estimates(n_refined,n_unrefined,base_error);
13752  Copy_of_problem_pt[0]->
13753  adapt_based_on_error_estimates(n_refined,n_unrefined,base_error);
13754  Copy_of_problem_pt[1]->
13755  adapt_based_on_error_estimates(n_refined,n_unrefined,base_error);
13756 
13757  //Now sort out the Dof pointer
13758  ndof_local = Dof_distribution_pt->nrow_local();
13759  if(Dof_derivative.size() != ndof_local)
13760  {
13761  Dof_derivative.resize(ndof_local,0.0);
13762  }
13763  if(Dof_current.size() != ndof_local)
13764  {
13765  Dof_current.resize(ndof_local,0.0);
13766  }
13767  for(unsigned i=0;i<ndof_local;i++)
13768  {
13769  Dof_derivative[i] = Copy_of_problem_pt[0]->dof(i);
13770  Dof_current[i] = Copy_of_problem_pt[1]->dof(i);
13771  }
13772  //Return immediately
13773  return;
13774  }
13775 
13776  oomph_info << std::endl << std::endl;
13777  oomph_info << "Adapting problem:" << std::endl;
13778  oomph_info << "=================" << std::endl;
13779 
13780  double t_start = 0.0;
13782  {
13783  t_start=TimingHelpers::timer();
13784  }
13785 
13786  //Call the actions before adaptation
13788 
13789  double t_end = 0.0;
13791  {
13792  t_end = TimingHelpers::timer();
13793  oomph_info << "Time for actions before adapt: "
13794  << t_end-t_start << std::endl;
13795  t_start = TimingHelpers::timer();
13796  }
13797 
13798  // Initialise counters
13799  n_refined=0;
13800  n_unrefined=0;
13801 
13802  // Number of submeshes?
13803  unsigned Nmesh=nsub_mesh();
13804 
13805  // Single mesh:
13806  //------------
13807  if(Nmesh==0)
13808  {
13809  // Refine single mesh if possible
13810  if(RefineableMeshBase* mmesh_pt =
13811  dynamic_cast<RefineableMeshBase*>(mesh_pt(0)))
13812  {
13813  if (mmesh_pt->is_adaptation_enabled())
13814  {
13815  double t_start = TimingHelpers::timer();
13816 
13817  // Get pointer to error estimator
13818  ErrorEstimator* error_estimator_pt=mmesh_pt->
13819  spatial_error_estimator_pt();
13820 
13821 #ifdef PARANOID
13822  if (error_estimator_pt==0)
13823  {
13824  throw OomphLibError(
13825  "Error estimator hasn't been set yet",
13826  OOMPH_CURRENT_FUNCTION,
13827  OOMPH_EXCEPTION_LOCATION);
13828  }
13829 #endif
13830 
13831  // Get error for all elements
13832  Vector<double> elemental_error(mmesh_pt->nelement());
13833 
13834  if (mmesh_pt->doc_info_pt()==0)
13835  {
13836  error_estimator_pt->get_element_errors(mesh_pt(0),elemental_error);
13837  }
13838  else
13839  {
13840  error_estimator_pt->get_element_errors(mesh_pt(0),elemental_error,
13841  *mmesh_pt->doc_info_pt());
13842  }
13843 
13844  // Store max./min actual error
13845  mmesh_pt->max_error()=
13846  std::fabs(*std::max_element(elemental_error.begin(),
13847  elemental_error.end(),AbsCmp<double>()));
13848 
13849  mmesh_pt->min_error()=
13850  std::fabs(*std::min_element(elemental_error.begin(),
13851  elemental_error.end(),AbsCmp<double>()));
13852 
13853  oomph_info << "\n Max/min error: "
13854  << mmesh_pt->max_error() << " "
13855  << mmesh_pt->min_error()
13856  << std::endl << std::endl;
13857 
13858 
13860  {
13861  t_end = TimingHelpers::timer();
13862  oomph_info << "Time for error estimation: "
13863  << t_end-t_start << std::endl;
13864  t_start = TimingHelpers::timer();
13865  }
13866 
13867  // Adapt mesh
13868  mmesh_pt->adapt(elemental_error);
13869 
13870  // Add to counters
13871  n_refined+=mmesh_pt->nrefined();
13872  n_unrefined+=mmesh_pt->nunrefined();
13873 
13875  {
13876  t_end = TimingHelpers::timer();
13877  oomph_info << "Time for complete mesh adaptation "
13878  << "(but excluding comp of error estimate): "
13879  << t_end-t_start << std::endl;
13880  t_start = TimingHelpers::timer();
13881  }
13882 
13883  }
13884  else
13885  {
13886  oomph_info << "Info/Warning: Mesh adaptation is disabled." << std::endl;
13887  }
13888  }
13889  else
13890  {
13891  oomph_info << "Info/Warning: Mesh cannot be adapted" << std::endl;
13892  }
13893 
13894  }
13895  //Multiple submeshes
13896  //------------------
13897  else
13898  {
13899  // Loop over submeshes
13900  for (unsigned imesh=0;imesh<Nmesh;imesh++)
13901  {
13902  // Refine single mesh uniformly if possible
13903  if(RefineableMeshBase* mmesh_pt =
13904  dynamic_cast<RefineableMeshBase*>(mesh_pt(imesh)))
13905  {
13906  double t_start = TimingHelpers::timer();
13907 
13908  // Get pointer to error estimator
13909  ErrorEstimator* error_estimator_pt=mmesh_pt->
13910  spatial_error_estimator_pt();
13911 
13912 #ifdef PARANOID
13913  if (error_estimator_pt==0)
13914  {
13915  throw OomphLibError(
13916  "Error estimator hasn't been set yet",
13917  OOMPH_CURRENT_FUNCTION,
13918  OOMPH_EXCEPTION_LOCATION);
13919  }
13920 #endif
13921 
13922  if (mmesh_pt->is_adaptation_enabled())
13923  {
13924  // Get error for all elements
13925  Vector<double> elemental_error(mmesh_pt->nelement());
13926  if (mmesh_pt->doc_info_pt()==0)
13927  {
13928  error_estimator_pt->get_element_errors(mesh_pt(imesh),
13929  elemental_error);
13930  }
13931  else
13932  {
13933  error_estimator_pt->get_element_errors(mesh_pt(imesh),
13934  elemental_error,
13935  *mmesh_pt->doc_info_pt());
13936  }
13937 
13938  // Store max./min error if the mesh has any elements
13939  if (mesh_pt(imesh)->nelement()>0)
13940  {
13941  mmesh_pt->max_error()=
13942  std::fabs(*std::max_element(elemental_error.begin(),
13943  elemental_error.end(),
13944  AbsCmp<double>()));
13945 
13946  mmesh_pt->min_error()=
13947  std::fabs(*std::min_element(elemental_error.begin(),
13948  elemental_error.end(),
13949  AbsCmp<double>()));
13950  }
13951 
13952  oomph_info << "\n Max/min error: "
13953  << mmesh_pt->max_error() << " "
13954  << mmesh_pt->min_error() << std::endl;
13955 
13956 
13958  {
13959  t_end = TimingHelpers::timer();
13960  oomph_info << "Time for error estimation: "
13961  << t_end-t_start << std::endl;
13962  t_start = TimingHelpers::timer();
13963  }
13964 
13965  // Adapt mesh
13966  mmesh_pt->adapt(elemental_error);
13967 
13968  // Add to counters
13969  n_refined+=mmesh_pt->nrefined();
13970  n_unrefined+=mmesh_pt->nunrefined();
13971 
13972 
13974  {
13975  t_end = TimingHelpers::timer();
13976  oomph_info << "Time for complete mesh adaptation "
13977  << "(but excluding comp of error estimate): "
13978  << t_end-t_start << std::endl;
13979  t_start = TimingHelpers::timer();
13980  }
13981 
13982  }
13983  else
13984  {
13985  oomph_info << "Info/Warning: Mesh adaptation is disabled."
13986  << std::endl;
13987  }
13988  }
13989  else
13990  {
13991  oomph_info << "Info/Warning: Mesh cannot be adapted." << std::endl;
13992  }
13993 
13994  } // End of loop over submeshes
13995 
13996  // Rebuild the global mesh
13998 
13999  }
14000 
14001 
14003  {
14004  t_end = TimingHelpers::timer();
14005  oomph_info << "Total time for actual adaptation "
14006  << "(all meshes; incl error estimates): "
14007  << t_end-t_start << std::endl;
14008  t_start = TimingHelpers::timer();
14009  }
14010 
14011  //Any actions after adapt
14013 
14014 
14016  {
14017  t_end = TimingHelpers::timer();
14018  oomph_info << "Time for actions after adapt: "
14019  << t_end-t_start << std::endl;
14020  t_start = TimingHelpers::timer();
14021 
14022  oomph_info << "About to start re-assigning eqn numbers "
14023  << "with Problem::assign_eqn_numbers() at end of "
14024  << "Problem::adapt().\n";
14025  }
14026 
14027  //Attach the boundary conditions to the mesh
14028  oomph_info <<"\nNumber of equations: " << assign_eqn_numbers()
14029  << std::endl<< std::endl;
14030 
14031 
14033  {
14034  t_end = TimingHelpers::timer();
14035  oomph_info << "Time for re-assigning eqn numbers with "
14036  << "Problem::assign_eqn_numbers() at end of Problem::adapt(): "
14037  << t_end-t_start << std::endl;
14038  oomph_info << "Total time for adapt: "
14039  << t_end-t_start_total << std::endl;
14040  }
14041 
14042 
14043 }
14044 
14045 //========================================================================
14046 /// p-adapt problem:
14047 /// Perform mesh adaptation for (all) refineable (sub)mesh(es),
14048 /// based on their own error estimates and the target errors specified
14049 /// in the mesh(es). Following mesh adaptation,
14050 /// update global mesh, and re-assign equation numbers.
14051 /// Return # of refined/unrefined elements. On return from this
14052 /// function, Problem can immediately be solved again.
14053 //======================================================================
14054 void Problem::p_adapt(unsigned &n_refined, unsigned &n_unrefined)
14055 {
14056 
14057  double t_start_total = 0.0;
14059  {
14060  t_start_total=TimingHelpers::timer();
14061  }
14062 
14063  //Get the bifurcation type
14064  int bifurcation_type = this->Assembly_handler_pt->bifurcation_type();
14065 
14066  //If we are tracking a bifurcation then call the bifurcation adapt function
14067  if(bifurcation_type!=0)
14068  {
14069  this->bifurcation_adapt_helper(n_refined,n_unrefined,bifurcation_type);
14070  //Return immediately
14071  return;
14072  }
14073 
14074  oomph_info << std::endl << std::endl;
14075  oomph_info << "p-adapting problem:" << std::endl;
14076  oomph_info << "===================" << std::endl;
14077 
14078  double t_start = 0.0;
14080  {
14081  t_start=TimingHelpers::timer();
14082  }
14083 
14084  //Call the actions before adaptation
14086 
14087  double t_end = 0.0;
14089  {
14090  t_end = TimingHelpers::timer();
14091  oomph_info << "Time for actions before adapt: "
14092  << t_end-t_start << std::endl;
14093  t_start = TimingHelpers::timer();
14094  }
14095 
14096  // Initialise counters
14097  n_refined=0;
14098  n_unrefined=0;
14099 
14100  // Number of submeshes?
14101  unsigned Nmesh=nsub_mesh();
14102 
14103  // Single mesh:
14104  //------------
14105  if(Nmesh==0)
14106  {
14107  // Refine single mesh if possible
14108  if(RefineableMeshBase* mmesh_pt =
14109  dynamic_cast<RefineableMeshBase*>(mesh_pt(0)))
14110  {
14111  if (mmesh_pt->is_p_adaptation_enabled())
14112  {
14113  double t_start = TimingHelpers::timer();
14114 
14115  // Get pointer to error estimator
14116  ErrorEstimator* error_estimator_pt=mmesh_pt->
14117  spatial_error_estimator_pt();
14118 
14119 #ifdef PARANOID
14120  if (error_estimator_pt==0)
14121  {
14122  throw OomphLibError(
14123  "Error estimator hasn't been set yet",
14124  OOMPH_CURRENT_FUNCTION,
14125  OOMPH_EXCEPTION_LOCATION);
14126  }
14127 #endif
14128 
14129  // Get error for all elements
14130  Vector<double> elemental_error(mmesh_pt->nelement());
14131 
14132  if (mmesh_pt->doc_info_pt()==0)
14133  {
14134  error_estimator_pt->get_element_errors(mesh_pt(0),elemental_error);
14135  }
14136  else
14137  {
14138  error_estimator_pt->get_element_errors(mesh_pt(0),elemental_error,
14139  *mmesh_pt->doc_info_pt());
14140  }
14141 
14142  // Store max./min actual error
14143  mmesh_pt->max_error()=
14144  std::fabs(*std::max_element(elemental_error.begin(),
14145  elemental_error.end(),AbsCmp<double>()));
14146 
14147  mmesh_pt->min_error()=
14148  std::fabs(*std::min_element(elemental_error.begin(),
14149  elemental_error.end(),AbsCmp<double>()));
14150 
14151  oomph_info << "\n Max/min error: "
14152  << mmesh_pt->max_error() << " "
14153  << mmesh_pt->min_error()
14154  << std::endl << std::endl;
14155 
14156 
14158  {
14159  t_end = TimingHelpers::timer();
14160  oomph_info << "Time for error estimation: "
14161  << t_end-t_start << std::endl;
14162  t_start = TimingHelpers::timer();
14163  }
14164 
14165  // Adapt mesh
14166  mmesh_pt->p_adapt(elemental_error);
14167 
14168  // Add to counters
14169  n_refined+=mmesh_pt->nrefined();
14170  n_unrefined+=mmesh_pt->nunrefined();
14171 
14173  {
14174  t_end = TimingHelpers::timer();
14175  oomph_info << "Time for complete mesh adaptation "
14176  << "(but excluding comp of error estimate): "
14177  << t_end-t_start << std::endl;
14178  t_start = TimingHelpers::timer();
14179  }
14180 
14181  }
14182  else
14183  {
14184  oomph_info << "Info/Warning: Mesh adaptation is disabled." << std::endl;
14185  }
14186  }
14187  else
14188  {
14189  oomph_info << "Info/Warning: Mesh cannot be adapted" << std::endl;
14190  }
14191 
14192  }
14193  //Multiple submeshes
14194  //------------------
14195  else
14196  {
14197  // Loop over submeshes
14198  for (unsigned imesh=0;imesh<Nmesh;imesh++)
14199  {
14200  // Refine single mesh uniformly if possible
14201  if(RefineableMeshBase* mmesh_pt =
14202  dynamic_cast<RefineableMeshBase*>(mesh_pt(imesh)))
14203  {
14204  double t_start = TimingHelpers::timer();
14205 
14206  // Get pointer to error estimator
14207  ErrorEstimator* error_estimator_pt=mmesh_pt->
14208  spatial_error_estimator_pt();
14209 
14210 #ifdef PARANOID
14211  if (error_estimator_pt==0)
14212  {
14213  throw OomphLibError(
14214  "Error estimator hasn't been set yet",
14215  OOMPH_CURRENT_FUNCTION,
14216  OOMPH_EXCEPTION_LOCATION);
14217  }
14218 #endif
14219 
14220  if (mmesh_pt->is_p_adaptation_enabled())
14221  {
14222  // Get error for all elements
14223  Vector<double> elemental_error(mmesh_pt->nelement());
14224  if (mmesh_pt->doc_info_pt()==0)
14225  {
14226  error_estimator_pt->get_element_errors(mesh_pt(imesh),
14227  elemental_error);
14228  }
14229  else
14230  {
14231  error_estimator_pt->get_element_errors(mesh_pt(imesh),
14232  elemental_error,
14233  *mmesh_pt->doc_info_pt());
14234  }
14235 
14236  // Store max./min error if the mesh has any elements
14237  if (mesh_pt(imesh)->nelement()>0)
14238  {
14239  mmesh_pt->max_error()=
14240  std::fabs(*std::max_element(elemental_error.begin(),
14241  elemental_error.end(),
14242  AbsCmp<double>()));
14243 
14244  mmesh_pt->min_error()=
14245  std::fabs(*std::min_element(elemental_error.begin(),
14246  elemental_error.end(),
14247  AbsCmp<double>()));
14248  }
14249 
14250  oomph_info << "\n Max/min error: "
14251  << mmesh_pt->max_error() << " "
14252  << mmesh_pt->min_error() << std::endl;
14253 
14254 
14256  {
14257  t_end = TimingHelpers::timer();
14258  oomph_info << "Time for error estimation: "
14259  << t_end-t_start << std::endl;
14260  t_start = TimingHelpers::timer();
14261  }
14262 
14263  // Adapt mesh
14264  mmesh_pt->p_adapt(elemental_error);
14265 
14266  // Add to counters
14267  n_refined+=mmesh_pt->nrefined();
14268  n_unrefined+=mmesh_pt->nunrefined();
14269 
14270 
14272  {
14273  t_end = TimingHelpers::timer();
14274  oomph_info << "Time for complete mesh adaptation "
14275  << "(but excluding comp of error estimate): "
14276  << t_end-t_start << std::endl;
14277  t_start = TimingHelpers::timer();
14278  }
14279 
14280  }
14281  else
14282  {
14283  oomph_info << "Info/Warning: Mesh adaptation is disabled."
14284  << std::endl;
14285  }
14286  }
14287  else
14288  {
14289  oomph_info << "Info/Warning: Mesh cannot be adapted." << std::endl;
14290  }
14291 
14292  } // End of loop over submeshes
14293 
14294  // Rebuild the global mesh
14296 
14297  }
14298 
14299 
14301  {
14302  t_end = TimingHelpers::timer();
14303  oomph_info << "Total time for actual adaptation "
14304  << "(all meshes; incl error estimates): "
14305  << t_end-t_start << std::endl;
14306  t_start = TimingHelpers::timer();
14307  }
14308 
14309  //Any actions after adapt
14311 
14312 
14314  {
14315  t_end = TimingHelpers::timer();
14316  oomph_info << "Time for actions after adapt: "
14317  << t_end-t_start << std::endl;
14318  t_start = TimingHelpers::timer();
14319 
14320  oomph_info << "About to start re-assigning eqn numbers "
14321  << "with Problem::assign_eqn_numbers() at end of "
14322  << "Problem::adapt().\n";
14323  }
14324 
14325  //Attach the boundary conditions to the mesh
14326  oomph_info <<"\nNumber of equations: " << assign_eqn_numbers()
14327  << std::endl<< std::endl;
14328 
14329 
14331  {
14332  t_end = TimingHelpers::timer();
14333  oomph_info << "Time for re-assigning eqn numbers with "
14334  << "Problem::assign_eqn_numbers() at end of Problem::adapt(): "
14335  << t_end-t_start << std::endl;
14336  oomph_info << "Total time for adapt: "
14337  << t_end-t_start_total << std::endl;
14338  }
14339 
14340 
14341 }
14342 
14343 //========================================================================
14344 /// Perform mesh adaptation for (all) refineable (sub)mesh(es),
14345 /// based on the error estimates in elemental_error
14346 /// and the target errors specified
14347 /// in the mesh(es). Following mesh adaptation,
14348 /// update global mesh, and re-assign equation numbers.
14349 /// Return # of refined/unrefined elements. On return from this
14350 /// function, Problem can immediately be solved again.
14351 //========================================================================
14353  unsigned &n_unrefined,
14355  &elemental_error)
14356 {
14357  oomph_info << std::endl << std::endl;
14358  oomph_info << "Adapting problem:" << std::endl;
14359  oomph_info << "=================" << std::endl;
14360 
14361  //Call the actions before adaptation
14363 
14364  //Initialise counters
14365  n_refined = 0;
14366  n_unrefined = 0;
14367 
14368  // Number of submeshes?
14369  unsigned Nmesh=nsub_mesh();
14370 
14371  // Single mesh:
14372  //------------
14373  if(Nmesh==0)
14374  {
14375  // Refine single mesh uniformly if possible
14376  if(RefineableMeshBase* mmesh_pt =
14377  dynamic_cast<RefineableMeshBase*>(Problem::mesh_pt(0)))
14378  {
14379  if (mmesh_pt->is_adaptation_enabled())
14380  {
14381  // Adapt mesh
14382  mmesh_pt->adapt(elemental_error[0]);
14383 
14384  // Add to counters
14385  n_refined += mmesh_pt->nrefined();
14386  n_unrefined += mmesh_pt->nunrefined();
14387 
14388  }
14389  else
14390  {
14391  oomph_info << "Info/Warning: Mesh adaptation is disabled."
14392  << std::endl;
14393  }
14394  }
14395  else
14396  {
14397  oomph_info << "Info/Warning: Mesh cannot be adapted" << std::endl;
14398  }
14399 
14400  }
14401 
14402  //Multiple submeshes
14403  //------------------
14404  else
14405  {
14406  // Loop over submeshes
14407  for (unsigned imesh=0;imesh<Nmesh;imesh++)
14408  {
14409  // Refine single mesh uniformly if possible
14410  if(RefineableMeshBase* mmesh_pt =
14411  dynamic_cast<RefineableMeshBase*>(Problem::mesh_pt(imesh)))
14412  {
14413  if (mmesh_pt->is_adaptation_enabled())
14414  {
14415  // Adapt mesh
14416  mmesh_pt->adapt(elemental_error[imesh]);
14417 
14418  // Add to counters
14419  n_refined += mmesh_pt->nrefined();
14420  n_unrefined += mmesh_pt->nunrefined();
14421  }
14422  else
14423  {
14424  oomph_info << "Info/Warning: Mesh adaptation is disabled."
14425  << std::endl;
14426  }
14427  }
14428  else
14429  {
14430  oomph_info << "Info/Warning: Mesh cannot be adapted." << std::endl;
14431  }
14432 
14433  } // End of loop over submeshes
14434 
14435  // Rebuild the global mesh
14437 
14438  }
14439 
14440  //Any actions after adapt
14442 
14443  //Attach the boundary conditions to the mesh
14444  oomph_info <<"\nNumber of equations: " << assign_eqn_numbers()
14445  << std::endl<< std::endl;
14446 
14447 }
14448 
14449 
14450 //========================================================================
14451 /// Return the error estimates computed by (all) refineable
14452 /// (sub)mesh(es) in the elemental_error structure, which consists of
14453 /// a vector of elemental errors for each (sub)mesh.
14454 //========================================================================
14456 {
14457  // Number of submeshes?
14458  const unsigned Nmesh=nsub_mesh();
14459 
14460  // Single mesh:
14461  //------------
14462  if(Nmesh==0)
14463  {
14464  //There is only one mesh
14465  elemental_error.resize(1);
14466  // Refine single mesh uniformly if possible
14467  if(RefineableMeshBase* mmesh_pt =
14468  dynamic_cast<RefineableMeshBase*>(Problem::mesh_pt(0)))
14469  {
14470  //If we can adapt the mesh
14471  if(mmesh_pt->is_adaptation_enabled())
14472  {
14473  // Get pointer to error estimator
14474  ErrorEstimator* error_estimator_pt=mmesh_pt->
14475  spatial_error_estimator_pt();
14476 
14477 #ifdef PARANOID
14478  if (error_estimator_pt==0)
14479  {
14480  throw OomphLibError(
14481  "Error estimator hasn't been set yet",
14482  OOMPH_CURRENT_FUNCTION,
14483  OOMPH_EXCEPTION_LOCATION);
14484  }
14485 #endif
14486 
14487  // Get error for all elements
14488  elemental_error[0].resize(mmesh_pt->nelement());
14489  //Are we documenting the errors or not
14490  if(mmesh_pt->doc_info_pt()==0)
14491  {
14492  error_estimator_pt->get_element_errors(Problem::mesh_pt(0),
14493  elemental_error[0]);
14494  }
14495  else
14496  {
14497  error_estimator_pt->get_element_errors(Problem::mesh_pt(0),
14498  elemental_error[0],
14499  *mmesh_pt->doc_info_pt());
14500  }
14501 
14502  // Store max./min actual error
14503  mmesh_pt->max_error()=
14504  std::fabs(*std::max_element(elemental_error[0].begin(),
14505  elemental_error[0].end(),AbsCmp<double>()));
14506 
14507  mmesh_pt->min_error()=
14508  std::fabs(*std::min_element(elemental_error[0].begin(),
14509  elemental_error[0].end(),AbsCmp<double>()));
14510 
14511  oomph_info << "\n Max/min error: "
14512  << mmesh_pt->max_error() << " "
14513  << mmesh_pt->min_error() << std::endl;
14514  }
14515  else
14516  {
14517  oomph_info << "Info/Warning: Mesh adaptation is disabled."
14518  << std::endl;
14519  }
14520  }
14521  else
14522  {
14523  oomph_info << "Info/Warning: Mesh cannot be adapted" << std::endl;
14524  }
14525 
14526  }
14527 
14528  //Multiple submeshes
14529  //------------------
14530  else
14531  {
14532  //Resize to the number of submeshes
14533  elemental_error.resize(Nmesh);
14534 
14535  // Loop over submeshes
14536  for (unsigned imesh=0;imesh<Nmesh;imesh++)
14537  {
14538  // Refine single mesh uniformly if possible
14539  if(RefineableMeshBase* mmesh_pt =
14540  dynamic_cast<RefineableMeshBase*>(Problem::mesh_pt(imesh)))
14541  {
14542  // Get pointer to error estimator
14543  ErrorEstimator* error_estimator_pt=mmesh_pt->
14544  spatial_error_estimator_pt();
14545 
14546 #ifdef PARANOID
14547  if (error_estimator_pt==0)
14548  {
14549  throw OomphLibError(
14550  "Error estimator hasn't been set yet",
14551  OOMPH_CURRENT_FUNCTION,
14552  OOMPH_EXCEPTION_LOCATION);
14553  }
14554 #endif
14555  //If we can adapt the mesh
14556  if (mmesh_pt->is_adaptation_enabled())
14557  {
14558  // Get error for all elements
14559  elemental_error[imesh].resize(mmesh_pt->nelement());
14560  if (mmesh_pt->doc_info_pt()==0)
14561  {
14562  error_estimator_pt->get_element_errors(Problem::mesh_pt(imesh),
14563  elemental_error[imesh]);
14564  }
14565  else
14566  {
14567  error_estimator_pt->get_element_errors(Problem::mesh_pt(imesh),
14568  elemental_error[imesh],
14569  *mmesh_pt->doc_info_pt());
14570  }
14571 
14572  // Store max./min error
14573  mmesh_pt->max_error()=
14574  std::fabs(*std::max_element(elemental_error[imesh].begin(),
14575  elemental_error[imesh].end(),
14576  AbsCmp<double>()));
14577 
14578  mmesh_pt->min_error()=
14579  std::fabs(*std::min_element(elemental_error[imesh].begin(),
14580  elemental_error[imesh].end(),
14581  AbsCmp<double>()));
14582 
14583  oomph_info << "\n Max/min error: "
14584  << mmesh_pt->max_error() << " "
14585  << mmesh_pt->min_error() << std::endl;
14586  }
14587  else
14588  {
14589  oomph_info << "Info/Warning: Mesh adaptation is disabled."
14590  << std::endl;
14591  }
14592  }
14593  else
14594  {
14595  oomph_info << "Info/Warning: Mesh cannot be adapted." << std::endl;
14596  }
14597 
14598  } // End of loop over submeshes
14599 
14600  }
14601 }
14602 
14603 //========================================================================
14604 /// \short Get max and min error for all elements in submeshes
14605 //========================================================================
14607 {
14608  //Get the bifurcation type
14609  int bifurcation_type = this->Assembly_handler_pt->bifurcation_type();
14610  //If we are tracking a bifurcation then call the bifurcation adapt function
14611  if(bifurcation_type!=0)
14612  {
14613  this->bifurcation_adapt_doc_errors(bifurcation_type);
14614  //Return immediately
14615  return;
14616  }
14617 
14618  // Number of submeshes?
14619  unsigned Nmesh=nsub_mesh();
14620 
14621  // Single mesh:
14622  //------------
14623  if (Nmesh==0)
14624  {
14625  // Is the single mesh refineable?
14626  if (RefineableMeshBase* mmesh_pt =
14627  dynamic_cast<RefineableMeshBase*>(mesh_pt(0)))
14628  {
14629 
14630  // Get pointer to error estimator
14631  ErrorEstimator* error_estimator_pt=mmesh_pt->
14632  spatial_error_estimator_pt();
14633 
14634 #ifdef PARANOID
14635  if (error_estimator_pt==0)
14636  {
14637  throw OomphLibError(
14638  "Error estimator hasn't been set yet",
14639  OOMPH_CURRENT_FUNCTION,
14640  OOMPH_EXCEPTION_LOCATION);
14641  }
14642 #endif
14643 
14644  // Get error for all elements
14645  Vector<double> elemental_error(mmesh_pt->nelement());
14646  if (!doc_info.is_doc_enabled())
14647  {
14648  error_estimator_pt->get_element_errors(mesh_pt(0),
14649  elemental_error);
14650  }
14651  else
14652  {
14653  error_estimator_pt->get_element_errors(mesh_pt(0),
14654  elemental_error,
14655  doc_info);
14656  }
14657 
14658  // Store max./min actual error
14659  mmesh_pt->max_error()=
14660  std::fabs(*std::max_element(elemental_error.begin(),
14661  elemental_error.end(),AbsCmp<double>()));
14662 
14663  mmesh_pt->min_error()=
14664  std::fabs(*std::min_element(elemental_error.begin(),
14665  elemental_error.end(),AbsCmp<double>()));
14666 
14667  oomph_info << "\n Max/min error: "
14668  << mmesh_pt->max_error() << " "
14669  << mmesh_pt->min_error() << std::endl;
14670 
14671  }
14672 
14673  }
14674 
14675  //Multiple submeshes
14676  //------------------
14677  else
14678  {
14679  // Loop over submeshes
14680  for (unsigned imesh=0;imesh<Nmesh;imesh++)
14681  {
14682 
14683  // Is the single mesh refineable?
14684  if (RefineableMeshBase* mmesh_pt=
14685  dynamic_cast<RefineableMeshBase*>(mesh_pt(imesh)))
14686  {
14687 
14688  // Get pointer to error estimator
14689  ErrorEstimator* error_estimator_pt=mmesh_pt->
14690  spatial_error_estimator_pt();
14691 
14692 #ifdef PARANOID
14693  if (error_estimator_pt==0)
14694  {
14695  throw OomphLibError(
14696  "Error estimator hasn't been set yet",
14697  OOMPH_CURRENT_FUNCTION,
14698  OOMPH_EXCEPTION_LOCATION);
14699  }
14700 #endif
14701 
14702  // Get error for all elements
14703  Vector<double> elemental_error(mmesh_pt->nelement());
14704  if (mmesh_pt->doc_info_pt()==0)
14705  {
14706  error_estimator_pt->get_element_errors(mesh_pt(imesh),
14707  elemental_error);
14708  }
14709  else
14710  {
14711  error_estimator_pt->get_element_errors(mesh_pt(imesh),
14712  elemental_error,
14713  *mmesh_pt->doc_info_pt());
14714  }
14715 
14716  // Store max./min error if the mesh has any elements
14717  if (mesh_pt(imesh)->nelement()>0)
14718  {
14719  mmesh_pt->max_error()=
14720  std::fabs(*std::max_element(elemental_error.begin(),
14721  elemental_error.end(),AbsCmp<double>()));
14722 
14723  mmesh_pt->min_error()=
14724  std::fabs(*std::min_element(elemental_error.begin(),
14725  elemental_error.end(),AbsCmp<double>()));
14726  }
14727 
14728  oomph_info << "\n Max/min error: "
14729  << mmesh_pt->max_error() << " "
14730  << mmesh_pt->min_error() << std::endl;
14731  }
14732 
14733  } // End of loop over submeshes
14734 
14735  }
14736 
14737 }
14738 
14739 //========================================================================
14740 /// Refine (one and only!) mesh by splitting the elements identified
14741 /// by their numbers relative to the problems' only mesh, then rebuild
14742 /// the problem.
14743 //========================================================================
14745  elements_to_be_refined)
14746 {
14748 
14749  // Number of submeshes?
14750  unsigned Nmesh=nsub_mesh();
14751 
14752  // Single mesh:
14753  if (Nmesh==0)
14754  {
14755  // Refine single mesh if possible
14756  if(TreeBasedRefineableMeshBase* mmesh_pt =
14757  dynamic_cast<TreeBasedRefineableMeshBase*>(mesh_pt(0)))
14758  {
14759  mmesh_pt->refine_selected_elements(elements_to_be_refined);
14760  }
14761  else
14762  {
14763  oomph_info << "Info/Warning: Mesh cannot be refined "
14764  << std::endl;
14765  }
14766  }
14767  //Multiple submeshes
14768  else
14769  {
14770  std::ostringstream error_message;
14771  error_message << "Problem::refine_selected_elements(...) only works for\n"
14772  << "multiple-mesh problems if you specify the mesh\n"
14773  << "number in the function argument before the Vector,\n"
14774  << "or a Vector of Vectors for each submesh.\n"
14775  << std::endl;
14776  throw OomphLibError(error_message.str(),
14777  OOMPH_CURRENT_FUNCTION,
14778  OOMPH_EXCEPTION_LOCATION);
14779  }
14780 
14781  //Any actions after the adapatation phase
14783 
14784  //Attach the boundary conditions to the mesh
14785  oomph_info <<"Number of equations: "
14786  << assign_eqn_numbers() << std::endl;
14787 }
14788 
14789 //========================================================================
14790 /// Refine (one and only!) mesh by splitting the elements identified
14791 /// by their pointers, then rebuild the problem.
14792 //========================================================================
14794  elements_to_be_refined_pt)
14795 {
14797 
14798  // Number of submeshes?
14799  unsigned Nmesh=nsub_mesh();
14800 
14801  // Single mesh:
14802  if (Nmesh==0)
14803  {
14804  // Refine single mesh if possible
14805  if(TreeBasedRefineableMeshBase* mmesh_pt =
14806  dynamic_cast<TreeBasedRefineableMeshBase*>(mesh_pt(0)))
14807  {
14808  mmesh_pt->refine_selected_elements(elements_to_be_refined_pt);
14809  }
14810  else
14811  {
14812  oomph_info << "Info/Warning: Mesh cannot be refined "
14813  << std::endl;
14814  }
14815  }
14816  //Multiple submeshes
14817  else
14818  {
14819  std::ostringstream error_message;
14820  error_message << "Problem::refine_selected_elements(...) only works for\n"
14821  << "multiple-mesh problems if you specify the mesh\n"
14822  << "number in the function argument before the Vector,\n"
14823  << "or a Vector of Vectors for each submesh.\n"
14824  << std::endl;
14825  throw OomphLibError(error_message.str(),
14826  OOMPH_CURRENT_FUNCTION,
14827  OOMPH_EXCEPTION_LOCATION);
14828  }
14829 
14830  //Any actions after the adapatation phase
14832 
14833  //Do equation numbering
14834  oomph_info <<"Number of equations: " << assign_eqn_numbers()
14835  << std::endl;
14836 }
14837 
14838 //========================================================================
14839 /// Refine specified submesh by splitting the elements identified
14840 /// by their numbers relative to the specified mesh, then rebuild the problem.
14841 //========================================================================
14842 void Problem::refine_selected_elements(const unsigned& i_mesh,
14843  const Vector<unsigned>&
14844  elements_to_be_refined)
14845  {
14847 
14848  // Number of submeshes?
14849  unsigned n_mesh=nsub_mesh();
14850 
14851  if (i_mesh>=n_mesh)
14852  {
14853  std::ostringstream error_message;
14854  error_message <<
14855  "Problem only has " << n_mesh << " submeshes. Cannot refine submesh "
14856  << i_mesh << std::endl;
14857  throw OomphLibError(error_message.str(),
14858  OOMPH_CURRENT_FUNCTION,
14859  OOMPH_EXCEPTION_LOCATION);
14860  }
14861 
14862  // Refine single mesh if possible
14863  if(TreeBasedRefineableMeshBase* mmesh_pt =
14864  dynamic_cast<TreeBasedRefineableMeshBase*>(mesh_pt(i_mesh)))
14865  {
14866  mmesh_pt->refine_selected_elements(elements_to_be_refined);
14867  }
14868  else
14869  {
14870  oomph_info << "Info/Warning: Mesh cannot be refined "
14871  << std::endl;
14872  }
14873 
14874  if (n_mesh>1)
14875  {
14876  //Rebuild the global mesh
14878  }
14879 
14880  //Any actions after the adapatation phase
14882 
14883  //Do equation numbering
14884  oomph_info <<"Number of equations: " << assign_eqn_numbers()
14885  << std::endl;
14886  }
14887 
14888 
14889 //========================================================================
14890 /// Refine specified submesh by splitting the elements identified
14891 /// by their pointers, then rebuild the problem.
14892 //========================================================================
14893 void Problem::refine_selected_elements(const unsigned& i_mesh,
14895  elements_to_be_refined_pt)
14896 {
14898 
14899  // Number of submeshes?
14900  unsigned n_mesh=nsub_mesh();
14901 
14902  if (i_mesh>=n_mesh)
14903  {
14904  std::ostringstream error_message;
14905  error_message <<
14906  "Problem only has " << n_mesh << " submeshes. Cannot refine submesh "
14907  << i_mesh << std::endl;
14908  throw OomphLibError(error_message.str(),
14909  OOMPH_CURRENT_FUNCTION,
14910  OOMPH_EXCEPTION_LOCATION);
14911  }
14912 
14913  // Refine single mesh if possible
14914  if(TreeBasedRefineableMeshBase* mmesh_pt =
14915  dynamic_cast<TreeBasedRefineableMeshBase*>(mesh_pt(i_mesh)))
14916  {
14917  mmesh_pt->refine_selected_elements(elements_to_be_refined_pt);
14918  }
14919  else
14920  {
14921  oomph_info << "Info/Warning: Mesh cannot be refined "
14922  << std::endl;
14923  }
14924 
14925  if (n_mesh>1)
14926  {
14927  //Rebuild the global mesh
14929  }
14930 
14931  //Any actions after the adapatation phase
14933 
14934  //Do equation numbering
14935  oomph_info <<"Number of equations: " << assign_eqn_numbers()
14936  << std::endl;
14937 }
14938 
14939 //========================================================================
14940 /// Refine all submeshes by splitting the elements identified by their
14941 /// numbers relative to each submesh in a Vector of Vectors, then
14942 /// rebuild the problem.
14943 //========================================================================
14945  elements_to_be_refined)
14946  {
14948 
14949  // Number of submeshes?
14950  unsigned n_mesh=nsub_mesh();
14951 
14952  // Refine all submeshes if possible
14953  for (unsigned i_mesh=0; i_mesh<n_mesh; i_mesh++)
14954  {
14955  if(TreeBasedRefineableMeshBase* mmesh_pt =
14956  dynamic_cast<TreeBasedRefineableMeshBase*>(mesh_pt(i_mesh)))
14957  {
14958  mmesh_pt->refine_selected_elements(elements_to_be_refined[i_mesh]);
14959  }
14960  else
14961  {
14962  oomph_info << "Info/Warning: Mesh cannot be refined "
14963  << std::endl;
14964  }
14965  }
14966 
14967  //Rebuild the global mesh
14969 
14970  //Any actions after the adapatation phase
14972 
14973  //Do equation numbering
14974  oomph_info <<"Number of equations: " << assign_eqn_numbers()
14975  << std::endl;
14976  }
14977 
14978 //========================================================================
14979 /// Refine all submeshes by splitting the elements identified by their
14980 /// pointers within each submesh in a Vector of Vectors, then
14981 /// rebuild the problem.
14982 //========================================================================
14985  elements_to_be_refined_pt)
14986  {
14988 
14989  // Number of submeshes?
14990  unsigned n_mesh=nsub_mesh();
14991 
14992  // Refine all submeshes if possible
14993  for (unsigned i_mesh=0; i_mesh<n_mesh; i_mesh++)
14994  {
14995  if(TreeBasedRefineableMeshBase* mmesh_pt =
14996  dynamic_cast<TreeBasedRefineableMeshBase*>(mesh_pt(i_mesh)))
14997  {
14998  mmesh_pt->refine_selected_elements(elements_to_be_refined_pt[i_mesh]);
14999  }
15000  else
15001  {
15002  oomph_info << "Info/Warning: Mesh cannot be refined "
15003  << std::endl;
15004  }
15005  }
15006 
15007  //Rebuild the global mesh
15009 
15010  //Any actions after the adapatation phase
15012 
15013  //Do equation numbering
15014  oomph_info <<"Number of equations: " << assign_eqn_numbers()
15015  << std::endl;
15016  }
15017 
15018 //========================================================================
15019 /// p-refine (one and only!) mesh by refining the elements identified
15020 /// by their numbers relative to the problems' only mesh, then rebuild
15021 /// the problem.
15022 //========================================================================
15024  elements_to_be_refined)
15025 {
15027 
15028  // Number of submeshes?
15029  unsigned Nmesh=nsub_mesh();
15030 
15031  // Single mesh:
15032  if (Nmesh==0)
15033  {
15034  // Refine single mesh if possible
15035  if(TreeBasedRefineableMeshBase* mmesh_pt =
15036  dynamic_cast<TreeBasedRefineableMeshBase*>(mesh_pt(0)))
15037  {
15038  mmesh_pt->p_refine_selected_elements(elements_to_be_refined);
15039  }
15040  else
15041  {
15042  oomph_info << "Info/Warning: Mesh cannot be refined "
15043  << std::endl;
15044  }
15045  }
15046  //Multiple submeshes
15047  else
15048  {
15049  std::ostringstream error_message;
15050  error_message << "Problem::p_refine_selected_elements(...) only works for\n"
15051  << "multiple-mesh problems if you specify the mesh\n"
15052  << "number in the function argument before the Vector,\n"
15053  << "or a Vector of Vectors for each submesh.\n"
15054  << std::endl;
15055  throw OomphLibError(error_message.str(),
15056  OOMPH_CURRENT_FUNCTION,
15057  OOMPH_EXCEPTION_LOCATION);
15058  }
15059 
15060  //Any actions after the adapatation phase
15062 
15063  //Attach the boundary conditions to the mesh
15064  oomph_info <<"Number of equations: "
15065  << assign_eqn_numbers() << std::endl;
15066 }
15067 
15068 //========================================================================
15069 /// p-refine (one and only!) mesh by refining the elements identified
15070 /// by their pointers, then rebuild the problem.
15071 //========================================================================
15073  elements_to_be_refined_pt)
15074 {
15076 
15077  // Number of submeshes?
15078  unsigned Nmesh=nsub_mesh();
15079 
15080  // Single mesh:
15081  if (Nmesh==0)
15082  {
15083  // Refine single mesh if possible
15084  if(TreeBasedRefineableMeshBase* mmesh_pt =
15085  dynamic_cast<TreeBasedRefineableMeshBase*>(mesh_pt(0)))
15086  {
15087  mmesh_pt->p_refine_selected_elements(elements_to_be_refined_pt);
15088  }
15089  else
15090  {
15091  oomph_info << "Info/Warning: Mesh cannot be refined "
15092  << std::endl;
15093  }
15094  }
15095  //Multiple submeshes
15096  else
15097  {
15098  std::ostringstream error_message;
15099  error_message << "Problem::p_refine_selected_elements(...) only works for\n"
15100  << "multiple-mesh problems if you specify the mesh\n"
15101  << "number in the function argument before the Vector,\n"
15102  << "or a Vector of Vectors for each submesh.\n"
15103  << std::endl;
15104  throw OomphLibError(error_message.str(),
15105  OOMPH_CURRENT_FUNCTION,
15106  OOMPH_EXCEPTION_LOCATION);
15107  }
15108 
15109  //Any actions after the adapatation phase
15111 
15112  //Do equation numbering
15113  oomph_info <<"Number of equations: " << assign_eqn_numbers()
15114  << std::endl;
15115 }
15116 
15117 //========================================================================
15118 /// p-refine specified submesh by refining the elements identified
15119 /// by their numbers relative to the specified mesh, then rebuild the problem.
15120 //========================================================================
15121 void Problem::p_refine_selected_elements(const unsigned& i_mesh,
15122  const Vector<unsigned>&
15123  elements_to_be_refined)
15124  {
15126  "p-refinement for multiple submeshes has not yet been tested.",
15127  "Problem::p_refine_selected_elements()",
15128  OOMPH_EXCEPTION_LOCATION);
15129 
15131 
15132  // Number of submeshes?
15133  unsigned n_mesh=nsub_mesh();
15134 
15135  if (i_mesh>=n_mesh)
15136  {
15137  std::ostringstream error_message;
15138  error_message <<
15139  "Problem only has " << n_mesh << " submeshes. Cannot p-refine submesh "
15140  << i_mesh << std::endl;
15141  throw OomphLibError(error_message.str(),
15142  OOMPH_CURRENT_FUNCTION,
15143  OOMPH_EXCEPTION_LOCATION);
15144  }
15145 
15146  // Refine single mesh if possible
15147  if(TreeBasedRefineableMeshBase* mmesh_pt =
15148  dynamic_cast<TreeBasedRefineableMeshBase*>(mesh_pt(i_mesh)))
15149  {
15150  mmesh_pt->p_refine_selected_elements(elements_to_be_refined);
15151  }
15152  else
15153  {
15154  oomph_info << "Info/Warning: Mesh cannot be refined "
15155  << std::endl;
15156  }
15157 
15158  if (n_mesh>1)
15159  {
15160  //Rebuild the global mesh
15162  }
15163 
15164  //Any actions after the adapatation phase
15166 
15167  //Do equation numbering
15168  oomph_info <<"Number of equations: " << assign_eqn_numbers()
15169  << std::endl;
15170  }
15171 
15172 
15173 //========================================================================
15174 /// p-refine specified submesh by refining the elements identified
15175 /// by their pointers, then rebuild the problem.
15176 //========================================================================
15177 void Problem::p_refine_selected_elements(const unsigned& i_mesh,
15179  elements_to_be_refined_pt)
15180 {
15182  "p-refinement for multiple submeshes has not yet been tested.",
15183  "Problem::p_refine_selected_elements()",
15184  OOMPH_EXCEPTION_LOCATION);
15185 
15187 
15188  // Number of submeshes?
15189  unsigned n_mesh=nsub_mesh();
15190 
15191  if (i_mesh>=n_mesh)
15192  {
15193  std::ostringstream error_message;
15194  error_message <<
15195  "Problem only has " << n_mesh << " submeshes. Cannot p-refine submesh "
15196  << i_mesh << std::endl;
15197  throw OomphLibError(error_message.str(),
15198  OOMPH_CURRENT_FUNCTION,
15199  OOMPH_EXCEPTION_LOCATION);
15200  }
15201 
15202  // Refine single mesh if possible
15203  if(TreeBasedRefineableMeshBase* mmesh_pt =
15204  dynamic_cast<TreeBasedRefineableMeshBase*>(mesh_pt(i_mesh)))
15205  {
15206  mmesh_pt->p_refine_selected_elements(elements_to_be_refined_pt);
15207  }
15208  else
15209  {
15210  oomph_info << "Info/Warning: Mesh cannot be refined "
15211  << std::endl;
15212  }
15213 
15214  if (n_mesh>1)
15215  {
15216  //Rebuild the global mesh
15218  }
15219 
15220  //Any actions after the adapatation phase
15222 
15223  //Do equation numbering
15224  oomph_info <<"Number of equations: " << assign_eqn_numbers()
15225  << std::endl;
15226 }
15227 
15228 //========================================================================
15229 /// p-refine all submeshes by refining the elements identified by their
15230 /// numbers relative to each submesh in a Vector of Vectors, then
15231 /// rebuild the problem.
15232 //========================================================================
15234  elements_to_be_refined)
15235  {
15237  "p-refinement for multiple submeshes has not yet been tested.",
15238  "Problem::p_refine_selected_elements()",
15239  OOMPH_EXCEPTION_LOCATION);
15240 
15242 
15243  // Number of submeshes?
15244  unsigned n_mesh=nsub_mesh();
15245 
15246  // Refine all submeshes if possible
15247  for (unsigned i_mesh=0; i_mesh<n_mesh; i_mesh++)
15248  {
15249  if(TreeBasedRefineableMeshBase* mmesh_pt =
15250  dynamic_cast<TreeBasedRefineableMeshBase*>(mesh_pt(i_mesh)))
15251  {
15252  mmesh_pt->p_refine_selected_elements(elements_to_be_refined[i_mesh]);
15253  }
15254  else
15255  {
15256  oomph_info << "Info/Warning: Mesh cannot be refined "
15257  << std::endl;
15258  }
15259  }
15260 
15261  //Rebuild the global mesh
15263 
15264  //Any actions after the adapatation phase
15266 
15267  //Do equation numbering
15268  oomph_info <<"Number of equations: " << assign_eqn_numbers()
15269  << std::endl;
15270  }
15271 
15272 //========================================================================
15273 /// p-refine all submeshes by refining the elements identified by their
15274 /// pointers within each submesh in a Vector of Vectors, then
15275 /// rebuild the problem.
15276 //========================================================================
15279  elements_to_be_refined_pt)
15280  {
15282  "p-refinement for multiple submeshes has not yet been tested.",
15283  "Problem::p_refine_selected_elements()",
15284  OOMPH_EXCEPTION_LOCATION);
15285 
15287 
15288  // Number of submeshes?
15289  unsigned n_mesh=nsub_mesh();
15290 
15291  // Refine all submeshes if possible
15292  for (unsigned i_mesh=0; i_mesh<n_mesh; i_mesh++)
15293  {
15294  if(TreeBasedRefineableMeshBase* mmesh_pt =
15295  dynamic_cast<TreeBasedRefineableMeshBase*>(mesh_pt(i_mesh)))
15296  {
15297  mmesh_pt->p_refine_selected_elements(elements_to_be_refined_pt[i_mesh]);
15298  }
15299  else
15300  {
15301  oomph_info << "Info/Warning: Mesh cannot be refined "
15302  << std::endl;
15303  }
15304  }
15305 
15306  //Rebuild the global mesh
15308 
15309  //Any actions after the adapatation phase
15311 
15312  //Do equation numbering
15313  oomph_info <<"Number of equations: " << assign_eqn_numbers()
15314  << std::endl;
15315  }
15316 
15317 
15318 //========================================================================
15319 /// Helper function to do compund refinement of (all) refineable
15320 /// (sub)mesh(es) uniformly as many times as specified in vector and
15321 /// rebuild problem; doc refinement process. Set boolean argument
15322 /// to true if you want to prune immediately after refining the meshes
15323 /// individually.
15324 //========================================================================
15326  DocInfo& doc_info,
15327  const bool& prune)
15328 {
15329 
15330  double t_start = 0.0;
15332  {
15333  t_start=TimingHelpers::timer();
15334  }
15335 
15337 
15338  double t_end = 0.0;
15340  {
15341  t_end = TimingHelpers::timer();
15342  oomph_info
15343  << "Time for actions before adapt in Problem::refine_uniformly_aux(): "
15344  << t_end-t_start << std::endl;
15345  t_start = TimingHelpers::timer();
15346  }
15347 
15348  // Number of submeshes?
15349  unsigned n_mesh=nsub_mesh();
15350 
15351  // Single mesh:
15352  if (n_mesh==0)
15353  {
15354  // Refine single mesh uniformly if possible
15355  if(RefineableMeshBase* mmesh_pt =
15356  dynamic_cast<RefineableMeshBase*>(mesh_pt(0)))
15357  {
15358  unsigned nref=nrefine_for_mesh[0];
15359  for (unsigned i=0;i<nref;i++)
15360  {
15361  mmesh_pt->refine_uniformly(doc_info);
15362  }
15363  }
15364  else
15365  {
15366  oomph_info << "Info/Warning: Mesh cannot be refined uniformly "
15367  << std::endl;
15368  }
15369  }
15370  //Multiple submeshes
15371  else
15372  {
15373  // Loop over submeshes
15374  for (unsigned imesh=0;imesh<n_mesh;imesh++)
15375  {
15376  // Refine i-th submesh uniformly if possible
15377  if (RefineableMeshBase* mmesh_pt =
15378  dynamic_cast<RefineableMeshBase*>(mesh_pt(imesh)))
15379  {
15380  unsigned nref=nrefine_for_mesh[imesh];
15381  for (unsigned i=0;i<nref;i++)
15382  {
15383  mmesh_pt->refine_uniformly(doc_info);
15384  }
15385  }
15386  else
15387  {
15388  oomph_info << "Info/Warning: Cannot refine mesh " << imesh
15389  << std::endl;
15390  }
15391  }
15392  //Rebuild the global mesh
15394  }
15395 
15397  {
15398  t_end = TimingHelpers::timer();
15399  oomph_info
15400  << "Time for mesh-level mesh refinement in "
15401  << "Problem::refine_uniformly_aux(): "
15402  << t_end-t_start << std::endl;
15403  t_start = TimingHelpers::timer();
15404  }
15405 
15406  //Any actions after the adaptation phase
15408 
15409 
15411  {
15412  t_end = TimingHelpers::timer();
15413  oomph_info
15414  << "Time for actions after adapt Problem::refine_uniformly_aux(): "
15415  << t_end-t_start << std::endl;
15416  t_start = TimingHelpers::timer();
15417  }
15418 
15419 
15420 #ifdef OOMPH_HAS_MPI
15421 
15422  // Prune it?
15423  if (prune)
15424  {
15425  // Note: This calls assign eqn numbers already...
15429 
15431  {
15432  t_end = TimingHelpers::timer();
15433  oomph_info
15434  << "Time for Problem::prune_halo_elements_and_nodes() in "
15435  << "Problem::refine_uniformly_aux(): "
15436  << t_end-t_start << std::endl;
15437  }
15438  }
15439  else
15440 #else
15441  if (prune)
15442  {
15443  std::ostringstream error_message;
15444  error_message
15445  << "Requested pruning in serial build. Ignoring the request.\n";
15446  OomphLibWarning(error_message.str(),
15447  "Problem::refine_uniformly_aux()",
15448  OOMPH_EXCEPTION_LOCATION);
15449  }
15450 #endif
15451  {
15452  //Do equation numbering
15453  oomph_info <<"Number of equations after Problem::refine_uniformly_aux(): "
15454  << assign_eqn_numbers() << std::endl;
15455 
15457  {
15458  t_end = TimingHelpers::timer();
15459  oomph_info
15460  << "Time for Problem::assign_eqn_numbers() in "
15461  << "Problem::refine_uniformly_aux(): "
15462  << t_end-t_start << std::endl;
15463  }
15464  }
15465 
15466 }
15467 
15468 
15469 //========================================================================
15470 /// Helper function to do compund p-refinement of (all) p-refineable
15471 /// (sub)mesh(es) uniformly as many times as specified in vector and
15472 /// rebuild problem; doc refinement process. Set boolean argument
15473 /// to true if you want to prune immediately after refining the meshes
15474 /// individually.
15475 //========================================================================
15477  DocInfo& doc_info,
15478  const bool& prune)
15479 {
15480 
15481  double t_start = 0.0;
15483  {
15484  t_start=TimingHelpers::timer();
15485  }
15486 
15488 
15489  double t_end = 0.0;
15491  {
15492  t_end = TimingHelpers::timer();
15493  oomph_info
15494  << "Time for actions before adapt in Problem::p_refine_uniformly_aux(): "
15495  << t_end-t_start << std::endl;
15496  t_start = TimingHelpers::timer();
15497  }
15498 
15499  // Number of submeshes?
15500  unsigned n_mesh=nsub_mesh();
15501 
15502  // Single mesh:
15503  if (n_mesh==0)
15504  {
15505  // Refine single mesh uniformly if possible
15506  if(RefineableMeshBase* mmesh_pt =
15507  dynamic_cast<RefineableMeshBase*>(mesh_pt(0)))
15508  {
15509  unsigned nref=nrefine_for_mesh[0];
15510  for (unsigned i=0;i<nref;i++)
15511  {
15512  mmesh_pt->p_refine_uniformly(doc_info);
15513  }
15514  }
15515  else
15516  {
15517  oomph_info << "Info/Warning: Mesh cannot be p-refined uniformly "
15518  << std::endl;
15519  }
15520  }
15521  //Multiple submeshes
15522  else
15523  {
15525  "p-refinement for multiple submeshes has not yet been tested.",
15526  "Problem::p_refine_uniformly_aux()",
15527  OOMPH_EXCEPTION_LOCATION);
15528 
15529  // Loop over submeshes
15530  for (unsigned imesh=0;imesh<n_mesh;imesh++)
15531  {
15532  // Refine i-th submesh uniformly if possible
15533  if (RefineableMeshBase* mmesh_pt =
15534  dynamic_cast<RefineableMeshBase*>(mesh_pt(imesh)))
15535  {
15536  unsigned nref=nrefine_for_mesh[imesh];
15537  for (unsigned i=0;i<nref;i++)
15538  {
15539  mmesh_pt->p_refine_uniformly(doc_info);
15540  }
15541  }
15542  else
15543  {
15544  oomph_info << "Info/Warning: Cannot p-refine mesh " << imesh
15545  << std::endl;
15546  }
15547  }
15548  //Rebuild the global mesh
15550  }
15551 
15553  {
15554  t_end = TimingHelpers::timer();
15555  oomph_info
15556  << "Time for mesh-level mesh refinement in "
15557  << "Problem::p_refine_uniformly_aux(): "
15558  << t_end-t_start << std::endl;
15559  t_start = TimingHelpers::timer();
15560  }
15561 
15562  //Any actions after the adaptation phase
15564 
15565 
15567  {
15568  t_end = TimingHelpers::timer();
15569  oomph_info
15570  << "Time for actions after adapt Problem::p_refine_uniformly_aux(): "
15571  << t_end-t_start << std::endl;
15572  t_start = TimingHelpers::timer();
15573  }
15574 
15575 
15576 #ifdef OOMPH_HAS_MPI
15577 
15578  // Prune it?
15579  if (prune)
15580  {
15581  // Note: This calls assign eqn numbers already...
15585 
15587  {
15588  t_end = TimingHelpers::timer();
15589  oomph_info
15590  << "Time for Problem::prune_halo_elements_and_nodes() in "
15591  << "Problem::p_refine_uniformly_aux(): "
15592  << t_end-t_start << std::endl;
15593  }
15594  }
15595  else
15596 #else
15597  if (prune)
15598  {
15599  std::ostringstream error_message;
15600  error_message
15601  << "Requested pruning in serial build. Ignoring the request.\n";
15602  OomphLibWarning(error_message.str(),
15603  "Problem::p_refine_uniformly_aux()",
15604  OOMPH_EXCEPTION_LOCATION);
15605  }
15606 #endif
15607  {
15608  //Do equation numbering
15609  oomph_info <<"Number of equations after Problem::p_refine_uniformly_aux(): "
15610  << assign_eqn_numbers() << std::endl;
15611 
15613  {
15614  t_end = TimingHelpers::timer();
15615  oomph_info
15616  << "Time for Problem::assign_eqn_numbers() in "
15617  << "Problem::p_refine_uniformly_aux(): "
15618  << t_end-t_start << std::endl;
15619  }
15620  }
15621 
15622 }
15623 
15624 //========================================================================
15625 /// Refine submesh i_mesh uniformly and rebuild problem;
15626 /// doc refinement process.
15627 //========================================================================
15628 void Problem::refine_uniformly(const unsigned& i_mesh,
15629  DocInfo& doc_info)
15630 {
15632 
15633 #ifdef PARANOID
15634  // Number of submeshes?
15635  if (i_mesh>=nsub_mesh())
15636  {
15637  std::ostringstream error_message;
15638  error_message << "imesh " << i_mesh
15639  << " is greater than the number of sub meshes "
15640  << nsub_mesh() << std::endl;
15641 
15642  throw OomphLibError(error_message.str(),
15643  OOMPH_CURRENT_FUNCTION,
15644  OOMPH_EXCEPTION_LOCATION);
15645  }
15646 #endif
15647 
15648  // Refine single mesh uniformly if possible
15649  if(RefineableMeshBase* mmesh_pt =
15650  dynamic_cast<RefineableMeshBase*>(mesh_pt(i_mesh)))
15651  {
15652  mmesh_pt->refine_uniformly(doc_info);
15653  }
15654  else
15655  {
15656  oomph_info << "Info/Warning: Mesh cannot be refined uniformly "
15657  << std::endl;
15658  }
15659 
15660  //Rebuild the global mesh
15662 
15663  //Any actions after the adaptation phase
15665 
15666  //Do equation numbering
15667  oomph_info <<"Number of equations: "
15668  << assign_eqn_numbers() << std::endl;
15669 
15670 }
15671 
15672 //========================================================================
15673 /// p-refine submesh i_mesh uniformly and rebuild problem;
15674 /// doc refinement process.
15675 //========================================================================
15676 void Problem::p_refine_uniformly(const unsigned& i_mesh,
15677  DocInfo& doc_info)
15678 {
15680 
15681 #ifdef PARANOID
15682  // Number of submeshes?
15683  if (i_mesh>=nsub_mesh())
15684  {
15685  std::ostringstream error_message;
15686  error_message << "imesh " << i_mesh
15687  << " is greater than the number of sub meshes "
15688  << nsub_mesh() << std::endl;
15689 
15690  throw OomphLibError(error_message.str(),
15691  OOMPH_CURRENT_FUNCTION,
15692  OOMPH_EXCEPTION_LOCATION);
15693  }
15694 #endif
15695 
15696  // Refine single mesh uniformly if possible
15697  if(RefineableMeshBase* mmesh_pt =
15698  dynamic_cast<RefineableMeshBase*>(mesh_pt(i_mesh)))
15699  {
15700  mmesh_pt->p_refine_uniformly(doc_info);
15701  }
15702  else
15703  {
15704  oomph_info << "Info/Warning: Mesh cannot be refined uniformly "
15705  << std::endl;
15706  }
15707 
15708  //Rebuild the global mesh
15710 
15711  //Any actions after the adaptation phase
15713 
15714  //Do equation numbering
15715  oomph_info <<"Number of equations: "
15716  << assign_eqn_numbers() << std::endl;
15717 
15718 }
15719 
15720 
15721 //========================================================================
15722 /// Unrefine (all) refineable (sub)mesh(es) uniformly and rebuild problem.
15723 /// Return 0 for success,
15724 /// 1 for failure (if unrefinement has reached the coarsest permitted
15725 /// level)
15726 //========================================================================
15728 {
15729  // Call actions_before_adapt()
15731 
15732  // Has unrefinement been successful?
15733  unsigned success_flag=0;
15734 
15735  // Number of submeshes?
15736  unsigned n_mesh=nsub_mesh();
15737 
15738  // Single mesh:
15739  if (n_mesh==0)
15740  {
15741  // Unrefine single mesh uniformly if possible
15742  if(RefineableMeshBase* mmesh_pt =
15743  dynamic_cast<RefineableMeshBase*>(mesh_pt(0)))
15744  {
15745  success_flag+=mmesh_pt->unrefine_uniformly();
15746  }
15747  else
15748  {
15749  oomph_info << "Info/Warning: Mesh cannot be unrefined uniformly "
15750  << std::endl;
15751  }
15752  }
15753  // Multiple submeshes
15754  else
15755  {
15756  // Loop over submeshes
15757  for (unsigned imesh=0;imesh<n_mesh;imesh++)
15758  {
15759  // Unrefine i-th submesh uniformly if possible
15760  if (RefineableMeshBase* mmesh_pt=
15761  dynamic_cast<RefineableMeshBase*>(mesh_pt(imesh)))
15762  {
15763  success_flag+=mmesh_pt->unrefine_uniformly();
15764  }
15765  else
15766  {
15767  oomph_info << "Info/Warning: Cannot unrefine mesh " << imesh
15768  << std::endl;
15769  }
15770  }
15771  // Rebuild the global mesh
15773  }
15774 
15775  // Any actions after the adaptation phase
15777 
15778  // Do equation numbering
15779  oomph_info << " Number of equations: "
15780  << assign_eqn_numbers() << std::endl;
15781 
15782  // Judge success
15783  if (success_flag>0)
15784  {
15785  return 1;
15786  }
15787  else
15788  {
15789  return 0;
15790  }
15791 
15792 }
15793 
15794 //========================================================================
15795 /// Unrefine submesh i_mesh uniformly and rebuild problem.
15796 /// Return 0 for success,
15797 /// 1 for failure (if unrefinement has reached the coarsest permitted
15798 /// level)
15799 //========================================================================
15800 unsigned Problem::unrefine_uniformly(const unsigned& i_mesh)
15801 {
15803 
15804  // Has unrefinement been successful?
15805  unsigned success_flag=0;
15806 
15807 #ifdef PARANOID
15808  // Number of submeshes?
15809  if (i_mesh>=nsub_mesh())
15810  {
15811  std::ostringstream error_message;
15812  error_message << "imesh " << i_mesh
15813  << " is greater than the number of sub meshes "
15814  << nsub_mesh() << std::endl;
15815 
15816  throw OomphLibError(error_message.str(),
15817  OOMPH_CURRENT_FUNCTION,
15818  OOMPH_EXCEPTION_LOCATION);
15819  }
15820 #endif
15821 
15822  // Unrefine single mesh uniformly if possible
15823  if(RefineableMeshBase* mmesh_pt =
15824  dynamic_cast<RefineableMeshBase*>(mesh_pt(i_mesh)))
15825  {
15826  success_flag+=mmesh_pt->unrefine_uniformly();
15827  }
15828  else
15829  {
15830  oomph_info << "Info/Warning: Mesh cannot be unrefined uniformly "
15831  << std::endl;
15832  }
15833 
15834  //Rebuild the global mesh
15836 
15837  //Any actions after the adaptation phase
15839 
15840  //Do equation numbering
15841  oomph_info <<"Number of equations: "
15842  << assign_eqn_numbers() << std::endl;
15843 
15844  // Judge success
15845  if (success_flag>0)
15846  {
15847  return 1;
15848  }
15849  else
15850  {
15851  return 0;
15852  }
15853 
15854 }
15855 
15856 
15857 //========================================================================
15858 /// p-unrefine (all) p-refineable (sub)mesh(es) uniformly and rebuild problem;
15859 /// doc refinement process.
15860 //========================================================================
15862 {
15864 
15865  // Number of submeshes?
15866  unsigned n_mesh=nsub_mesh();
15867 
15868  // Single mesh:
15869  if (n_mesh==0)
15870  {
15871  // Unrefine single mesh uniformly if possible
15872  if(RefineableMeshBase* mmesh_pt =
15873  dynamic_cast<RefineableMeshBase*>(mesh_pt(0)))
15874  {
15875  mmesh_pt->p_unrefine_uniformly(doc_info);
15876  }
15877  else
15878  {
15879  oomph_info << "Info/Warning: Mesh cannot be p-unrefined uniformly "
15880  << std::endl;
15881  }
15882  }
15883  //Multiple submeshes
15884  else
15885  {
15886  //Not tested:
15887  throw OomphLibError("This functionality has not yet been tested.",
15888  OOMPH_CURRENT_FUNCTION,
15889  OOMPH_EXCEPTION_LOCATION);
15890  // Loop over submeshes
15891  for (unsigned imesh=0;imesh<n_mesh;imesh++)
15892  {
15893  // Unrefine i-th submesh uniformly if possible
15894  if (RefineableMeshBase* mmesh_pt=
15895  dynamic_cast<RefineableMeshBase*>(mesh_pt(imesh)))
15896  {
15897  mmesh_pt->p_unrefine_uniformly(doc_info);
15898  }
15899  else
15900  {
15901  oomph_info << "Info/Warning: Cannot p-unrefine mesh " << imesh
15902  << std::endl;
15903  }
15904  }
15905  //Rebuild the global mesh
15907  }
15908 
15909  //Any actions after the adaptation phase
15911 
15912  //Do equation numbering
15913  oomph_info <<"Number of equations: "
15914  << assign_eqn_numbers() << std::endl;
15915 
15916 }
15917 
15918 //========================================================================
15919 /// p-unrefine submesh i_mesh uniformly and rebuild problem;
15920 /// doc refinement process.
15921 //========================================================================
15922 void Problem::p_unrefine_uniformly(const unsigned& i_mesh,
15923  DocInfo& doc_info)
15924 {
15926 
15927 #ifdef PARANOID
15928  // Number of submeshes?
15929  if (i_mesh>=nsub_mesh())
15930  {
15931  std::ostringstream error_message;
15932  error_message << "imesh " << i_mesh
15933  << " is greater than the number of sub meshes "
15934  << nsub_mesh() << std::endl;
15935 
15936  throw OomphLibError(error_message.str(),
15937  OOMPH_CURRENT_FUNCTION,
15938  OOMPH_EXCEPTION_LOCATION);
15939  }
15940 #endif
15941 
15942  // Refine single mesh uniformly if possible
15943  if(RefineableMeshBase* mmesh_pt =
15944  dynamic_cast<RefineableMeshBase*>(mesh_pt(i_mesh)))
15945  {
15946  mmesh_pt->p_unrefine_uniformly(doc_info);
15947  }
15948  else
15949  {
15950  oomph_info << "Info/Warning: Mesh cannot be p-unrefined uniformly "
15951  << std::endl;
15952  }
15953 
15954  //Rebuild the global mesh
15956 
15957  //Any actions after the adaptation phase
15959 
15960  //Do equation numbering
15961  oomph_info <<"Number of equations: "
15962  << assign_eqn_numbers() << std::endl;
15963 
15964 }
15965 
15966 
15967 //========================================================================
15968 /// Do one timestep, dt, forward using Newton's method with specified
15969 /// tolerance and linear solver specified via member data.
15970 /// Keep adapting on all meshes to criteria specified in
15971 /// these meshes (up to max_adapt adaptations are performed).
15972 /// If first_timestep==true, re-set initial conditions after mesh adaptation.
15973 /// Shifting of time can be suppressed by overwriting the
15974 /// default value of shift (true). [Shifting must be done
15975 /// if first_timestep==true because we're constantly re-assigning
15976 /// the initial conditions; if first_timestep==true and shift==false
15977 /// shifting is performed anyway and a warning is issued.
15978 //========================================================================
15979 void Problem::unsteady_newton_solve(const double &dt,
15980  const unsigned &max_adapt,
15981  const bool &first_timestep,
15982  const bool& shift)
15983 {
15984 
15985 
15986  // Do shifting or not?
15987  bool shift_it=shift;
15988 
15989  // Warning:
15990  if (first_timestep && (!shift)
15992  {
15993  shift_it=true;
15994  oomph_info
15995  << "\n\n===========================================================\n";
15996  oomph_info << " ******** WARNING *********** \n";
15997  oomph_info
15998  << "===========================================================\n";
15999  oomph_info << "Problem::unsteady_newton_solve() called with " << std::endl;
16000  oomph_info << "first_timestep: " << first_timestep << std::endl;
16001  oomph_info << "shift: " << shift << std::endl;
16002  oomph_info << "This doesn't make sense (shifting does have to be done"
16003  << std::endl;
16004  oomph_info
16005  << "since we're constantly re-assigning the initial conditions"
16006  << std::endl;
16007  oomph_info
16008  << "\n===========================================================\n\n";
16009  }
16010 
16011 
16012  //Find the initial time
16013  double initial_time = time_pt()->time();
16014 
16015  // Max number of solves
16016  unsigned max_solve=max_adapt+1;
16017 
16018  // Adaptation loop
16019  //----------------
16020  for (unsigned isolve=0;isolve<max_solve;isolve++)
16021  {
16022  // Only adapt after the first solve has been done!
16023  if (isolve>0)
16024  {
16025  unsigned n_refined;
16026  unsigned n_unrefined;
16027 
16028  // Adapt problem
16029  adapt(n_refined,n_unrefined);
16030 
16031 #ifdef OOMPH_HAS_MPI
16032  // Adaptation only converges if ALL the processes have no
16033  // refinement or unrefinement to perform
16034  unsigned total_refined=0;
16035  unsigned total_unrefined=0;
16037  {
16038  MPI_Allreduce(&n_refined,&total_refined,1,MPI_UNSIGNED,MPI_SUM,
16039  this->communicator_pt()->mpi_comm());
16040  n_refined=total_refined;
16041  MPI_Allreduce(&n_unrefined,&total_unrefined,1,MPI_UNSIGNED,MPI_SUM,
16042  this->communicator_pt()->mpi_comm());
16043  n_unrefined=total_unrefined;
16044  }
16045 #endif
16046 
16047  oomph_info << "---> " << n_refined << " elements were refined, and "
16048  << n_unrefined << " were unrefined, in total." << std::endl;
16049 
16050  // Check convergence of adaptation cycle
16051  if ((n_refined==0)&&(n_unrefined==0))
16052  {
16053  oomph_info << "\n \n Solution is fully converged in "
16054  << "Problem::unsteady_newton_solver() \n \n ";
16055  break;
16056  }
16057 
16058  //Reset the time
16059  time_pt()->time() = initial_time;
16060 
16061  // Reset the inital condition on refined meshes. Note that because we
16062  // have reset the global time to the initial time, the initial conditions
16063  // are reset at time t=0 rather than at time t=dt
16064  if (first_timestep)
16065  {
16066  // Reset default set_initial_condition has been called flag to false
16068 
16069  oomph_info << "Re-setting initial condition " << std::endl;
16071 
16072  // If the default set_initial_condition function has been called,
16073  // we must not shift the timevalues on the first timestep, as we
16074  // will NOT be constantly re-assigning the initial condition
16075  if(Default_set_initial_condition_called) { shift_it=false; }
16076  }
16077  }
16078 
16079  //Now do the actual unsteady timestep
16080  //If it's the first time around the loop, or the first timestep
16081  //shift the timevalues, otherwise don't
16082  // Note: we need to shift if it's the first timestep because
16083  // we're constantly re-assigning the initial condition above!
16084  // The only exception to this is if the default set_initial_condition
16085  // function has been called, in which case we must NOT shift!
16086  if((isolve==0) || (first_timestep))
16087  {
16088  Problem::unsteady_newton_solve(dt,shift_it);
16089  }
16090  // Subsequent solve: Have shifted already -- don't do it again.
16091  else
16092  {
16093  shift_it=false;
16094  Problem::unsteady_newton_solve(dt,shift_it);
16095  }
16096 
16097  if (isolve==max_solve-1)
16098  {
16099  oomph_info << std::endl
16100  << "----------------------------------------------------------"
16101  << std::endl
16102  << "Reached max. number of adaptations in \n"
16103  << "Problem::unsteady_newton_solver().\n"
16104  << "----------------------------------------------------------"
16105  << std::endl
16106  << std::endl;
16107  }
16108 
16109  } // End of adaptation loop
16110 
16111 
16112 }
16113 
16114 
16115 
16116 //========================================================================
16117 /// \short Adaptive Newton solver.
16118 /// The linear solver takes a pointer to the problem (which defines
16119 /// the Jacobian \b J and the residual Vector \b r) and returns
16120 /// the solution \b x of the system
16121 /// \f[ {\bf J} {\bf x} = - \bf{r} \f].
16122 /// Performs at most max_adapt adaptations on all meshes.
16123 //========================================================================
16124 void Problem::newton_solve(const unsigned &max_adapt)
16125 {
16126  // Max number of solves
16127  unsigned max_solve=max_adapt+1;
16128 
16129  // Adaptation loop
16130  //----------------
16131  for (unsigned isolve=0;isolve<max_solve;isolve++)
16132  {
16133 
16134  // Only adapt after the first solve has been done!
16135  if (isolve>0)
16136  {
16137 
16138  unsigned n_refined;
16139  unsigned n_unrefined;
16140 
16141  // Adapt problem
16142  adapt(n_refined,n_unrefined);
16143 
16144 #ifdef OOMPH_HAS_MPI
16145  // Adaptation only converges if ALL the processes have no
16146  // refinement or unrefinement to perform
16147  unsigned total_refined=0;
16148  unsigned total_unrefined=0;
16150  {
16151  MPI_Allreduce(&n_refined,&total_refined,1,MPI_UNSIGNED,MPI_SUM,
16152  this->communicator_pt()->mpi_comm());
16153  n_refined=total_refined;
16154  MPI_Allreduce(&n_unrefined,&total_unrefined,1,MPI_UNSIGNED,MPI_SUM,
16155  this->communicator_pt()->mpi_comm());
16156  n_unrefined=total_unrefined;
16157  }
16158 #endif
16159 
16160  oomph_info << "---> " << n_refined << " elements were refined, and "
16161  << n_unrefined
16162  << " were unrefined"
16163 #ifdef OOMPH_HAS_MPI
16164  << ", in total (over all processors).\n";
16165 #else
16166  << ".\n";
16167 #endif
16168 
16169 
16170  // Check convergence of adaptation cycle
16171  if ((n_refined==0)&&(n_unrefined==0))
16172  {
16173  oomph_info << "\n \n Solution is fully converged in "
16174  << "Problem::newton_solver(). \n \n ";
16175  break;
16176  }
16177  }
16178 
16179 
16180  // Do actual solve
16181  //----------------
16182  {
16183 
16184  //Now update anything that needs updating
16185  // NOT NEEDED -- IS CALLED IN newton_solve BELOW! #
16186  // actions_before_newton_solve();
16187 
16188  try
16189  {
16190  //Solve the non-linear problem for this timestep with Newton's method
16191  newton_solve();
16192  }
16193  //Catch any exceptions thrown in the Newton solver
16194  catch(NewtonSolverError &error)
16195  {
16196  oomph_info << std::endl
16197  << "USER-DEFINED ERROR IN NEWTON SOLVER " << std::endl;
16198  //Check to see whether we have reached Max_iterations
16200  {
16201  oomph_info << "MAXIMUM NUMBER OF ITERATIONS ("
16202  << error.iterations
16203  << ") REACHED WITHOUT CONVERGENCE " << std::endl;
16204  }
16205  //If not, it must be that we have exceeded the maximum residuals
16206  else
16207  {
16208  oomph_info << "MAXIMUM RESIDUALS: " << error.maxres
16209  <<"EXCEEDS PREDEFINED MAXIMUM "
16210  << Max_residuals
16211  << std::endl;
16212  }
16213 
16214  //Die horribly!!
16215  std::ostringstream error_stream;
16216  error_stream << "Error occured in adaptive Newton solver. "
16217  << std::endl;
16218  throw OomphLibError(error_stream.str(),
16219  OOMPH_CURRENT_FUNCTION,
16220  OOMPH_EXCEPTION_LOCATION);
16221  }
16222 
16223  //Now update anything that needs updating
16224  // NOT NEEDED -- WAS CALLED IN newton_solve ABOVE
16225  // !actions_after_newton_solve();
16226 
16227  } //End of solve block
16228 
16229 
16230  if (isolve==max_solve-1)
16231  {
16232  oomph_info
16233  << std::endl
16234  << "----------------------------------------------------------"
16235  << std::endl
16236  << "Reached max. number of adaptations in \n"
16237  << "Problem::newton_solver().\n"
16238  << "----------------------------------------------------------"
16239  << std::endl << std::endl;
16240  }
16241 
16242  } // End of adaptation loop
16243 
16244 
16245 }
16246 
16247 //========================================================================
16248 /// Delete any external storage for any submeshes
16249 /// NB this would ordinarily take place within the adaptation procedure
16250 /// for each submesh (See RefineableMesh::adapt_mesh(...)), but there
16251 /// are instances where the actions_before/after_adapt routines are used
16252 /// and no adaptive routines are called in between (e.g. when doc-ing
16253 /// errors at the end of an adaptive newton solver)
16254 //========================================================================
16256 {
16257  //Number of submeshes
16258  unsigned n_mesh=nsub_mesh();
16259 
16260  //External storage will only exist if there is more than one (sub)mesh
16261  if (n_mesh>1)
16262  {
16263  for (unsigned i_mesh=0;i_mesh<n_mesh;i_mesh++)
16264  {
16266  }
16267  }
16268 }
16269 
16270 
16271 #ifdef OOMPH_HAS_MPI
16272 
16273 //====================================================================
16274 /// Get all the halo data stored on this processor and store pointers
16275 /// to the data in a map, indexed by the gobal eqn number
16276 //====================================================================
16277 void Problem::get_all_halo_data(std::map<unsigned,double*> &map_of_halo_data)
16278 {
16279  //Halo data is stored in the meshes, so kick the problem down to that
16280  //level
16281 
16282  //Find the number of meshes
16283  unsigned n_mesh = this->nsub_mesh();
16284  //If there are no submeshes it's only the main mesh
16285  if(n_mesh==0)
16286  {
16287  mesh_pt()->get_all_halo_data(map_of_halo_data);
16288  }
16289  //Otherwise loop over all the submeshes
16290  else
16291  {
16292  for(unsigned imesh=0;imesh<n_mesh;++imesh)
16293  {
16294  mesh_pt(imesh)->get_all_halo_data(map_of_halo_data);
16295  }
16296  }
16297 }
16298 
16299 
16300 //========================================================================
16301 /// Check the halo/haloed/shared node/element schemes.
16302 //========================================================================
16304 {
16305  // The bulk of the stuff that was in this routine is mesh-based, and
16306  // should therefore drop into the Mesh base class. All that needs to remain
16307  // here is a "wrapper" which calls the function dependent upon the number
16308  // of (sub)meshes that may have been distributed.
16309 
16310  unsigned n_mesh=nsub_mesh();
16311 
16312  if (n_mesh==0)
16313  {
16314  oomph_info << "Checking halo schemes on single mesh" << std::endl;
16315  doc_info.label()="_one_and_only_mesh_";
16316  mesh_pt()->check_halo_schemes(doc_info,
16318  }
16319  else // there are submeshes
16320  {
16321  for (unsigned i_mesh=0; i_mesh<n_mesh; i_mesh++)
16322  {
16323  oomph_info << "Checking halo schemes on submesh " << i_mesh << std::endl;
16324  std::stringstream tmp;
16325  tmp << "_mesh" << i_mesh << "_";
16326  doc_info.label()=tmp.str();
16327  mesh_pt(i_mesh)->check_halo_schemes(doc_info,
16329  }
16330  }
16331 
16332 }
16333 
16334 
16335 //========================================================================
16336 /// Synchronise all dofs by calling the appropriate synchronisation
16337 /// routines for all meshes and the assembly handler
16338 //========================================================================
16340 {
16341  // Synchronise dofs themselves
16342  bool do_halos=true;
16343  bool do_external_halos=false;
16344  this->synchronise_dofs(do_halos,do_external_halos);
16345 
16346 
16347  do_halos=false;
16348  do_external_halos=true;
16349  this->synchronise_dofs(do_halos,do_external_halos);
16350 
16351  //Now perform any synchronisation required by the assembly handler
16352  this->assembly_handler_pt()->synchronise();
16353 }
16354 
16355 
16356 
16357 //========================================================================
16358 /// Synchronise the degrees of freedom by overwriting
16359 /// the haloed values with their non-halo counterparts held
16360 /// on other processors. Bools control if we deal with data associated with
16361  /// external halo/ed elements/nodes or the "normal" halo/ed ones.
16362 //========================================================================
16363 void Problem::synchronise_dofs(const bool& do_halos,
16364  const bool& do_external_halos)
16365 {
16366  // Do we have submeshes?
16367  unsigned n_mesh_loop=1;
16368  unsigned nmesh=nsub_mesh();
16369  if (nmesh>0)
16370  {
16371  n_mesh_loop=nmesh;
16372  }
16373 
16374  // Local storage for number of processors and current processor
16375  const int n_proc=this->communicator_pt()->nproc();
16376 
16377  //If only one processor then return
16378  if(n_proc==1) {return;}
16379 
16380  const int my_rank=this->communicator_pt()->my_rank();
16381 
16382  // Storage for number of data to be sent to each processor
16383  Vector<int> send_n(n_proc,0);
16384 
16385  // Storage for all values to be sent to all processors
16386  Vector<double> send_data;
16387 
16388  // Start location within send_data for data to be sent to each processor
16389  Vector<int> send_displacement(n_proc,0);
16390 
16391  // Loop over all processors
16392  for(int rank=0;rank<n_proc;rank++)
16393  {
16394  //Set the offset for the current processor
16395  send_displacement[rank] = send_data.size();
16396 
16397  //Don't bother to do anything if the processor in the loop is the
16398  //current processor
16399  if(rank!=my_rank)
16400  {
16401  // Deal with sub-meshes one-by-one if required
16402  Mesh* my_mesh_pt=0;
16403 
16404  // Loop over submeshes
16405  for (unsigned imesh=0;imesh<n_mesh_loop;imesh++)
16406  {
16407  if (nmesh==0)
16408  {
16409  my_mesh_pt=mesh_pt();
16410  }
16411  else
16412  {
16413  my_mesh_pt=mesh_pt(imesh);
16414  }
16415 
16416  if (do_halos)
16417  {
16418  // How many of my nodes are haloed by the processor whose values
16419  // are updated?
16420  unsigned n_nod=my_mesh_pt->nhaloed_node(rank);
16421  for (unsigned n=0;n<n_nod;n++)
16422  {
16423  //Add the data for each haloed node to the vector
16424  my_mesh_pt->haloed_node_pt(rank,n)->add_values_to_vector(send_data);
16425  }
16426 
16427  // Now loop over haloed elements and prepare to add their
16428  // internal data to the big vector to be sent
16430  haloed_elem_pt=my_mesh_pt->haloed_element_pt(rank);
16431  unsigned nelem_haloed=haloed_elem_pt.size();
16432  for (unsigned e=0; e<nelem_haloed; e++)
16433  {
16434  haloed_elem_pt[e]->
16435  add_internal_data_values_to_vector(send_data);
16436  }
16437  }
16438 
16439  if (do_external_halos)
16440  {
16441  // How many of my nodes are externally haloed by the processor whose
16442  // values are updated? NB these nodes are on the external mesh.
16443  unsigned n_ext_nod=my_mesh_pt->nexternal_haloed_node(rank);
16444  for (unsigned n=0;n<n_ext_nod;n++)
16445  {
16446  //Add data from each external haloed node to the vector
16447  my_mesh_pt->external_haloed_node_pt(rank,n)->
16448  add_values_to_vector(send_data);
16449  }
16450 
16451  // Now loop over haloed elements and prepare to send internal data
16452  unsigned next_elem_haloed=my_mesh_pt->nexternal_haloed_element(rank);
16453  for (unsigned e=0; e<next_elem_haloed; e++)
16454  {
16455  my_mesh_pt->external_haloed_element_pt(rank,e)->
16456  add_internal_data_values_to_vector(send_data);
16457  }
16458  }
16459  } // end of loop over meshes
16460 
16461  }
16462 
16463  //Find the number of data added to the vector
16464  send_n[rank] = send_data.size() - send_displacement[rank];
16465 
16466  }
16467 
16468 
16469  //Storage for the number of data to be received from each processor
16470  Vector<int> receive_n(n_proc,0);
16471 
16472  //Now send numbers of data to be sent between all processors
16473  MPI_Alltoall(&send_n[0],1,MPI_INT,&receive_n[0],1,MPI_INT,
16474  this->communicator_pt()->mpi_comm());
16475 
16476  //We now prepare the data to be received
16477  //by working out the displacements from the received data
16478  Vector<int> receive_displacement(n_proc,0);
16479  int receive_data_count=0;
16480  for(int rank=0;rank<n_proc;++rank)
16481  {
16482  //Displacement is number of data received so far
16483  receive_displacement[rank] = receive_data_count;
16484  receive_data_count += receive_n[rank];
16485  }
16486 
16487  //Now resize the receive buffer for all data from all processors
16488  //Make sure that it has a size of at least one
16489  if(receive_data_count==0) {++receive_data_count;}
16490  Vector<double> receive_data(receive_data_count);
16491 
16492  //Make sure that the send buffer has size at least one
16493  //so that we don't get a segmentation fault
16494  if(send_data.size()==0) {send_data.resize(1);}
16495 
16496  //Now send the data between all the processors
16497  MPI_Alltoallv(&send_data[0],&send_n[0],&send_displacement[0],
16498  MPI_DOUBLE,
16499  &receive_data[0],&receive_n[0],
16500  &receive_displacement[0],
16501  MPI_DOUBLE,
16502  this->communicator_pt()->mpi_comm());
16503 
16504  //Now use the received data to update the halo nodes
16505  for (int send_rank=0;send_rank<n_proc;send_rank++)
16506  {
16507  //Don't bother to do anything for the processor corresponding to the
16508  //current processor or if no data were received from this processor
16509  if((send_rank != my_rank) && (receive_n[send_rank] != 0))
16510  {
16511  //Counter for the data within the large array
16512  unsigned count=receive_displacement[send_rank];
16513 
16514  // Deal with sub-meshes one-by-one if required
16515  Mesh* my_mesh_pt=0;
16516 
16517  // Loop over submeshes
16518  for (unsigned imesh=0;imesh<n_mesh_loop;imesh++)
16519  {
16520  if (nmesh==0)
16521  {
16522  my_mesh_pt=mesh_pt();
16523  }
16524  else
16525  {
16526  my_mesh_pt=mesh_pt(imesh);
16527  }
16528 
16529  if (do_halos)
16530  {
16531  // How many of my nodes are halos whose non-halo counter
16532  // parts live on processor send_rank?
16533  unsigned n_nod=my_mesh_pt->nhalo_node(send_rank);
16534  for (unsigned n=0;n<n_nod;n++)
16535  {
16536  //Read in values for each halo node
16537  my_mesh_pt->halo_node_pt(send_rank,n)->
16538  read_values_from_vector(receive_data,count);
16539  }
16540 
16541  // Get number of halo elements whose non-halo is
16542  // on process send_rank
16543  Vector<GeneralisedElement*> halo_elem_pt=my_mesh_pt->
16544  halo_element_pt(send_rank);
16545 
16546  unsigned nelem_halo=halo_elem_pt.size();
16547  for (unsigned e=0;e<nelem_halo;e++)
16548  {
16549  halo_elem_pt[e]->
16550  read_internal_data_values_from_vector(receive_data,count);
16551  }
16552  }
16553 
16554  if (do_external_halos)
16555  {
16556  // How many of my nodes are external halos whose external non-halo
16557  // counterparts live on processor send_rank?
16558  unsigned n_ext_nod=my_mesh_pt->nexternal_halo_node(send_rank);
16559 
16560  // Copy into the values of the external halo nodes
16561  // on the present processors
16562  for (unsigned n=0;n<n_ext_nod;n++)
16563  {
16564  //Read the data from the array into each halo node
16565  my_mesh_pt->external_halo_node_pt(send_rank,n)->
16566  read_values_from_vector(receive_data,count);
16567  }
16568 
16569  // Get number of halo elements whose non-halo is
16570  // on process send_rank
16571  unsigned next_elem_halo=my_mesh_pt->nexternal_halo_element(send_rank);
16572  for (unsigned e=0;e<next_elem_halo;e++)
16573  {
16574  my_mesh_pt->external_halo_element_pt(send_rank,e)->
16575  read_internal_data_values_from_vector(receive_data,count);
16576  }
16577  }
16578 
16579  } // end of loop over meshes
16580  }
16581  } //End of data is received
16582 } //End of synchronise
16583 
16584 
16585 //========================================================================
16586 /// Synchronise equation numbers and return the total
16587 /// number of degrees of freedom in the overall problem
16588 //========================================================================
16589 long Problem::synchronise_eqn_numbers(const bool& assign_local_eqn_numbers)
16590 {
16591  // number of equations on this processor, which at this stage is only known
16592  // by counting the number of dofs that have been added to the problem
16593  unsigned my_n_eqn = Dof_pt.size();
16594 
16595  // my rank
16596  unsigned my_rank = Communicator_pt->my_rank();
16597 
16598  // number of processors
16599  unsigned nproc = Communicator_pt->nproc();
16600 
16601 // // Time alternative communication
16602 // Vector<unsigned> n_eqn(nproc);
16603 // {
16604 // double t_start = TimingHelpers::timer();
16605 
16606 // // Gather numbers of equations (enumerated independently on all procs)
16607 // MPI_Allgather(&my_n_eqn,1,MPI_UNSIGNED,&n_eqn[0],
16608 // 1,MPI_INT,Communicator_pt->mpi_comm());
16609 
16610 // double t_end = TimingHelpers::timer();
16611 // oomph_info << "Time for allgather-based exchange of eqn numbers: "
16612 // << t_end-t_start << std::endl;
16613 // }
16614 
16615  double t_start = TimingHelpers::timer();
16616 
16617  // send my_n_eqn to with rank greater than my_rank
16618  unsigned n_send = nproc-my_rank-1;
16619  Vector<MPI_Request> send_req(n_send);
16620  for (unsigned p = my_rank+1; p < nproc; p++)
16621  {
16622  MPI_Isend(&my_n_eqn,1,MPI_UNSIGNED,p,0,
16623  Communicator_pt->mpi_comm(),&send_req[p-my_rank-1]);
16624  }
16625 
16626  // recv n_eqn from processors with rank less than my_rank
16627  Vector<unsigned> n_eqn_on_proc(my_rank);
16628  for (unsigned p = 0; p < my_rank; p++)
16629  {
16630  MPI_Recv(&n_eqn_on_proc[p],1,MPI_UNSIGNED,p,0,
16631  Communicator_pt->mpi_comm(),MPI_STATUS_IGNORE);
16632  }
16633 
16634  double t_end = 0.0;
16636  {
16637  t_end=TimingHelpers::timer();
16638  oomph_info << "Time for send and receive stuff: "
16639  << t_end-t_start << std::endl;
16640  t_start = TimingHelpers::timer();
16641  }
16642 
16643  // determine the number of equation on processors with rank
16644  // less than my_rank
16645  unsigned my_eqn_num_base = 0;
16646  for (unsigned p = 0; p < my_rank; p++)
16647  {
16648  my_eqn_num_base += n_eqn_on_proc[p];
16649 // if (n_eqn_on_proc[p]!=n_eqn[p])
16650 // {
16651 // std::cout << "proc " << my_rank << "clash in eqn numbers: "
16652 // << p << " " << n_eqn_on_proc[p] << " " << n_eqn[p]
16653 // << std::endl;
16654 // }
16655  }
16656 
16657  // Loop over all internal data (on elements) and bump up their
16658  // equation numbers if they exist
16659  unsigned nelem=mesh_pt()->nelement();
16660  for (unsigned e=0;e<nelem;e++)
16661  {
16663 
16664  unsigned nintern_data=el_pt->ninternal_data();
16665  for (unsigned iintern=0;iintern<nintern_data;iintern++)
16666  {
16667  Data* int_data_pt=el_pt->internal_data_pt(iintern);
16668  unsigned nval=int_data_pt->nvalue();
16669  for (unsigned ival=0;ival<nval;ival++)
16670  {
16671  int old_eqn_number=int_data_pt->eqn_number(ival);
16672  if (old_eqn_number>=0) // i.e. it's being used
16673  {
16674  // Bump up eqn number
16675  int new_eqn_number=old_eqn_number+my_eqn_num_base;
16676  int_data_pt->eqn_number(ival)=new_eqn_number;
16677  }
16678  }
16679  }
16680  }
16681 
16682  // Loop over all nodes on current processor and bump up their
16683  // equation numbers if they're not pinned!
16684  unsigned nnod=mesh_pt()->nnode();
16685  for (unsigned j=0;j<nnod;j++)
16686  {
16687  Node* nod_pt=mesh_pt()->node_pt(j);
16688 
16689  // loop over ALL eqn numbers - variable number of values
16690  unsigned nval=nod_pt->nvalue();
16691 
16692  for (unsigned ival=0;ival<nval;ival++)
16693  {
16694  int old_eqn_number=nod_pt->eqn_number(ival);
16695  // Include all eqn numbers
16696  if (old_eqn_number>=0)
16697  {
16698  // Bump up eqn number
16699  int new_eqn_number=old_eqn_number+my_eqn_num_base;
16700  nod_pt->eqn_number(ival)=new_eqn_number;
16701  }
16702  }
16703 
16704  // Is this a solid node? If so, need to bump up its equation number(s)
16705  SolidNode* solid_nod_pt=dynamic_cast<SolidNode*>(nod_pt);
16706 
16707  if (solid_nod_pt!=0)
16708  {
16709  // Find equation numbers
16710  unsigned nval=solid_nod_pt->variable_position_pt()->nvalue();
16711  for (unsigned ival=0;ival<nval;ival++)
16712  {
16713  int old_eqn_number=solid_nod_pt->variable_position_pt()
16714  ->eqn_number(ival);
16715  // include all eqn numbers
16716 
16717  if (old_eqn_number>=0)
16718  {
16719  // Bump up eqn number
16720  int new_eqn_number=old_eqn_number+my_eqn_num_base;
16721  solid_nod_pt->variable_position_pt()->eqn_number(ival)=new_eqn_number;
16722  }
16723  }
16724  }
16725  }
16726 
16728  {
16729  t_end = TimingHelpers::timer();
16730  oomph_info << "Time for bumping: "
16731  << t_end-t_start << std::endl;
16732  t_start = TimingHelpers::timer();
16733  }
16734 
16735 
16736  // Now copy the haloed eqn numbers across
16737  // This has to include the internal data equation numbers as well
16738  // as the solid node equation numbers
16739  bool do_halos=true;
16740  bool do_external_halos=false;
16741  copy_haloed_eqn_numbers_helper(do_halos,do_external_halos);
16742 
16744  {
16745  t_end = TimingHelpers::timer();
16746  oomph_info << "Time for copy_haloed_eqn_numbers_helper for halos: "
16747  << t_end-t_start << std::endl;
16748  t_start = TimingHelpers::timer();
16749  }
16750 
16751  // Now do external halo stuff
16752  do_halos=false;
16753  do_external_halos=true;
16754  copy_haloed_eqn_numbers_helper(do_halos,do_external_halos);
16755 
16757  {
16758  t_end = TimingHelpers::timer();
16759  oomph_info << "Time for copy_haloed_eqn_numbers_helper for external halos: "
16760  << t_end-t_start << std::endl;
16761  t_start = TimingHelpers::timer();
16762  }
16763 
16764  // Now the global equation numbers have been updated.
16765  //---------------------------------------------------
16766  // Setup the local equation numbers again.
16767  //----------------------------------------
16768  if (assign_local_eqn_numbers)
16769  {
16770  // Loop over the submeshes: Note we need to call the submeshes' own
16771  // assign_*_eqn_number() otherwise we miss additional functionality
16772  // that is implemented (e.g.) in SolidMeshes!
16773  unsigned n_sub_mesh=nsub_mesh();
16774  if (n_sub_mesh==0)
16775  {
16777  }
16778  else
16779  {
16780  for (unsigned i=0;i<n_sub_mesh;i++)
16781  {
16783  }
16784  }
16785  }
16786 
16788  {
16789  t_end = TimingHelpers::timer();
16790  oomph_info << "Time for assign_local_eqn_numbers in sync: "
16791  << t_end-t_start << std::endl;
16792  t_start = TimingHelpers::timer();
16793  }
16794 
16795  // wait for the sends to complete
16796  if (n_send > 0)
16797  {
16798  Vector<MPI_Status> send_status(n_send);
16799  MPI_Waitall(n_send,&send_req[0],&send_status[0]);
16800  }
16801 
16803  {
16804  t_end = TimingHelpers::timer();
16805  oomph_info << "Time for waitall: "
16806  << t_end-t_start << std::endl;
16807  t_start = TimingHelpers::timer();
16808  }
16809 
16810  // build the Dof distribution pt
16811  Dof_distribution_pt->build(Communicator_pt,my_eqn_num_base,
16812  my_n_eqn);
16813 
16814  // and return the total number of equations in the problem
16815  return (long)Dof_distribution_pt->nrow();
16816 }
16817 
16818 
16819 //=======================================================================
16820 /// A private helper function to
16821 /// copy the haloed equation numbers into the halo equation numbers,
16822 /// either for the problem's one and only mesh or for all of its
16823 /// submeshes. Bools control if we deal with data associated with
16824  /// external halo/ed elements/nodes or the "normal" halo/ed ones.
16825 //===================================================================
16827  const bool& do_external_halos)
16828 {
16829  // Do we have submeshes?
16830  unsigned n_mesh_loop=1;
16831  unsigned nmesh=nsub_mesh();
16832  if (nmesh>0)
16833  {
16834  n_mesh_loop=nmesh;
16835  }
16836 
16837  // Storage for number of processors and current processor
16838  int n_proc=this->communicator_pt()->nproc();
16839 
16840  //If only one processor then return
16841  if(n_proc==1) {return;}
16842  int my_rank=this->communicator_pt()->my_rank();
16843 
16844  // Storage for number of data to be sent to each processor
16845  Vector<int> send_n(n_proc,0);
16846  // Storage for all equation numbers to be sent to all processors
16847  Vector<long> send_data;
16848  // Start location within send_data for data to be sent to each processor
16849  Vector<int> send_displacement(n_proc,0);
16850 
16851 
16852  // Loop over all processors whose eqn numbers are to be updated
16853  for (int rank=0;rank<n_proc;rank++)
16854  {
16855  //Set the displacement of the current processor in the loop
16856  send_displacement[rank] = send_data.size();
16857 
16858  // If I'm not the processor whose halo eqn numbers are updated,
16859  // some of my nodes may be haloed: Stick their
16860  // eqn numbers into the vector
16861  if (rank!=my_rank)
16862  {
16863  // Deal with sub-meshes one-by-one if required
16864  Mesh* my_mesh_pt=0;
16865 
16866  // Loop over submeshes
16867  for (unsigned imesh=0;imesh<n_mesh_loop;imesh++)
16868  {
16869  if (nmesh==0)
16870  {
16871  my_mesh_pt=mesh_pt();
16872  }
16873  else
16874  {
16875  my_mesh_pt=mesh_pt(imesh);
16876  }
16877 
16878  if (do_halos)
16879  {
16880  //Add equation numbers for each haloed node
16881  unsigned n_nod=my_mesh_pt->nhaloed_node(rank);
16882  for (unsigned n=0;n<n_nod;n++)
16883  {
16884  my_mesh_pt->haloed_node_pt(rank,n)->
16885  add_eqn_numbers_to_vector(send_data);
16886  }
16887 
16888  //Add the equation numbers associated with internal data
16889  //in the haloed elements
16891  haloed_elem_pt=my_mesh_pt->haloed_element_pt(rank);
16892  unsigned nelem_haloed=haloed_elem_pt.size();
16893  for (unsigned e=0; e<nelem_haloed; e++)
16894  {
16895  haloed_elem_pt[e]->add_internal_eqn_numbers_to_vector(send_data);
16896  }
16897  }
16898 
16899  if (do_external_halos)
16900  {
16901  //Add equation numbers associated with external haloed nodes
16902  unsigned n_ext_nod=my_mesh_pt->nexternal_haloed_node(rank);
16903  for (unsigned n=0;n<n_ext_nod;n++)
16904  {
16905  my_mesh_pt->external_haloed_node_pt(rank,n)->
16906  add_eqn_numbers_to_vector(send_data);
16907  }
16908 
16909  //Add the equation numbers associated with internal data in
16910  //each external haloed element
16911  unsigned next_elem_haloed=my_mesh_pt->nexternal_haloed_element(rank);
16912  for (unsigned e=0; e<next_elem_haloed; e++)
16913  {
16914  // how many internal data values for this element?
16915  my_mesh_pt->external_haloed_element_pt(rank,e)->
16916  add_internal_eqn_numbers_to_vector(send_data);
16917  }
16918  }
16919 
16920  } // end of loop over meshes
16921  }
16922 
16923  //Find the number of data added to the vector by this processor
16924  send_n[rank] = send_data.size() - send_displacement[rank];
16925  }
16926 
16927  //Storage for the number of data to be received from each processor
16928  Vector<int> receive_n(n_proc,0);
16929 
16930  //Communicate all numbers of data to be sent between all processors
16931  MPI_Alltoall(&send_n[0],1,MPI_INT,&receive_n[0],1,MPI_INT,
16932  this->communicator_pt()->mpi_comm());
16933 
16934  //We now prepare the data to be received
16935  //by working out the displacements from the received data
16936  Vector<int> receive_displacement(n_proc,0);
16937  int receive_data_count=0;
16938  for(int rank=0;rank<n_proc;++rank)
16939  {
16940  //Displacement is number of data received so far
16941  receive_displacement[rank] = receive_data_count;
16942  receive_data_count += receive_n[rank];
16943  }
16944 
16945  //Now resize the receive buffer
16946  //Make sure that it has a size of at least one
16947  if(receive_data_count==0) {++receive_data_count;}
16948  Vector<long> receive_data(receive_data_count);
16949 
16950  //Make sure that the send buffer has size at least one
16951  //so that we don't get a segmentation fault
16952  if(send_data.size()==0) {send_data.resize(1);}
16953 
16954  //Now send the data between all the processors
16955  MPI_Alltoallv(&send_data[0],&send_n[0],&send_displacement[0],
16956  MPI_LONG,
16957  &receive_data[0],&receive_n[0],
16958  &receive_displacement[0],
16959  MPI_LONG,
16960  this->communicator_pt()->mpi_comm());
16961 
16962 
16963  // Loop over all other processors to receive their
16964  // eqn numbers
16965  for (int send_rank=0;send_rank<n_proc;send_rank++)
16966  {
16967  // Don't do anything for the processor corresponding to the
16968  // current processor or if no data were received from this processor
16969  if((send_rank!=my_rank) && (receive_n[send_rank] != 0))
16970  {
16971  //Counter for the data within the large array
16972  unsigned count = receive_displacement[send_rank];
16973 
16974  // Deal with sub-meshes one-by-one if required
16975  Mesh* my_mesh_pt=0;
16976 
16977  // Loop over submeshes
16978  for (unsigned imesh=0;imesh<n_mesh_loop;imesh++)
16979  {
16980  if (nmesh==0)
16981  {
16982  my_mesh_pt=mesh_pt();
16983  }
16984  else
16985  {
16986  my_mesh_pt=mesh_pt(imesh);
16987  }
16988 
16989  if (do_halos)
16990  {
16991  // How many of my nodes are halos whose non-halo counter
16992  // parts live on processor send_rank?
16993  unsigned n_nod=my_mesh_pt->nhalo_node(send_rank);
16994  for (unsigned n=0;n<n_nod;n++)
16995  {
16996  // Generalise to variable number of values per node
16997  my_mesh_pt->halo_node_pt(send_rank,n)->
16998  read_eqn_numbers_from_vector(receive_data,count);
16999  }
17000 
17001  // Get number of halo elements whose non-halo is on
17002  //process send_rank
17003  Vector<GeneralisedElement*> halo_elem_pt=my_mesh_pt->
17004  halo_element_pt(send_rank);
17005  unsigned nelem_halo=halo_elem_pt.size();
17006  for (unsigned e=0;e<nelem_halo;e++)
17007  {
17008  halo_elem_pt[e]->
17009  read_internal_eqn_numbers_from_vector(receive_data,count);
17010  }
17011  }
17012 
17013  if (do_external_halos)
17014  {
17015  // How many of my nodes are external halos whose external non-halo
17016  // counterparts live on processor send_rank?
17017  unsigned n_ext_nod=my_mesh_pt->nexternal_halo_node(send_rank);
17018  for (unsigned n=0;n<n_ext_nod;n++)
17019  {
17020  my_mesh_pt->external_halo_node_pt(send_rank,n)->
17021  read_eqn_numbers_from_vector(receive_data,count);
17022  }
17023 
17024  // Get number of external halo elements whose external haloed
17025  // counterpart is on process send_rank
17026  unsigned next_elem_halo=my_mesh_pt->nexternal_halo_element(send_rank);
17027  for (unsigned e=0;e<next_elem_halo;e++)
17028  {
17029  my_mesh_pt->external_halo_element_pt(send_rank,e)->
17030  read_internal_eqn_numbers_from_vector(receive_data,count);
17031  }
17032  }
17033 
17034  } // end of loop over meshes
17035  }
17036  } //End of loop over processors
17037 }
17038 
17039 //==========================================================================
17040 /// Balance the load of a (possibly non-uniformly refined) problem that has
17041 /// already been distributed, by re-distributing elements over the processors.
17042 /// Produce explicit stats of load balancing process if boolean, report_stats,
17043 /// is set to true and doc various bits of data (mainly for debugging)
17044 /// in directory specified by DocInfo object.
17045 //==========================================================================
17047  const bool& report_stats,
17048  const Vector<unsigned>&
17049  input_target_domain_for_local_non_halo_element)
17050 {
17051  double start_t = TimingHelpers::timer();
17052 
17053  // Number of processes
17054  const unsigned n_proc = this->communicator_pt()->nproc();
17055 
17056  // Don't do anything if this is a single-process job
17057  if (n_proc==1)
17058  {
17059  if (report_stats)
17060  {
17061  std::ostringstream warn_message;
17062  warn_message << "WARNING: You've tried to load balance a problem over\n"
17063  << "only one processor: ignoring your request.\n";
17064  OomphLibWarning(warn_message.str(),
17065  "Problem::load_balance()",
17066  OOMPH_EXCEPTION_LOCATION);
17067  }
17068  }
17069  // Multiple processors
17070  else
17071  {
17072  // This will only work if the problem has already been distributed
17074  {
17075  // Throw an error
17076  std::ostringstream error_stream;
17077  error_stream << "You have called Problem::load_balance()\n"
17078  << "on a non-distributed problem. This doesn't\n"
17079  << "make sense -- go distribute your problem first."
17080  << std::endl;
17081  throw OomphLibError(error_stream.str(),
17082  OOMPH_CURRENT_FUNCTION,
17083  OOMPH_EXCEPTION_LOCATION);
17084  }
17085 
17086  // Timings
17087  double t_start=0.0; double t_metis=0.0; double t_partition=0.0;
17088  double t_distribute=0.0; double t_refine=0.0; double t_copy_solution=0.0;
17089 
17090  if (report_stats)
17091  {
17092  t_start=TimingHelpers::timer();
17093  }
17094 
17095 
17096 #ifdef PARANOID
17097  unsigned old_ndof=ndof();
17098 #endif
17099 
17100  // Store pointers to the old mesh(es) so we retain a handle
17101  //---------------------------------------------------------
17102  // to them for deletion
17103  //---------------------
17104  Vector<Mesh*> old_mesh_pt;
17105  unsigned n_mesh=nsub_mesh();
17106  if (n_mesh==0)
17107  {
17108  // Resize the container
17109  old_mesh_pt.resize(1);
17110  old_mesh_pt[0]=mesh_pt();
17111  }
17112  else
17113  {
17114  // Resize the container
17115  old_mesh_pt.resize(n_mesh);
17116  for (unsigned i_mesh=0;i_mesh<n_mesh;i_mesh++)
17117  {
17118  old_mesh_pt[i_mesh]=mesh_pt(i_mesh);
17119  }
17120  }
17121 
17122 
17123  // Partition the global mesh in its current state
17124  //-----------------------------------------------
17125 
17126  // target_domain_for_local_non_halo_element[e] contains the number
17127  // of the domain [0,1,...,nproc-1] to which non-halo element e on THE
17128  // CURRENT PROCESSOR ONLY has been assigned. The order of the non-halo
17129  // elements is the same as in the Problem's mesh, with the halo
17130  // elements being skipped.
17131  Vector<unsigned> target_domain_for_local_non_halo_element;
17132 
17133  // Do any of the processors want to go through externally imposed
17134  // partitioning? If so, we'd better do it here too (even if the processor
17135  // is empty, e.g. following a restart on a larger number of procs) or
17136  // we hang.
17137  unsigned local_ntarget=
17138  input_target_domain_for_local_non_halo_element.size();
17139  unsigned global_ntarget=0;
17140  MPI_Allreduce(&local_ntarget,&global_ntarget,1,
17141  MPI_UNSIGNED,MPI_MAX,
17142  Communicator_pt->mpi_comm());
17143 
17144  // External prescribed partitioning
17145  if (global_ntarget>0)
17146  {
17147  target_domain_for_local_non_halo_element=
17148  input_target_domain_for_local_non_halo_element;
17149  }
17150  else
17151  {
17152  // Metis does not always produce repeatable results which is
17153  // a disaster for validation runs -- this bypasses metis and
17154  // comes up with a stupid but repeatable partioning.
17156  {
17157  // Bypass METIS to perform the partitioning
17158  unsigned objective=0;
17159  bool bypass_metis=true;
17161  this,objective,
17162  target_domain_for_local_non_halo_element,
17163  bypass_metis);
17164  }
17165  else
17166  {
17167  // Use METIS to perform the partitioning
17168  unsigned objective=0;
17170  this,objective,
17171  target_domain_for_local_non_halo_element);
17172  }
17173  }
17174 
17175  if (report_stats)
17176  {
17177  t_metis=TimingHelpers::timer();
17178  }
17179 
17180  // Setup map linking element with target domain
17181  std::map<GeneralisedElement*,unsigned>
17182  target_domain_for_local_non_halo_element_map;
17183  unsigned n_elem=mesh_pt()->nelement();
17184  unsigned count_non_halo_el=0;
17185  for (unsigned e=0;e<n_elem;e++)
17186  {
17188  if (!el_pt->is_halo())
17189  {
17190  target_domain_for_local_non_halo_element_map[el_pt]=
17191  target_domain_for_local_non_halo_element[count_non_halo_el];
17192  count_non_halo_el++;
17193  }
17194  }
17195 
17196  // Load balancing is equivalent to distribution so call the
17197  // appropriate "actions before". NOTE: This acts on the
17198  // current, refined, distributed, etc. problem object
17199  // before it's being wiped. This step is therefore not
17200  // a duplicate of the call below, which acts on the
17201  // new, not-yet refined, distributed etc. problem!
17203 
17204  // Re-setup target domains for remaining elements (FaceElements
17205  // are likely to have been stripped out in actions_before_distribute()
17206  n_elem=mesh_pt()->nelement();
17207  target_domain_for_local_non_halo_element.clear();
17208  target_domain_for_local_non_halo_element.reserve(n_elem);
17209  count_non_halo_el=0;
17210  for (unsigned e = 0; e < n_elem; e++)
17211  {
17213  if (!el_pt->is_halo())
17214  {
17215  target_domain_for_local_non_halo_element.push_back(
17216  target_domain_for_local_non_halo_element_map[el_pt]);
17217  }
17218  } // for (e < n_elem)
17219 
17220  // Re-setup the number of sub-meshes since some of them may have
17221  // been stripped out in actions_before_distribute(), but save the
17222  // number of old sub-meshes
17223  const unsigned n_old_sub_meshes=n_mesh;
17224  n_mesh=nsub_mesh();
17225 
17226  // Now get the target domains for each of the submeshes, we only
17227  // get the target domains for the nonhalo elements
17229  target_domain_for_local_non_halo_element_submesh(n_mesh);
17230  // If we have no sub-meshes then we do not need to copy the target areas
17231  // of the submeshes
17232  if (n_mesh != 0)
17233  {
17234  // Counter to copy the target domains from the global vector
17235  unsigned count_td = 0;
17236  for (unsigned i_mesh = 0; i_mesh < n_mesh; i_mesh++)
17237  {
17238  // Get the number of elements (considering halo elements)
17239  const unsigned nsub_ele = mesh_pt(i_mesh)->nelement();
17240  // Now copy that number of data from the global target domains
17241  for (unsigned i = 0; i < nsub_ele; i++)
17242  {
17243  // Get the element
17244  GeneralisedElement* ele_pt = mesh_pt(i_mesh)->element_pt(i);
17245  // ... and check if it is a nonhalo element
17246  if (!ele_pt->is_halo())
17247  {
17248  // Get the target domain for the current element
17249  const unsigned target_domain =
17250  target_domain_for_local_non_halo_element[count_td++];
17251  // Add the target domain for the nonhalo element in the
17252  // submesh
17253  target_domain_for_local_non_halo_element_submesh[i_mesh].
17254  push_back(target_domain);
17255  } // if (!ele_pt->is_halo())
17256  } // for (i < nsub_ele)
17257  } // for (imesh < n_mesh)
17258 
17259 #ifdef PARANOID
17260  // Check that the total number of copied data be the same as the
17261  // total number of nonhalo elements in the (sub)-mesh(es)
17262  const unsigned ntarget_domain =
17263  target_domain_for_local_non_halo_element.size();
17264  if (count_td != ntarget_domain)
17265  {
17266  std::ostringstream error_stream;
17267  error_stream
17268  << "The number of nonhalo elements ("<<count_td<<") found in (all)\n"
17269  << "the (sub)-mesh(es) is different from the number of target domains\n"
17270  << "(" << ntarget_domain << ") for the nonhalo elements.\n"
17271  << "Please ensure that you called the rebuild_global_mesh() method "
17272  << "after the\npossible deletion of FaceElements in "
17273  << "actions_before_distribute()!!!\n\n";
17274  throw OomphLibError(error_stream.str(),"Problem::load_balance()",
17275  OOMPH_EXCEPTION_LOCATION);
17276  } // if (count_td != ntarget_domain)
17277 #endif
17278 
17279  } // if (n_mesh != 0)
17280 
17281  // Check if we have different type of submeshes (unstructured
17282  // and/or structured). Identify to which type each submesh belongs.
17283  // If we have only one mesh then identify to which type that mesh
17284  // belongs.
17285 
17286  // The load balancing strategy acts in the structured meshes and
17287  // then acts in the unstructured meshes
17288 
17289  // Vector to temporaly store pointers to unstructured meshes
17290  // (TriangleMeshBase)
17291  Vector<TriangleMeshBase*> unstructured_mesh_pt;
17292  std::vector<bool> is_unstructured_mesh;
17293 
17294  // Flag to indicate that there are unstructured meshes as part of
17295  // the problem
17296  bool are_there_unstructured_meshes = false;
17297 
17298  // We have only one mesh
17299  if (n_mesh == 0)
17300  {
17301  // Check if it is a TriangleMeshBase mesh
17302  if (TriangleMeshBase* tri_mesh_pt =
17303  dynamic_cast<TriangleMeshBase*>(old_mesh_pt[0]))
17304  {
17305  // Add the pointer to the unstructured meshes container
17306  unstructured_mesh_pt.push_back(tri_mesh_pt);
17307  // Indicate that it is an unstructured mesh
17308  is_unstructured_mesh.push_back(true);
17309  // Indicate that there are unstructured meshes as part of the
17310  // problem
17311  are_there_unstructured_meshes = true;
17312  }
17313  else
17314  {
17315  // Add the pointer to the unstructured meshes container (null
17316  // pointer)
17317  unstructured_mesh_pt.push_back(tri_mesh_pt);
17318  // Indicate that it is not an unstructured mesh
17319  is_unstructured_mesh.push_back(false);
17320  }
17321  } // if (n_mesh == 0)
17322  else // We have sub-meshes
17323  {
17324  // Check which sub-meshes are unstructured meshes (work with the
17325  // old sub-meshes number)
17326  for (unsigned i_mesh = 0; i_mesh < n_mesh; i_mesh++)
17327  {
17328  // Is it a TriangleMeshBase mesh
17329  if (TriangleMeshBase* tri_mesh_pt =
17330  dynamic_cast<TriangleMeshBase*>(old_mesh_pt[i_mesh]))
17331  {
17332  // Add the pointer to the unstructured meshes container
17333  unstructured_mesh_pt.push_back(tri_mesh_pt);
17334  // Indicate that it is an unstructured mesh
17335  is_unstructured_mesh.push_back(true);
17336  // Indicate that there are unstructured meshes as part of the
17337  // problem
17338  are_there_unstructured_meshes = true;
17339  }
17340  else
17341  {
17342  // Add the pointer to the unstructured meshes container (null
17343  // pointer)
17344  unstructured_mesh_pt.push_back(tri_mesh_pt);
17345  // Indicate that it is not an unstructured mesh
17346  is_unstructured_mesh.push_back(false);
17347  }
17348  } // for (i_mesh < n_mesh)
17349  } // else if (n_mesh == 0) // We have sub-meshes
17350 
17351  // Extract data to be sent to various processors after the
17352  //--------------------------------------------------------
17353  // problem has been rebuilt/re-distributed
17354  //----------------------------------------
17355 
17356  // Storage for number of data to be sent to each processor
17357  Vector<int> send_n(n_proc,0);
17358 
17359  // Storage for all values to be sent to all processors
17360  Vector<double> send_data;
17361 
17362  // Start location within send_data for data to be sent to each processor
17363  Vector<int> send_displacement(n_proc,0);
17364 
17365  // Old and new domains for each base element (available for all, for
17366  // convenience)
17367  Vector<unsigned> old_domain_for_base_element;
17368  Vector<unsigned> new_domain_for_base_element;
17369 
17370  // Flat-packed refinement info, labeled by id of locally
17371  // available root elements
17372  std::map<unsigned, Vector<unsigned> >
17373  flat_packed_refinement_info_for_root;
17374 
17375  // Max. level of refinement
17376  unsigned max_refinement_level_overall=0;
17377 
17378  // Prepare the input for the get_data...() method, only copy the
17379  // data from the structured meshes, TreeBaseMesh meshes
17381  target_domain_for_local_non_halo_element_in_structured_mesh;
17382  if (n_mesh == 0)
17383  {
17384  // Check if the mesh is an structured mesh
17385  if (!is_unstructured_mesh[0])
17386  {
17387  const unsigned nele_mesh =
17388  target_domain_for_local_non_halo_element.size();
17389  for (unsigned e = 0; e < nele_mesh; e++)
17390  {
17391  const unsigned target_domain =
17392  target_domain_for_local_non_halo_element[e];
17393  target_domain_for_local_non_halo_element_in_structured_mesh.
17394  push_back(target_domain);
17395  } // for (e < nele_mesh)
17396  } // if (!is_unstructured_mesh[0])
17397  } // if (n_mesh == 0)
17398  else
17399  {
17400  // Copy the target domains from the structured meshes only
17401  for (unsigned i_mesh = 0; i_mesh < n_mesh; i_mesh++)
17402  {
17403  // Check if the mesh is an structured mesh
17404  if (!is_unstructured_mesh[i_mesh])
17405  {
17406  const unsigned nele_sub_mesh =
17407  target_domain_for_local_non_halo_element_submesh[i_mesh].size();
17408  for (unsigned e = 0; e < nele_sub_mesh; e++)
17409  {
17410  const unsigned target_domain =
17411  target_domain_for_local_non_halo_element_submesh[i_mesh][e];
17412  target_domain_for_local_non_halo_element_in_structured_mesh.
17413  push_back(target_domain);
17414  } // for (e < nele_sub_mesh)
17415  } // if (!is_triangle_mesh_base[i_mesh])
17416  } // for (i_mesh < n_mesh)
17417  } // else if (n_mesh == 0)
17418 
17419  // Extract data from current problem
17420  // sorted into data to be sent to various processors after
17421  // rebuilding the meshes in a load-balanced form
17423  (target_domain_for_local_non_halo_element_in_structured_mesh,
17424  send_n,
17425  send_data,
17426  send_displacement,
17427  old_domain_for_base_element,
17428  new_domain_for_base_element,
17429  max_refinement_level_overall);
17430 
17431  // Extract flat-packed refinement pattern
17433  old_domain_for_base_element,
17434  new_domain_for_base_element,
17435  max_refinement_level_overall,
17436  flat_packed_refinement_info_for_root);
17437 
17438  if (report_stats)
17439  {
17440  t_partition=TimingHelpers::timer();
17441  oomph_info << "CPU for partition calculation for roots: "
17442  << t_partition-t_metis << std::endl;
17443  }
17444 
17445 
17446  // Flush and delete old submeshes and null the global mesh
17447  //--------------------------------------------------------
17448  // and rebuild the new (not yet distributed, refined etc.) mesh
17449  //-------------------------------------------------------------
17450  // that will be distributed in the new, improved way determined
17451  //-------------------------------------------------------------
17452  // by METIS
17453  //---------
17454  Vector<unsigned> pruned_refinement_level(std::max(int(n_old_sub_meshes),1));
17455  if (n_mesh==0)
17456  {
17457  pruned_refinement_level[0]=0;
17458  TreeBasedRefineableMeshBase* ref_mesh_pt=
17459  dynamic_cast<TreeBasedRefineableMeshBase*>(old_mesh_pt[0]);
17460  if (ref_mesh_pt!=0)
17461  {
17462  pruned_refinement_level[0]=
17463  ref_mesh_pt->uniform_refinement_level_when_pruned();
17464  }
17465 
17466  // If the mesh is an unstructured mesh (TriangleMeshBase mesh)
17467  // then we should not delete it since the load balance strategy
17468  // requires the mesh
17469 
17470  // Delete the mesh if it is not an unstructured mesh
17471  if (!is_unstructured_mesh[0])
17472  {
17473  delete old_mesh_pt[0];
17474  old_mesh_pt[0] = 0;
17475  } // if (!is_unstructured_mesh[0])
17476  } // if (n_mesh==0)
17477  else
17478  {
17479  // Loop over the number of old meshes (required to delete the
17480  // pointers of structured meshes in the old_mesh_pt structure)
17481  for (unsigned i_mesh=0;i_mesh<n_old_sub_meshes;i_mesh++)
17482  {
17483  pruned_refinement_level[i_mesh]=0;
17484  TreeBasedRefineableMeshBase* ref_mesh_pt=
17485  dynamic_cast<TreeBasedRefineableMeshBase*>(old_mesh_pt[i_mesh]);
17486  if (ref_mesh_pt!=0)
17487  {
17488  pruned_refinement_level[i_mesh]=
17489  ref_mesh_pt->uniform_refinement_level_when_pruned();
17490  }
17491 
17492  // If the mesh is an unstructured mesh (TriangleMeshBase mesh)
17493  // then we should NOT delete it since the load balance strategy
17494  // requires the mesh
17495 
17496  // Delete the mesh if it is not an unstructured mesh
17497  if (!is_unstructured_mesh[i_mesh])
17498  {
17499  delete old_mesh_pt[i_mesh];
17500  old_mesh_pt[i_mesh] = 0;
17501  } // if (!is_unstructured_mesh[i_mesh])
17502 
17503  } // for (i_mesh<n_mesh)
17504 
17505  // Empty storage for sub-meshes
17506  flush_sub_meshes();
17507 
17508  // Flush the storage for nodes and elements in compound mesh
17509  // (they've already been deleted in the sub-meshes)
17511 
17512  // Kill
17513  delete mesh_pt();
17514  mesh_pt()=0;
17515  } // else if (n_mesh==0)
17516 
17517  bool some_mesh_has_been_pruned=false;
17518  unsigned n=pruned_refinement_level.size();
17519  for (unsigned i=0;i<n;i++)
17520  {
17521  if (pruned_refinement_level[i]>0) some_mesh_has_been_pruned=true;
17522  }
17523 
17524  // (Re-)build the new mesh(es) -- this must get the problem into the
17525  // state it was in when it was first distributed!
17526  build_mesh();
17527 
17528  // Has one of the meshes been pruned; if so refine to the
17529  // common refinement level
17530  if (some_mesh_has_been_pruned)
17531  {
17532  // Do actions before adapt
17534 
17535  // Re-assign number of submeshes -- when this was first
17536  // set, the problem may have had face meshes that have now
17537  // disappeared.
17538  n_mesh=nsub_mesh();
17539 
17540  // Now adapt meshes manually
17541  if (n_mesh==0)
17542  {
17543  TreeBasedRefineableMeshBase* ref_mesh_pt=
17544  dynamic_cast<TreeBasedRefineableMeshBase*>(mesh_pt());
17545  if (ref_mesh_pt!=0)
17546  {
17547 
17548  // Get min and max refinement level
17549  unsigned local_min_ref=0;
17550  unsigned local_max_ref=0;
17551  ref_mesh_pt->get_refinement_levels(local_min_ref,local_max_ref);
17552 
17553  // Reconcile between processors: If (e.g. following
17554  // distribution/pruning) the mesh has no elements on this
17555  // processor) then ignore its contribution to the poll of
17556  // max/min refinement levels
17557  int int_local_min_ref=local_min_ref;
17558  if (ref_mesh_pt->nelement()==0)
17559  {
17560  int_local_min_ref=INT_MAX;
17561  }
17562  int int_min_ref=0;
17563  MPI_Allreduce(&int_local_min_ref,&int_min_ref,1,
17564  MPI_INT,MPI_MIN,
17565  Communicator_pt->mpi_comm());
17566 
17567  // Overall min refinement level over all meshes
17568  unsigned min_ref=unsigned(int_min_ref);
17569 
17570  // Refine as many times as required to get refinement up to
17571  // uniform refinement level after last prune
17572  unsigned nref=pruned_refinement_level[0]-min_ref;
17573  oomph_info << "Refining one-and-only mesh uniformly "
17574  << nref << " times\n";
17575  for (unsigned i=0;i<nref;i++)
17576  {
17577  ref_mesh_pt->refine_uniformly();
17578  }
17579  }
17580  }
17581  else
17582  {
17583  for (unsigned i_mesh=0;i_mesh<n_mesh;i_mesh++)
17584  {
17585  TreeBasedRefineableMeshBase* ref_mesh_pt=
17586  dynamic_cast<TreeBasedRefineableMeshBase*>(mesh_pt(i_mesh));
17587  if (ref_mesh_pt!=0)
17588  {
17589  // Get min and max refinement level
17590  unsigned local_min_ref=0;
17591  unsigned local_max_ref=0;
17592  ref_mesh_pt->get_refinement_levels(local_min_ref,local_max_ref);
17593 
17594  // Reconcile between processors: If (e.g. following
17595  // distribution/pruning) the mesh has no elements on this
17596  // processor) then ignore its contribution to the poll of
17597  // max/min refinement levels
17598  int int_local_min_ref=local_min_ref;
17599  if (ref_mesh_pt->nelement()==0)
17600  {
17601  int_local_min_ref=INT_MAX;
17602  }
17603  int int_min_ref=0;
17604  MPI_Allreduce(&int_local_min_ref,&int_min_ref,1,
17605  MPI_INT,MPI_MIN,
17606  Communicator_pt->mpi_comm());
17607 
17608  // Overall min refinement level over all meshes
17609  unsigned min_ref=unsigned(int_min_ref);
17610 
17611  // Refine as many times as required to get refinement up to
17612  // uniform refinement level after last prune
17613  unsigned nref=pruned_refinement_level[i_mesh]-min_ref;
17614  oomph_info << "Refining sub-mesh " << i_mesh << " uniformly "
17615  << nref << " times\n";
17616  for (unsigned i=0;i<nref;i++)
17617  {
17618  ref_mesh_pt->refine_uniformly();
17619  }
17620  }
17621  }
17622  // Rebuild the global mesh
17624  }
17625 
17626  // Do actions after adapt
17628 
17629  // Re-assign number of submeshes -- when this was first
17630  // set, the problem may have had face meshes that have now
17631  // disappeared.
17632  n_mesh=nsub_mesh();
17633  } // if (some_mesh_has_been_pruned)
17634 
17635 
17636  // Perform any actions before distribution but now for the new mesh
17637  // NOTE: This does NOT replicate the actions_before_distribute()
17638  // call made above for the previous mesh!
17640 
17641  // Do some book-keeping
17642  //---------------------
17643 
17644  // Re-assign number of submeshes -- when this was first
17645  // set, the problem may have had face meshes that have now
17646  // disappeared.
17647  n_mesh=nsub_mesh();
17648 
17649  // The submeshes, if they exist, need to know their own element
17650  // domains.
17651  // NOTE: This vector only stores the target domains or the
17652  // element partition for structured meshes
17653  Vector<Vector<unsigned> > submesh_element_partition(n_mesh);
17654  if (n_mesh!=0)
17655  {
17656  unsigned count=0;
17657  for (unsigned i_mesh=0;i_mesh<n_mesh;i_mesh++)
17658  {
17659  // Only work with structured meshes
17660  if (!is_unstructured_mesh[i_mesh])
17661  {
17662  // Get the number of element in the mesh
17663  const unsigned nsub_ele = mesh_pt(i_mesh)->nelement();
17664  submesh_element_partition[i_mesh].resize(nsub_ele);
17665  for (unsigned e=0; e<nsub_ele; e++)
17666  {
17667  submesh_element_partition[i_mesh][e]=
17668  new_domain_for_base_element[count++];
17669  } // for (e<nsub_elem)
17670  } // if (sub_mesh_pt!=0)
17671  } // for (i_mesh<n_mesh)
17672 
17673 #ifdef PARANOID
17674  const unsigned nnew_domain_for_base_element =
17675  new_domain_for_base_element.size();
17676  if (count != nnew_domain_for_base_element)
17677  {
17678  std::ostringstream error_stream;
17679  error_stream
17680  << "The number of READ target domains for nonhalo elements\n"
17681  << " is (" << count << "), but the number of target domains for\n"
17682  << "nonhalo elements is ("<<nnew_domain_for_base_element<<")!\n";
17683  throw OomphLibError(error_stream.str(),"Problem::load_balance()",
17684  OOMPH_EXCEPTION_LOCATION);
17685  }
17686 #endif
17687 
17688  } // if (n_mesh!=0)
17689 
17690  // Setup the map between "root" element and number in global mesh
17691  // again
17692 
17693  // This map is only established for structured meshes, then we
17694  // need to check here the type of mesh
17695  if (n_mesh==0)
17696  {
17697  // Check if the only one mesh is an stuctured mesh
17698  if (!is_unstructured_mesh[0])
17699  {
17700  const unsigned n_ele = mesh_pt()->nelement();
17701  Base_mesh_element_pt.resize(n_ele);
17703  for (unsigned e=0;e<n_ele;e++)
17704  {
17707  Base_mesh_element_pt[e]=el_pt;
17708  } // for (e<n_ele)
17709  } // if (!is_triangle_mesh_base[0])
17710  } // if (n_mesh==0)
17711  else
17712  {
17713  // If we have submeshes then we only add those elements that
17714  // belong to structured meshes, but first compute the number of
17715  // total elements in the structured sub-meshes
17716  unsigned nglobal_element = 0;
17717  for (unsigned i_mesh=0;i_mesh<n_mesh;i_mesh++)
17718  {
17719  // Check if mesh is an structured mesh
17720  if (!is_unstructured_mesh[i_mesh])
17721  {
17722  nglobal_element+=mesh_pt(i_mesh)->nelement();
17723  } // if (!is_triangle_mesh_base[i_mesh])
17724  } // for (i_mesh<n_mesh)
17725 
17726  // Once computed the number of elements, then resize the
17727  // structure
17728  Base_mesh_element_pt.resize(nglobal_element);
17730  unsigned counter_base = 0;
17731  for (unsigned i_mesh=0;i_mesh<n_mesh;i_mesh++)
17732  {
17733  // Check if mesh is a structured mesh
17734  if (!is_unstructured_mesh[i_mesh])
17735  {
17736  const unsigned n_ele = mesh_pt(i_mesh)->nelement();
17737  for (unsigned e=0;e<n_ele;e++)
17738  {
17739  GeneralisedElement* el_pt=mesh_pt(i_mesh)->element_pt(e);
17740  Base_mesh_element_number_plus_one[el_pt]=counter_base+1;
17741  Base_mesh_element_pt[counter_base]=el_pt;
17742  // Inrease the global element number
17743  counter_base++;
17744  } // for (e<n_ele)
17745  } // if (!is_triangle_mesh_base[i_mesh])
17746  } // for (i_mesh<n_mesh)
17747 
17748 #ifdef PARANOID
17749  if (counter_base != nglobal_element)
17750  {
17751  std::ostringstream error_stream;
17752  error_stream
17753  << "The number of global elements ("<<nglobal_element
17754  <<") is not the same as the number of\nadded elements ("
17755  << counter_base <<") to the Base_mesh_element_pt data "
17756  << "structure!!!\n\n";
17757  throw OomphLibError(error_stream.str(),
17758  "Problem::load_balance()",
17759  OOMPH_EXCEPTION_LOCATION);
17760  } // if (counter_base != nglobal_element)
17761 #endif // #ifdef PARANOID
17762 
17763  } // else if (n_mesh==0)
17764 
17765  // Storage for the number of face elements in the base mesh --
17766  // element is identified by number of bulk element and face index
17767  // so we can reconstruct it if and when the FaceElements have been wiped
17768  // in actions_before_distribute().
17769  // NOTE: Not really clear (any more) why this is required. Typically
17770  // FaceElements get wiped in actions_before_distribute() so
17771  // at this point there shouldn't be any of them left.
17772  // This is certainly the case in all our currently existing
17773  // test codes. However, I'm too scared to take this out
17774  // in case it does matter (we're not insisting that FaceElements
17775  // are always removed in actions_before_distribute()...).
17776  std::map<unsigned, std::map<int,unsigned> > face_element_number;
17777  unsigned n_element=mesh_pt()->nelement();
17778  for (unsigned e=0;e<n_element;e++)
17779  {
17780  FaceElement* face_el_pt=
17781  dynamic_cast<FaceElement*>(mesh_pt()->finite_element_pt(e));
17782  if (face_el_pt!=0)
17783  {
17784 #ifdef PARANOID
17785  std::stringstream info;
17786  info << "================================================\n";
17787  info << "INFO: I've come across a FaceElement while \n";
17788  info <<" load-balancing a problem. \n";
17789  info << "================================================\n";
17790  oomph_info << info.str() << std::endl;
17791 #endif
17792  FiniteElement* bulk_elem_pt=face_el_pt->bulk_element_pt();
17793  unsigned e_bulk=Base_mesh_element_number_plus_one[bulk_elem_pt];
17794 #ifdef PARANOID
17795  if (e_bulk==0)
17796  {
17797  throw OomphLibError("Base_mesh_element_number_plus_one[...]=0",
17798  OOMPH_CURRENT_FUNCTION,
17799  OOMPH_EXCEPTION_LOCATION);
17800  }
17801 #endif
17802  e_bulk-=1;
17803  int face_index=face_el_pt->face_index();
17804  face_element_number[e_bulk][face_index]=e;
17805  }
17806  }
17807 
17808  // Distribute the (sub)meshes
17809  //---------------------------
17810  Vector<GeneralisedElement*> deleted_element_pt;
17811  if (n_mesh==0)
17812  {
17813  // Only distribute (load balance strategy) if this is an
17814  // structured mesh
17815  if (!is_unstructured_mesh[0])
17816  {
17817 #ifdef PARANOID
17818  if (mesh_pt()->nelement()!=new_domain_for_base_element.size())
17819  {
17820  std::ostringstream error_stream;
17821  error_stream << "Distributing one-and-only mesh containing "
17822  << mesh_pt()->nelement() << " elements with info for "
17823  << new_domain_for_base_element.size()
17824  << std::endl;
17825  throw OomphLibError(error_stream.str(),
17826  OOMPH_CURRENT_FUNCTION,
17827  OOMPH_EXCEPTION_LOCATION);
17828  }
17829 #endif
17830 
17831  if (report_stats)
17832  {
17833  oomph_info << "Distributing one and only mesh\n"
17834  << "------------------------------" << std::endl;
17835  }
17836 
17837  // No pre-set distribution from restart that may leave some
17838  // processors empty so no need to overrule deletion of elements
17839  bool overrule_keep_as_halo_element_status=false;
17840 
17841  mesh_pt()->distribute(this->communicator_pt(),
17842  new_domain_for_base_element,
17843  deleted_element_pt,
17844  doc_info,report_stats,
17845  overrule_keep_as_halo_element_status);
17846 
17847  } // if (!is_unstructured_mesh[0])
17848 
17849  } // if (n_mesh==0)
17850  else // There are submeshes, "distribute" each one separately
17851  {
17852 
17853  // Rebuild the mesh only if one of the meshes was modified
17854  bool need_to_rebuild_mesh = false;
17855  for (unsigned i_mesh=0; i_mesh<n_mesh; i_mesh++)
17856  {
17857  // Perform the load balancing based on distribution in the
17858  // structured meshes only
17859  if (!is_unstructured_mesh[i_mesh])
17860  {
17861 
17862  if (report_stats)
17863  {
17864  oomph_info << "Distributing submesh " << i_mesh << " of "
17865  << n_mesh << " in total\n"
17866  << "---------------------------------------------"
17867  << std::endl;
17868  }
17869 
17870  // Set the doc_info number to reflect the submesh
17871  doc_info.number()=i_mesh;
17872 
17873  // No pre-set distribution from restart that may leave some
17874  // processors empty so no need to overrule deletion of elements
17875  bool overrule_keep_as_halo_element_status=false;
17876  mesh_pt(i_mesh)->distribute(this->communicator_pt(),
17877  submesh_element_partition[i_mesh],
17878  deleted_element_pt,
17879  doc_info,report_stats,
17880  overrule_keep_as_halo_element_status);
17881 
17882  // Set the flag to rebuild the global mesh
17883  need_to_rebuild_mesh = true;
17884 
17885  } // if (!is_unstructured_mesh[i_mesh])
17886 
17887  } // for (i_mesh<n_mesh)
17888 
17889  if (need_to_rebuild_mesh)
17890  {
17891  // Rebuild the global mesh
17893  } // if (need_to_rebuild_mesh)
17894 
17895  } // else if (n_mesh==0)
17896 
17897  // Null out information associated with deleted elements
17898  unsigned n_del=deleted_element_pt.size();
17899  for (unsigned e=0;e<n_del;e++)
17900  {
17901  GeneralisedElement* el_pt=deleted_element_pt[e];
17902  unsigned old_el_number=Base_mesh_element_number_plus_one[el_pt]-1;
17904  Base_mesh_element_pt[old_el_number]=0;
17905  }
17906 
17907  // Has one of the meshes been pruned before distribution? If so
17908  // then prune here now
17909  if (some_mesh_has_been_pruned)
17910  {
17911  Vector<GeneralisedElement*> deleted_element_pt;
17912  if (n_mesh==0)
17913  {
17914  TreeBasedRefineableMeshBase* ref_mesh_pt=
17915  dynamic_cast<TreeBasedRefineableMeshBase*>(mesh_pt());
17916  if (ref_mesh_pt!=0)
17917  {
17918  ref_mesh_pt->prune_halo_elements_and_nodes
17919  (deleted_element_pt,doc_info,report_stats);
17920  }
17921  }
17922  else
17923  {
17924  for (unsigned i_mesh=0;i_mesh<n_mesh;i_mesh++)
17925  {
17926  TreeBasedRefineableMeshBase* ref_mesh_pt=
17927  dynamic_cast<TreeBasedRefineableMeshBase*>(mesh_pt(i_mesh));
17928  if (ref_mesh_pt!=0)
17929  {
17930  ref_mesh_pt->prune_halo_elements_and_nodes
17931  (deleted_element_pt,doc_info,report_stats);
17932  }
17933  }
17934  // Rebuild the global mesh
17936  }
17937 
17938  // Null out information associated with deleted elements
17939  unsigned n_del=deleted_element_pt.size();
17940  for (unsigned e=0;e<n_del;e++)
17941  {
17942  GeneralisedElement* el_pt=deleted_element_pt[e];
17943  unsigned old_el_number=Base_mesh_element_number_plus_one[el_pt]-1;
17945  Base_mesh_element_pt[old_el_number]=0;
17946  }
17947 
17948  // Setup the map between "root" element and number in global mesh again
17950 
17951  }
17952 
17953  if (report_stats)
17954  {
17955  t_distribute=TimingHelpers::timer();
17956  oomph_info << "CPU for build and distribution of new mesh(es): "
17957  << t_distribute-t_partition << std::endl;
17958  }
17959 
17960 
17961  // Send refinement info to other processors
17962  //-----------------------------------------
17963 
17964  // Storage for refinement pattern: Given ID of root element,
17965  // root_element_id, and current refinement level, level, the e-th entry in
17966  // refinement_info_for_root_elements[root_element_id][level][e] is equal
17967 
17968  // to 2 if the e-th element (using the enumeration when the mesh has been
17969  // refined to the level-th level) is to be refined during the next
17970  // refinement; it's 1 if it's not to be refined.
17971  Vector<Vector<Vector<unsigned> > > refinement_info_for_root_elements;
17972 
17973 
17974  // Send refinement information between processors, using flat-packed
17975  // information accumulated earlier
17976  send_refinement_info_helper(old_domain_for_base_element,
17977  new_domain_for_base_element,
17978  max_refinement_level_overall,
17979  flat_packed_refinement_info_for_root,
17980  refinement_info_for_root_elements);
17981 
17982  // Refine each mesh based upon refinement information stored for each root
17983  //------------------------------------------------------------------------
17984  refine_distributed_base_mesh(refinement_info_for_root_elements,
17985  max_refinement_level_overall);
17986 
17987  if (report_stats)
17988  {
17989  t_refine=TimingHelpers::timer();
17990  oomph_info << "CPU for refinement of base mesh: "
17991  << t_refine-t_distribute << std::endl;
17992  }
17993 
17994  // NOTE: The following two calls are important e.g. when
17995  // FaceElements that resize nodes are attached/detached
17996  // after/before adaptation. If we don't attach them
17997  // on the newly built/refined mesh, there isn't enough
17998  // storage for the nodal values that are sent around
17999  // (in a flat-packed format) resulting in total disaster.
18000  // So we attach them first, but then immediatly strip
18001  // them out again because the FaceElements themselves
18002  // will have been stripped out before distribution/adaptation.
18003 
18004  // Do actions after adapt because we have just adapted the mesh.
18006 
18007  // Now strip it back out to get problem into the same state
18008  // it was in when data to be sent was recorded.
18010 
18011  // Send the stored values in each root from the old mesh into the new mesh
18012  //------------------------------------------------------------------------
18014  send_displacement);
18015 
18016  // If there are unstructured meshes here we perform the load
18017  // balancing of those meshes
18018  if (are_there_unstructured_meshes)
18019  {
18020  // Delete any storage of external elements and nodes
18022 
18023  if (n_mesh == 0)
18024  {
18025  // Before doing the load balancing delete the mesh created at
18026  // calling build_mesh(), and restore the pointer to the old
18027  // mesh
18028 
18029  // It MUST be an unstructured mesh, otherwise we should not be
18030  // here
18031  if (is_unstructured_mesh[0])
18032  {
18033  // Delete the new created mesh
18034  delete mesh_pt();
18035  // Re-assign the pointer to the old mesh
18036  this->mesh_pt() = old_mesh_pt[0];
18037  } // if (is_unstructured_mesh[0])
18038 #ifdef PARANOID
18039  else
18040  {
18041  std::ostringstream error_stream;
18042  error_stream
18043  <<"The only one mesh in the problem is not an unstructured mesh,\n"
18044  <<"but the flag 'are_there_unstructures_meshes' ("
18045  <<are_there_unstructured_meshes<<") was turned on,\n"
18046  <<"this is weird. Please check for any condition that may have\n"
18047  <<"turned on this flag!!!!\n\n";
18048  throw OomphLibError(error_stream.str(),"Problem::load_balance()",
18049  OOMPH_EXCEPTION_LOCATION);
18050  }
18051 #endif
18052 
18053  unstructured_mesh_pt[0]->
18054  load_balance(target_domain_for_local_non_halo_element);
18055  } // if (n_mesh == 0)
18056  else
18057  {
18058  // Before doing the load balancing delete the meshes created
18059  // at calling build_mesh(), and restore the pointer to the
18060  // old meshes
18061  for (unsigned i_mesh = 0; i_mesh < n_mesh; i_mesh++)
18062  {
18063  if (is_unstructured_mesh[i_mesh])
18064  {
18065  // Delete the new created mesh
18066  delete mesh_pt(i_mesh);
18067  // Now point it to nothing
18068  mesh_pt(i_mesh) = 0;
18069  // ... and re-assign the pointer to the old mesh
18070  this->mesh_pt(i_mesh) = old_mesh_pt[i_mesh];
18071  } // if (is_unstructured_mesh[i_mesh])
18072 
18073  } // for (i_mesh<n_mesh)
18074 
18075  // Empty storage for sub-meshes
18076  //flush_sub_meshes();
18077 
18078  // Flush the storage for nodes and elements in compound mesh
18079  // (they've already been deleted in the sub-meshes)
18081 
18082  // Now we can procede with the load balancing thing
18083  for (unsigned i_mesh = 0; i_mesh < n_mesh; i_mesh++)
18084  {
18085  if (is_unstructured_mesh[i_mesh])
18086  {
18087  // Get the number of elements in the "i_mesh" (the old one)
18088  const unsigned n_element = old_mesh_pt[i_mesh]->nelement();
18089 
18090  // Perform the load balancing if there are elements in the
18091  // mesh. We check for this case because the meshes created
18092  // from face elements have been cleaned in
18093  // "actions_before_distribute()"
18094  if (n_element > 0 && is_unstructured_mesh[i_mesh])
18095  {
18096  unstructured_mesh_pt[i_mesh]->load_balance(
18097  target_domain_for_local_non_halo_element_submesh[i_mesh]);
18098  } // if (n_element > 0)
18099  } // if (is_unstructured_mesh[i_mesh)]
18100  } // for (i_mesh < n_mesh)
18101 
18102  // Rebuild the global mesh
18104 
18105  } // else if (n_mesh == 0)
18106 
18107  } // if (are_there_unstructured_meshes)
18108 
18109  if (report_stats)
18110  {
18111  t_copy_solution=TimingHelpers::timer();
18112  oomph_info << "CPU for transferring solution to new mesh(es): "
18113  << t_copy_solution-t_refine << std::endl;
18114  oomph_info << "CPU for load balancing: "
18115  << t_copy_solution-t_start << std::endl;
18116  }
18117 
18118  // Do actions after distribution
18120 
18121  // Re-assign equation numbers
18122 #ifdef PARANOID
18123  unsigned n_dof=assign_eqn_numbers();
18124 #else
18126 #endif
18127 
18128  if (report_stats)
18129  {
18130  oomph_info
18131  << "Total number of elements on this processor after load balance: "
18132  << mesh_pt()->nelement() << std::endl;
18133 
18134  oomph_info
18135  << "Number of non-halo elements on this processor after load balance: "
18136  << mesh_pt()->nnon_halo_element() << std::endl;
18137  }
18138 
18139 #ifdef PARANOID
18140  if (n_dof!=old_ndof)
18141  {
18142  std::ostringstream error_stream;
18143  error_stream
18144  << "Number of dofs in load_balance() has changed from "
18145  << old_ndof << " to " << n_dof << "\n"
18146  << "Check that you've implemented any necessary actions_before/after\n"
18147  << "adapt/distribute functions, e.g. to pin redundant pressure dofs"
18148  << " etc.\n";
18149  throw OomphLibError(error_stream.str(),
18150  OOMPH_CURRENT_FUNCTION,
18151  OOMPH_EXCEPTION_LOCATION);
18152  }
18153 #endif
18154  }
18155 
18156  // Finally synchronise all dofs to allow halo check to pass
18158 
18159  double end_t = TimingHelpers::timer();
18160  oomph_info << "Time for load_balance() [sec] : "
18161  << end_t-start_t << std::endl;
18162 
18163 }
18164 
18165 
18166 //==========================================================================
18167 /// Send refinement information between processors
18168 //==========================================================================
18170  Vector<unsigned>& old_domain_for_base_element,
18171  Vector<unsigned>& new_domain_for_base_element,
18172  const unsigned& max_refinement_level_overall,
18173  std::map<unsigned, Vector<unsigned> >& flat_packed_refinement_info_for_root,
18174  Vector<Vector<Vector<unsigned> > >& refinement_info_for_root_elements)
18175 {
18176  // Number of processes etc.
18177  const int n_proc=this->communicator_pt()->nproc();
18178  const int my_rank=this->communicator_pt()->my_rank();
18179 
18180  // Make space
18181  unsigned n_base_element=old_domain_for_base_element.size();
18182  refinement_info_for_root_elements.resize(n_base_element);
18183 
18184  // Make space for list of domains that the refinement info
18185  // is to be forwarded to
18186  std::map<unsigned,Vector<unsigned> > halo_domain_of_haloed_base_element;
18187 
18188  // Find out haloed elements in new, redistributed problem
18189  //-------------------------------------------------------
18190 
18191  // halo_domains[e][j] = j-th halo domain associated with (haloed) element e
18192  std::map<unsigned,Vector<unsigned> > halo_domains;
18193 
18194  // Loop over sub meshes
18195  unsigned n_sub_mesh=nsub_mesh();
18196  unsigned max_mesh=std::max(n_sub_mesh,unsigned(1));
18197  for (unsigned i_mesh=0;i_mesh<max_mesh;i_mesh++)
18198  {
18199  // Choose the right mesh
18200  Mesh* my_mesh_pt=0;
18201  if (n_sub_mesh==0)
18202  {
18203  my_mesh_pt=mesh_pt();
18204  }
18205  else
18206  {
18207  my_mesh_pt=mesh_pt(i_mesh);
18208  }
18209 
18210  // Only work with structured meshes
18211  TriangleMeshBase* sub_mesh_pt =
18212  dynamic_cast<TriangleMeshBase*>(mesh_pt(i_mesh));
18213  if (!(sub_mesh_pt!=0))
18214  {
18215  // Loop over processors to find haloed elements -- need to
18216  // send their refinement patterns processors that hold their
18217  // halo counterparts!
18218  for (int p=0;p<n_proc;p++)
18219  {
18220  Vector<GeneralisedElement*> haloed_elem_pt=
18221  my_mesh_pt->haloed_element_pt(p);
18222  unsigned nhaloed=haloed_elem_pt.size();
18223  for (unsigned h=0;h<nhaloed;h++)
18224  {
18225  // This element must send its refinement information to processor p
18226  unsigned e=Base_mesh_element_number_plus_one[haloed_elem_pt[h]];
18227 #ifdef PARANOID
18228  if (e==0)
18229  {
18230  throw OomphLibError("Base_mesh_element_number_plus_one[...]=0",
18231  OOMPH_CURRENT_FUNCTION,
18232  OOMPH_EXCEPTION_LOCATION);
18233  }
18234 #endif
18235  e-=1;
18236  halo_domains[e].push_back(p);
18237  }
18238  }
18239  } // if (!(sub_mesh_pt!=0))
18240  } // for (i_mesh<max_mesh)
18241 
18242  // Accumulate relevant flat-packed refinement data to be sent to
18243  //--------------------------------------------------------------
18244  // various processors
18245  //-------------------
18246 
18247  // Map to accumulate unsigned data to be sent to each processor
18248  // (map for sparsity)
18249  std::map<unsigned, Vector<unsigned> > data_for_proc;
18250 
18251  // Number of base elements to be sent to specified domain
18252  Vector<unsigned> nbase_elements_for_proc(n_proc,0);
18253 
18254  // Total number of entries in send vector
18255  unsigned count=0;
18256 
18257  // Loop over all base elements
18258  //----------------------------
18259  for (unsigned e=0;e<n_base_element;e++)
18260  {
18261 
18262  // Is it one of mine (i.e. was it a non-halo element on this
18263  //----------------------------------------------------------
18264  // processor before re-distribution, and do I therefore hold
18265  //----------------------------------------------------------
18266  // refinement information for it)?
18267  //--------------------------------
18268  if (int(old_domain_for_base_element[e])==my_rank)
18269  {
18270  // Where does it go?
18271  unsigned new_domain=new_domain_for_base_element[e];
18272 
18273  // Keep counting
18274  nbase_elements_for_proc[new_domain]++;
18275 
18276  // If it stays local, deal with it here
18277  if (int(new_domain)==my_rank)
18278  {
18279  // Record on which other procs/domains the refinement info for
18280  // this element is required because it's haloed.
18281  unsigned nhalo=halo_domains[e].size();
18282  halo_domain_of_haloed_base_element[e].resize(nhalo);
18283  for (unsigned j=0;j<nhalo;j++)
18284  {
18285  halo_domain_of_haloed_base_element[e][j]=halo_domains[e][j];
18286  }
18287 
18288  // Provide storage for refinement pattern
18289  refinement_info_for_root_elements[e].
18290  resize(max_refinement_level_overall);
18291 
18292 #ifdef PARANOID
18293  // Get number of additional data sent for check
18294  unsigned n_additional_data=
18295  flat_packed_refinement_info_for_root[e].size();
18296 #endif
18297 
18298  // Get number of tree nodes
18299  unsigned n_tree_nodes=flat_packed_refinement_info_for_root[e][0];
18300 
18301  // Counter for entries to be processed locally
18302  unsigned local_count=1; // (have already processed zero-th entry)
18303 
18304  // Loop over levels and number of nodes in tree
18305  for (unsigned level=0; level<max_refinement_level_overall; level++)
18306  {
18307  for (unsigned ee=0; ee<n_tree_nodes; ee++)
18308  {
18309  // Element exists at this level
18310  if (flat_packed_refinement_info_for_root[e][local_count]==1)
18311  {
18312  local_count++;
18313 
18314  // Element should be refined
18315  if (flat_packed_refinement_info_for_root[e][local_count]==1)
18316  {
18317  refinement_info_for_root_elements[e][level].push_back(2);
18318  local_count++;
18319  }
18320  // Element should not be refined
18321  else
18322  {
18323  refinement_info_for_root_elements[e][level].push_back(1);
18324  local_count++;
18325  }
18326  }
18327  // Element does not exist at this level
18328  else
18329  {
18330  refinement_info_for_root_elements[e][level].push_back(0);
18331  local_count++;
18332  }
18333  }
18334  }
18335 
18336 #ifdef PARANOID
18337  if (n_additional_data!=local_count)
18338  {
18339  std::stringstream error_message;
18340  error_message << "Number of additional data: " << n_additional_data
18341  << " doesn't match that actually send: " << local_count
18342  << std::endl;
18343  throw OomphLibError(error_message.str(),
18344  OOMPH_CURRENT_FUNCTION,
18345  OOMPH_EXCEPTION_LOCATION);
18346  }
18347 #endif
18348 
18349  }
18350  // Element in question is not one of mine so prepare for sending
18351  //--------------------------------------------------------------
18352  else
18353  {
18354  // Make space
18355  unsigned current_size=data_for_proc[new_domain].size();
18356  unsigned n_additional_data=flat_packed_refinement_info_for_root[e].
18357  size();
18358  data_for_proc[new_domain].reserve(current_size+n_additional_data+2);
18359 
18360  // Keep counting
18361  count+=n_additional_data+2;
18362 
18363  // Add base element number
18364  data_for_proc[new_domain].push_back(e);
18365 
18366 #ifdef PARANOID
18367  // Add number of flat-packed instructions to follow
18368  data_for_proc[new_domain].push_back(n_additional_data);
18369 #endif
18370 
18371  // Add flat packed refinement data
18372  for (unsigned j=0;j<n_additional_data;j++)
18373  {
18374  data_for_proc[new_domain].push_back(
18375  flat_packed_refinement_info_for_root[e][j]);
18376  }
18377  }
18378  }
18379  }
18380 
18381 
18382  // Now do the actual send/receive
18383  //-------------------------------
18384 
18385  // Storage for number of data to be sent to each processor
18386  Vector<int> send_n(n_proc,0);
18387 
18388  // Storage for all values to be sent to all processors
18389  Vector<unsigned> send_data;
18390  send_data.reserve(count);
18391 
18392  // Start location within send_data for data to be sent to each processor
18393  Vector<int> send_displacement(n_proc,0);
18394 
18395  // Loop over all processors
18396  for(int rank=0;rank<n_proc;rank++)
18397  {
18398  //Set the offset for the current processor
18399  send_displacement[rank] = send_data.size();
18400 
18401  //Don't bother to do anything if the processor in the loop is the
18402  //current processor
18403  if(rank!=my_rank)
18404  {
18405  // Record how many base elements are to be sent
18406  send_data.push_back(nbase_elements_for_proc[rank]);
18407 
18408  // Add data
18409  unsigned n_data=data_for_proc[rank].size();
18410  for (unsigned j=0;j<n_data;j++)
18411  {
18412  send_data.push_back(data_for_proc[rank][j]);
18413  }
18414  }
18415 
18416  //Find the number of data added to the vector
18417  send_n[rank] = send_data.size() - send_displacement[rank];
18418  }
18419 
18420  //Storage for the number of data to be received from each processor
18421  Vector<int> receive_n(n_proc,0);
18422 
18423  //Now send numbers of data to be sent between all processors
18424  MPI_Alltoall(&send_n[0],1,MPI_INT,&receive_n[0],1,MPI_INT,
18425  this->communicator_pt()->mpi_comm());
18426 
18427  //We now prepare the data to be received
18428  //by working out the displacements from the received data
18429  Vector<int> receive_displacement(n_proc,0);
18430  int receive_data_count=0;
18431  for(int rank=0;rank<n_proc;++rank)
18432  {
18433  //Displacement is number of data received so far
18434  receive_displacement[rank] = receive_data_count;
18435  receive_data_count += receive_n[rank];
18436  }
18437 
18438  //Now resize the receive buffer for all data from all processors
18439  //Make sure that it has a size of at least one
18440  if(receive_data_count==0) {++receive_data_count;}
18441  Vector<unsigned> receive_data(receive_data_count);
18442 
18443  //Make sure that the send buffer has size at least one
18444  //so that we don't get a segmentation fault
18445  if(send_data.size()==0) {send_data.resize(1);}
18446 
18447  //Now send the data between all the processors
18448  MPI_Alltoallv(&send_data[0],&send_n[0],&send_displacement[0],
18449  MPI_UNSIGNED,
18450  &receive_data[0],&receive_n[0],
18451  &receive_displacement[0],
18452  MPI_UNSIGNED,
18453  this->communicator_pt()->mpi_comm());
18454 
18455 
18456 
18457  //Now use the received data to update
18458  //-----------------------------------
18459  for (int send_rank=0;send_rank<n_proc;send_rank++)
18460  {
18461  //Don't bother to do anything for the processor corresponding to the
18462  //current processor or if no data were received from this processor
18463  if((send_rank != my_rank) && (receive_n[send_rank] != 0))
18464  {
18465  //Counter for the data within the large array
18466  unsigned count=receive_displacement[send_rank];
18467 
18468  // Loop over base elements
18469  unsigned nbase_element=receive_data[count];
18470  count++;
18471  for (unsigned b=0;b<nbase_element;b++)
18472  {
18473  // Get base element number
18474  unsigned base_element_number=receive_data[count];
18475  count++;
18476 
18477  // Record on which other procs/domains the refinement info for
18478  // this element is required because it's haloed.
18479  unsigned nhalo=halo_domains[base_element_number].size();
18480  halo_domain_of_haloed_base_element[base_element_number].resize(nhalo);
18481  for (unsigned j=0;j<nhalo;j++)
18482  {
18483  halo_domain_of_haloed_base_element[base_element_number][j]=
18484  halo_domains[base_element_number][j];
18485  }
18486 
18487  // Provide storage for refinement pattern
18488  refinement_info_for_root_elements[base_element_number].
18489  resize(max_refinement_level_overall);
18490 
18491  // Get number of flat-packed instructions to follow
18492  // (only used for check)
18493 #ifdef PARANOID
18494  unsigned n_additional_data=receive_data[count];
18495  count++;
18496 
18497  // Counter for number of additional data (validation only)
18498  unsigned check_count=0;
18499 #endif
18500 
18501  // Get number of tree nodes
18502  unsigned n_tree_nodes=receive_data[count];
18503  count++;
18504 
18505 #ifdef PARANOID
18506  check_count++;
18507 #endif
18508 
18509  // Loop over levels and number of nodes in tree
18510  for (unsigned level=0; level<max_refinement_level_overall; level++)
18511  {
18512  for (unsigned e=0; e<n_tree_nodes; e++)
18513  {
18514  // Element exists at this level
18515  if (receive_data[count]==1)
18516  {
18517  count++;
18518 
18519 #ifdef PARANOID
18520  check_count++;
18521 #endif
18522 
18523  // Element should be refined
18524  if (receive_data[count]==1)
18525  {
18526  refinement_info_for_root_elements[base_element_number][level].
18527  push_back(2);
18528  count++;
18529 
18530 #ifdef PARANOID
18531  check_count++;
18532 #endif
18533  }
18534  // Element should not be refined
18535  else
18536  {
18537  refinement_info_for_root_elements[base_element_number][level].
18538  push_back(1);
18539  count++;
18540 
18541 #ifdef PARANOID
18542  check_count++;
18543 #endif
18544  }
18545  }
18546  // Element does not exist at this level
18547  else
18548  {
18549  refinement_info_for_root_elements[base_element_number][level].
18550  push_back(0);
18551  count++;
18552 
18553 #ifdef PARANOID
18554  check_count++;
18555 #endif
18556  }
18557  }
18558  }
18559 
18560 #ifdef PARANOID
18561  if ( n_additional_data!=check_count)
18562  {
18563  std::stringstream error_message;
18564  error_message << "Number of additional data: " << n_additional_data
18565  << " doesn't match that actually send: " << check_count
18566  << std::endl;
18567  throw OomphLibError(error_message.str(),
18568  OOMPH_CURRENT_FUNCTION,
18569  OOMPH_EXCEPTION_LOCATION);
18570  }
18571 #endif
18572  }
18573  }
18574  }
18575 
18576 
18577 
18578  // Now send the fully assembled refinement info to halo elements
18579  //---------------------------------------------------------------
18580  {
18581 
18582  // Accumulate data to be sent
18583  //---------------------------
18584 
18585  // Map to accumulate data to be sent to other procs
18586  // (map for sparsity)
18587  std::map<unsigned, Vector<unsigned> > data_for_proc;
18588 
18589  // Number of base elements to be sent to specified domain
18590  Vector<unsigned> nbase_elements_for_proc(n_proc,0);
18591 
18592  // Loop over all haloed root elements and find out which
18593  // processors they have haloes on
18594  for (std::map<unsigned,Vector<unsigned> >::iterator it=
18595  halo_domain_of_haloed_base_element.begin();
18596  it!=halo_domain_of_haloed_base_element.end();it++)
18597  {
18598  // Get base element number
18599  unsigned base_element_number=(*it).first;
18600 
18601  // Loop over target domains
18602  Vector<unsigned> domains=(*it).second;
18603  unsigned nd=domains.size();
18604  for (unsigned jd=0;jd<nd;jd++)
18605  {
18606  // Actual number of domain
18607  unsigned d=domains[jd];
18608 
18609  // Keep counting number of base elemements for domain
18610  nbase_elements_for_proc[d]++;
18611 
18612  // Write base element number
18613  data_for_proc[d].push_back(base_element_number);
18614 
18615  // Write refinement info in flat-packed form
18616  for (unsigned level=0;level<max_refinement_level_overall;level++)
18617  {
18618  // Number of entries at each level
18619  unsigned n=
18620  refinement_info_for_root_elements[base_element_number][level].size();
18621  data_for_proc[d].push_back(n);
18622  for (unsigned j=0;j<n;j++)
18623  {
18624  data_for_proc[d].push_back(
18625  refinement_info_for_root_elements[base_element_number][level][j]);
18626  }
18627  }
18628  }
18629  }
18630 
18631 
18632  // Do the actual send
18633  //-------------------
18634 
18635  // Storage for number of data to be sent to each processor
18636  Vector<int> send_n(n_proc,0);
18637 
18638  // Storage for all values to be sent to all processors
18639  Vector<unsigned> send_data;
18640  send_data.reserve(count);
18641 
18642  // Start location within send_data for data to be sent to each processor
18643  Vector<int> send_displacement(n_proc,0);
18644 
18645  // Loop over all processors
18646  for(int rank=0;rank<n_proc;rank++)
18647  {
18648  //Set the offset for the current processor
18649  send_displacement[rank] = send_data.size();
18650 
18651  //Don't bother to do anything if the processor in the loop is the
18652  //current processor
18653  if(rank!=my_rank)
18654  {
18655  // Record how many base elements are to be sent
18656  send_data.push_back(nbase_elements_for_proc[rank]);
18657 
18658  // Add data
18659  unsigned n_data=data_for_proc[rank].size();
18660  for (unsigned j=0;j<n_data;j++)
18661  {
18662  send_data.push_back(data_for_proc[rank][j]);
18663  }
18664  }
18665  //Find the number of data added to the vector
18666  send_n[rank] = send_data.size() - send_displacement[rank];
18667  }
18668 
18669  //Storage for the number of data to be received from each processor
18670  Vector<int> receive_n(n_proc,0);
18671 
18672  //Now send numbers of data to be sent between all processors
18673  MPI_Alltoall(&send_n[0],1,MPI_INT,&receive_n[0],1,MPI_INT,
18674  this->communicator_pt()->mpi_comm());
18675 
18676  //We now prepare the data to be received
18677  //by working out the displacements from the received data
18678  Vector<int> receive_displacement(n_proc,0);
18679  int receive_data_count=0;
18680  for(int rank=0;rank<n_proc;++rank)
18681  {
18682  //Displacement is number of data received so far
18683  receive_displacement[rank] = receive_data_count;
18684  receive_data_count += receive_n[rank];
18685  }
18686 
18687  //Now resize the receive buffer for all data from all processors
18688  //Make sure that it has a size of at least one
18689  if(receive_data_count==0) {++receive_data_count;}
18690  Vector<unsigned> receive_data(receive_data_count);
18691 
18692  //Make sure that the send buffer has size at least one
18693  //so that we don't get a segmentation fault
18694  if(send_data.size()==0) {send_data.resize(1);}
18695 
18696  //Now send the data between all the processors
18697  MPI_Alltoallv(&send_data[0],&send_n[0],&send_displacement[0],
18698  MPI_UNSIGNED,
18699  &receive_data[0],&receive_n[0],
18700  &receive_displacement[0],
18701  MPI_UNSIGNED,
18702  this->communicator_pt()->mpi_comm());
18703 
18704 
18705 
18706  //Now use the received data
18707  //------------------------
18708  for (int send_rank=0;send_rank<n_proc;send_rank++)
18709  {
18710  //Don't bother to do anything for the processor corresponding to the
18711  //current processor or if no data were received from this processor
18712  if((send_rank != my_rank) && (receive_n[send_rank] != 0))
18713  {
18714  //Counter for the data within the large array
18715  unsigned count=receive_displacement[send_rank];
18716 
18717  // Read number of base elements
18718  unsigned nbase_element=receive_data[count];
18719  count++;
18720 
18721  for (unsigned e=0;e<nbase_element;e++)
18722  {
18723  // Read base element number
18724  unsigned base_element_number=receive_data[count];
18725  count++;
18726 
18727  // Provide storage for refinement pattern
18728  refinement_info_for_root_elements[base_element_number].
18729  resize(max_refinement_level_overall);
18730 
18731  // Read refinement info in flat-packed form
18732  for (unsigned level=0;level<max_refinement_level_overall;level++)
18733  {
18734  // Read number of entries at each level
18735  unsigned n=receive_data[count];
18736  count++;
18737 
18738  // Read entries
18739  for (unsigned j=0;j<n;j++)
18740  {
18741  refinement_info_for_root_elements[base_element_number][level].
18742  push_back(
18743  receive_data[count]);
18744  count++;
18745  }
18746  }
18747  }
18748  }
18749  }
18750  }
18751 }
18752 
18753 //==========================================================================
18754 /// Load balance helper routine: Send data to other
18755 /// processors during load balancing.
18756 /// - send_n: Input, number of data to be sent to each processor
18757 /// - send_data: Input, storage for all values to be sent to all processors
18758 /// - send_displacement: Input, start location within send_data for data to
18759 /// be sent to each processor
18760 //==========================================================================
18762  Vector<int>& send_n,
18763  Vector<double>& send_data,
18764  Vector<int>& send_displacement)
18765 {
18766 
18767  // Communicator info
18768  OomphCommunicator* comm_pt=this->communicator_pt();
18769  const int n_proc=comm_pt->nproc();
18770 
18771  //Storage for the number of data to be received from each processor
18772  Vector<int> receive_n(n_proc,0);
18773 
18774  //Now send numbers of data to be sent between all processors
18775  MPI_Alltoall(&send_n[0],1,MPI_INT,&receive_n[0],1,MPI_INT,
18776  this->communicator_pt()->mpi_comm());
18777 
18778  //We now prepare the data to be received
18779  //by working out the displacements from the received data
18780  Vector<int> receive_displacement(n_proc,0);
18781  int receive_data_count=0;
18782  for(int rank=0;rank<n_proc;++rank)
18783  {
18784  //Displacement is number of data received so far
18785  receive_displacement[rank] = receive_data_count;
18786  receive_data_count += receive_n[rank];
18787  }
18788 
18789  //Now resize the receive buffer for all data from all processors
18790  //Make sure that it has a size of at least one
18791  if(receive_data_count==0) {++receive_data_count;}
18792  Vector<double> receive_data(receive_data_count);
18793 
18794  //Make sure that the send buffer has size at least one
18795  //so that we don't get a segmentation fault
18796  if(send_data.size()==0) {send_data.resize(1);}
18797 
18798  //Now send the data between all the processors
18799  MPI_Alltoallv(&send_data[0],&send_n[0],&send_displacement[0],
18800  MPI_DOUBLE,
18801  &receive_data[0],&receive_n[0],
18802  &receive_displacement[0],
18803  MPI_DOUBLE,
18804  this->communicator_pt()->mpi_comm());
18805 
18806  unsigned el_count=0;
18807 
18808  // Only do each node once
18809  Vector<std::map<Node*,bool> >node_done(n_proc);
18810 
18811  //Now use the received data to update the halo nodes
18812  for (int send_rank=0;send_rank<n_proc;send_rank++)
18813  {
18814  //Don't bother to do anything if no data were received from this processor
18815  // NOTE: We do have to loop over our own processor number to process
18816  // the data locally.
18817  if (receive_n[send_rank] != 0)
18818  {
18819  //Counter for the data within the large array
18820  unsigned count=receive_displacement[send_rank];
18821 
18822  // How many batches are there for current rank
18823  unsigned nbatch=unsigned(receive_data[count]);
18824  count++;
18825 
18826  // Loop over batches (containing leaves associated with root elements)
18827  for (unsigned b=0;b<nbatch;b++)
18828  {
18829 
18830  // How many elements were received for this batch?
18831  unsigned nel=unsigned(receive_data[count]);
18832  count++;
18833 
18834  // Get the unique base/root element number of this batch
18835  // in unrefined mesh
18836  unsigned base_el_no=unsigned(receive_data[count]);
18837  count++;
18838 
18839  // Get pointer to base/root element from reverse lookup scheme
18840  GeneralisedElement* root_el_pt=Base_mesh_element_pt[base_el_no];
18841 
18842  // Vector for pointers to associated elements in batch
18843  Vector<GeneralisedElement*> batch_el_pt;
18844 
18845  // Is it a refineable element?
18846  RefineableElement* ref_root_el_pt=
18847  dynamic_cast<RefineableElement*>(root_el_pt);
18848  if (ref_root_el_pt!=0)
18849  {
18850  // Get all leaves associated with this base/root element
18851  Vector<Tree*> all_leaf_nodes_pt;
18852  ref_root_el_pt->tree_pt()->
18853  stick_leaves_into_vector(all_leaf_nodes_pt);
18854 
18855  // How many leaves are there?
18856  unsigned n_leaf=all_leaf_nodes_pt.size();
18857 
18858 #ifdef PARANOID
18859  if (n_leaf!=nel)
18860  {
18861  std::ostringstream error_message;
18862  error_message
18863  << "Number of leaves: " << n_leaf << " "
18864  << " doesn't match number of elements sent in batch: "
18865  << nel << "\n";
18866  throw OomphLibError(
18867  error_message.str(),
18868  OOMPH_CURRENT_FUNCTION,
18869  OOMPH_EXCEPTION_LOCATION);
18870  }
18871 #endif
18872 
18873  // Loop over batch of elements associated with this base/root element
18874  batch_el_pt.resize(n_leaf);
18875  for (unsigned e=0;e<n_leaf;e++)
18876  {
18877  batch_el_pt[e]=all_leaf_nodes_pt[e]->object_pt();
18878  }
18879  }
18880  // Not refineable -- the batch contains just the root element itself
18881  else
18882  {
18883 #ifdef PARANOID
18884  if (1!=nel)
18885  {
18886  std::ostringstream error_message;
18887  error_message
18888  << "Non-refineable root element should only be associated with"
18889  << " one element but nel=" << nel << "\n";
18890  throw OomphLibError(
18891  error_message.str(),
18892  OOMPH_CURRENT_FUNCTION,
18893  OOMPH_EXCEPTION_LOCATION);
18894  }
18895 #endif
18896  batch_el_pt.push_back(root_el_pt);
18897  }
18898 
18899  // Now loop over all elements in batch
18900  for (unsigned e=0;e<nel;e++)
18901  {
18902  GeneralisedElement* el_pt=batch_el_pt[e];
18903  el_count++;
18904 
18905  // FE?
18906  FiniteElement* fe_pt=dynamic_cast<FiniteElement*>(el_pt);
18907  if (fe_pt!=0)
18908  {
18909  // Loop over nodes
18910  unsigned nnod=fe_pt->nnode();
18911  for (unsigned j=0;j<nnod;j++)
18912  {
18913  Node* nod_pt=fe_pt->node_pt(j);
18914  if (!node_done[send_rank][nod_pt])
18915  {
18916  node_done[send_rank][nod_pt]=true;
18917 
18918 
18919  // Read number of values (as double) to allow for resizing
18920  // before read (req'd in case we store data that
18921  // got introduced by attaching FaceElements to bulk)
18922  unsigned nval=unsigned(receive_data[count]);
18923  count++;
18924 
18925 #ifdef PARANOID
18926  // Does the size match?
18927  if (nval<nod_pt->nvalue())
18928  {
18929  std::ostringstream error_message;
18930  error_message
18931  << "Node has more values, namely " << nod_pt->nvalue()
18932  << ", than we're about to receive, namely " << nval
18933  << ". Something's wrong!\n";
18934  throw OomphLibError(
18935  error_message.str(),
18936  OOMPH_CURRENT_FUNCTION,
18937  OOMPH_EXCEPTION_LOCATION);
18938  }
18939 #endif
18940 
18941 
18942 #ifdef PARANOID
18943  // Check if it's been sent as a boundary node
18944  unsigned is_boundary_node=unsigned(receive_data[count]);
18945  count++;
18946 #endif
18947 
18948  // Check if it's actually a boundary node
18949  BoundaryNodeBase *bnod_pt =
18950  dynamic_cast<BoundaryNodeBase*>(nod_pt);
18951  if (bnod_pt!=0)
18952  {
18953 
18954 #ifdef PARANOID
18955  // Check if local and received status are consistent
18956  if (is_boundary_node!=1)
18957  {
18958  std::ostringstream error_message;
18959  error_message
18960  << "Local node is boundary node but information sent is\n"
18961  << "for non-boundary node\n";
18962  throw OomphLibError(
18963  error_message.str(),
18964  OOMPH_CURRENT_FUNCTION,
18965  OOMPH_EXCEPTION_LOCATION);
18966  }
18967 #endif
18968 
18969  // Do we have entries in the map?
18970  unsigned n_entry=unsigned(receive_data[count]);
18971  count++;
18972  if (n_entry>0)
18973  {
18974  // Create storage, if it doesn't already exist, for the map
18975  // that will contain the position of the first entry of
18976  // this face element's additional values,
18977  if(bnod_pt->
18978  index_of_first_value_assigned_by_face_element_pt()==0)
18979  {
18980  bnod_pt->
18981  index_of_first_value_assigned_by_face_element_pt()=
18982  new std::map<unsigned, unsigned>;
18983  }
18984 
18985  // Get pointer to the map of indices associated with
18986  // additional values created by face elements
18987  std::map<unsigned, unsigned>* map_pt=
18988  bnod_pt->
18989  index_of_first_value_assigned_by_face_element_pt();
18990 
18991  // Loop over number of entries in map
18992  for (unsigned i=0;i<n_entry;i++)
18993  {
18994  // Read out pairs...
18995  unsigned first=unsigned(receive_data[count]);
18996  count++;
18997  unsigned second=unsigned(receive_data[count]);
18998  count++;
18999 
19000  // ...and assign
19001  (*map_pt)[first]=second;
19002  }
19003  }
19004  }
19005 #ifdef PARANOID
19006  // Not a boundary node
19007  else
19008  {
19009 
19010  // Check if local and received status are consistent
19011  if (is_boundary_node!=0)
19012  {
19013  std::ostringstream error_message;
19014  error_message
19015  << "Local node is not a boundary node but information \n"
19016  << "sent is for boundary node.\n";
19017  throw OomphLibError(
19018  error_message.str(),
19019  OOMPH_CURRENT_FUNCTION,
19020  OOMPH_EXCEPTION_LOCATION);
19021  }
19022  }
19023 #endif
19024 
19025  // Do we have to resize? This can happen if node was
19026  // resized (due to a FaceElement that hasn't been attached
19027  // yet here) when the send data was written. If so make space
19028  // for the data here
19029  if (nval>nod_pt->nvalue())
19030  {
19031  nod_pt->resize(nval);
19032  }
19033 
19034  // Now read the actual values
19035  nod_pt->read_values_from_vector(receive_data,count);
19036  }
19037  }
19038  }
19039 
19040  // Now add internal data
19041  el_pt->read_internal_data_values_from_vector(receive_data,count);
19042  }
19043  }
19044  }
19045  }
19046 
19047  // Now that this is done, we need to synchronise dofs to get
19048  // the halo element and node values correct
19049  bool do_halos=true;
19050  bool do_external_halos=false;
19051  this->synchronise_dofs(do_halos,do_external_halos);
19052 
19053  // Now rebuild global mesh if required
19054  unsigned n_mesh=nsub_mesh();
19055  if (n_mesh!=0)
19056  {
19057  bool do_halos=false;
19058  bool do_external_halos=true;
19059  this->synchronise_dofs(do_halos,do_external_halos);
19061  }
19062 
19063 }
19064 
19065 
19066 //==========================================================================
19067 /// Load balance helper routine: Get data to be sent to other
19068 /// processors during load balancing and other information about
19069 /// re-distribution.
19070 /// - target_domain_for_local_non_halo_element: Input, generated by METIS.
19071 /// target_domain_for_local_non_halo_element[e] contains the number
19072 /// of the domain [0,1,...,nproc-1] to which non-halo element e on THE
19073 /// CURRENT PROCESSOR ONLY has been assigned. The order of the non-halo
19074 /// elements is the same as in the Problem's mesh, with the halo
19075 /// elements being skipped.
19076 /// - send_n: Output, number of data to be sent to each processor
19077 /// - send_data: Output, storage for all values to be sent to all processors
19078 /// - send_displacement: Output, start location within send_data for data to
19079 /// be sent to each processor
19080 /// - max_refinement_level_overall: Output, max. refinement level of any
19081 /// element
19082 //==========================================================================
19084  const Vector<unsigned>& target_domain_for_local_non_halo_element,
19085  Vector<int>& send_n,
19086  Vector<double>& send_data,
19087  Vector<int>& send_displacement,
19088  Vector<unsigned>& old_domain_for_base_element,
19089  Vector<unsigned>& new_domain_for_base_element,
19090  unsigned& max_refinement_level_overall)
19091 {
19092 
19093  // Communicator info
19094  OomphCommunicator* comm_pt=this->communicator_pt();
19095  const int n_proc=comm_pt->nproc();
19096  const int my_rank=this->communicator_pt()->my_rank();
19097 
19098  //------------------------------------------------------------------------
19099  // Overall strategy: Loop over all elements (in structured meshes),
19100  // identify their corresponding root elements and move all associated
19101  // leaves together, collecting the leaves in batches.
19102  // ------------------------------------------------------------------------
19103 
19104  // Map to store whether the root element has been visited yet
19105  std::map<RefineableElement*,bool> root_el_done;
19106 
19107 #ifdef PARANOID
19108 
19109  // Map for checking if all elements associated with same root
19110  // have the same target processor
19111  std::map<RefineableElement*,unsigned> target_plus_one_for_root;
19112 
19113 #endif
19114 
19115  // Storage for maximum refinement level
19116  unsigned max_refinement_level=0;
19117 
19118  // Storage for (vector of) elements associated with target domain
19119  // (stored in map for sparsity): element_for_processor[d][e] is pointer
19120  // to e-th element that's supposed to move onto processor (domain) d.
19121  std::map<unsigned,Vector<GeneralisedElement*> > element_for_processor;
19122 
19123  // Storage for the number of elements in a specified batch of leaf
19124  // elements, all of which are associated with the same root/base element:
19125  // nelement_batch_for_processor[d][j] is the number of (leaf)
19126  // elements (all associated with the same root) to be moved together to
19127  // domain/processor d, in the j-th batch of elements.
19128  std::map<unsigned,Vector<unsigned> > nelement_batch_for_processor;
19129 
19130  // Storage for the unique number of the root element (in the unrefined
19131  // base mesh) whose leaves are moved together in a batch:
19132  // base_element_for_element_batch_for_processo[d][j] is the number of
19133  // unique number of the root element (in the unrefined
19134  // base mesh) of all leaf elements (associated with that root),
19135  // to be moved together to domain/processor d, in the j-th batch of elements.
19136  std::map<unsigned,Vector<unsigned> >
19137  base_element_for_element_batch_for_processor;
19138 
19139  // Record old and new domains for non-halo root elements (will be
19140  // communicated globally). Initialise to -1 so we can use max
19141  // to extract the right one via MPI_Allreduce.
19142  // NOTE: We communicate these globally to facilitate distribution
19143  // of refinement pattern. While the data itself can be
19144  // sent point-to-point for non-halo elements,
19145  // mesh refinement information also needs to be sent for
19146  // halo elements which aren't known yet.
19147  unsigned n_base_element=Base_mesh_element_pt.size();
19148  Vector<int> old_domain_for_base_element_local(n_base_element,-1);
19149  Vector<int> new_domain_for_base_element_local(n_base_element,-1);
19150 
19151  // Loop over all non-halo elements on current processor and identify roots
19152  // -------------------------------------------------------------------
19153  // All leaf elements in associated tree (must!) get moved together
19154  //----------------------------------------------------------------
19155  unsigned count_non_halo_el=0;
19156  // Get the number of submeshs, if there are no submeshes, then
19157  // increase the counter so that the loop below also work for the only
19158  // one mesh in the problem
19159  unsigned n_mesh = nsub_mesh();
19160  if (n_mesh == 0)
19161  {n_mesh = 1;}
19162  // We need to know if there are structure meshes (with elements) as
19163  // part of the problem in order to perform (or not) the proper
19164  // communications
19165  bool are_there_structured_meshes = false;
19166  // Go for the nonhalo elements only in the TreeBaseMeshes
19167  for (unsigned i_mesh = 0; i_mesh < n_mesh; i_mesh++)
19168  {
19169  // Only work with structured meshes
19170  TriangleMeshBase* sub_mesh_pt =
19171  dynamic_cast<TriangleMeshBase*>(mesh_pt(i_mesh));
19172  if (!(sub_mesh_pt!=0))
19173  {
19174  const unsigned nele = mesh_pt(i_mesh)->nelement();
19175  if (nele > 0)
19176  {
19177  // Change the flag to indicate that there are structured meshes
19178  // (with elements, because we may have meshes with face
19179  // elements and therefore zero elements at this point)
19180  are_there_structured_meshes = true;
19181  }
19182 
19183  for (unsigned e = 0; e < nele; e++)
19184  {
19185  GeneralisedElement* el_pt = mesh_pt(i_mesh)->element_pt(e);
19186  if (!el_pt->is_halo())
19187  {
19188  // New non-halo: Where is this element supposed to go to?
19189  //-------------------------------------------------------
19190  unsigned target_domain=
19191  target_domain_for_local_non_halo_element[count_non_halo_el];
19192 
19193  // Bump up counter for non-halo elements
19194  count_non_halo_el++;
19195 
19196  // Is it a root element? (It is, trivially, if it's not refineable)
19197  //------------------------------------------------------------------
19198  RefineableElement* ref_el_pt=dynamic_cast<RefineableElement*>(el_pt);
19199  if (ref_el_pt==0)
19200  {
19201  // Not refineable so add element itself
19202  element_for_processor[target_domain].push_back(el_pt);
19203 
19204  // Number of elements associated with this root/base
19205  // element (just the element itself)
19206  nelement_batch_for_processor[target_domain].push_back(1);
19207 
19208  // This is the unique base/root element number in unrefined mesh
19209  unsigned element_number_in_base_mesh=
19211 #ifdef PARANOID
19212  if (element_number_in_base_mesh==0)
19213  {
19214  throw OomphLibError("Base_mesh_element_number_plus_one[...]=0",
19215  OOMPH_CURRENT_FUNCTION,
19216  OOMPH_EXCEPTION_LOCATION);
19217  }
19218 #endif
19219  element_number_in_base_mesh-=1;
19220  base_element_for_element_batch_for_processor[target_domain].
19221  push_back(element_number_in_base_mesh);
19222 
19223  /// Where do I come from, where do I go to?
19224  old_domain_for_base_element_local[element_number_in_base_mesh]=
19225  my_rank;
19226  new_domain_for_base_element_local[element_number_in_base_mesh]=
19227  target_domain;
19228  } // if (ref_el_pt==0)
19229  // It's not a root element so we package its leaves into a batch
19230  //--------------------------------------------------------------
19231  // of elements
19232  //------------
19233  else
19234  {
19235  // Get the root element
19236  RefineableElement* root_el_pt=ref_el_pt->root_element_pt();
19237 
19238  // Has this root been visited yet?
19239  if (!root_el_done[root_el_pt])
19240  {
19241  // Now we've done it
19242  root_el_done[root_el_pt]=true;
19243 
19244  // Unique number of root element in base mesh
19245  unsigned element_number_in_base_mesh=
19247 #ifdef PARANOID
19248  if (element_number_in_base_mesh==0)
19249  {
19250  throw OomphLibError("Base_mesh_element_number_plus_one[...]=0",
19251  OOMPH_CURRENT_FUNCTION,
19252  OOMPH_EXCEPTION_LOCATION);
19253  }
19254 #endif
19255  element_number_in_base_mesh-=1;
19256 
19257  /// Where do I come from, where do I go to?
19258  old_domain_for_base_element_local[element_number_in_base_mesh]=
19259  my_rank;
19260  new_domain_for_base_element_local[element_number_in_base_mesh]=
19261  target_domain;
19262 
19263 #ifdef PARANOID
19264  // Store target domain associated with this root element
19265  // (offset by one) to allow checking that all elements
19266  // with the same root move to the same processor
19267  target_plus_one_for_root[root_el_pt]=target_domain+1;
19268 #endif
19269 
19270  // Package all leaves into batch of elements
19271  Vector<Tree*> all_leaf_nodes_pt;
19272  root_el_pt->tree_pt()->stick_leaves_into_vector(all_leaf_nodes_pt);
19273 
19274  // Number of leaves
19275  unsigned n_leaf=all_leaf_nodes_pt.size();
19276 
19277  // Number of elements associated with this root/base element (all
19278  // the leaves)
19279  nelement_batch_for_processor[target_domain].push_back(n_leaf);
19280 
19281  // Store the unique base/root element number in unrefined mesh
19282  base_element_for_element_batch_for_processor[target_domain].
19283  push_back(element_number_in_base_mesh);
19284 
19285  // Loop over leaves
19286  for (unsigned i_leaf=0;i_leaf<n_leaf;i_leaf++)
19287  {
19288  // Add element object at leaf
19289  RefineableElement* leaf_el_pt=
19290  all_leaf_nodes_pt[i_leaf]->object_pt();
19291  element_for_processor[target_domain].push_back(leaf_el_pt);
19292 
19293  // Monitor/update maximum refinement level
19294  unsigned level=all_leaf_nodes_pt[i_leaf]->level();
19295  if (level>max_refinement_level)
19296  {
19297  max_refinement_level=level;
19298  }
19299  }
19300  }
19301 
19302 #ifdef PARANOID
19303  // Root element has already been visited
19304  else
19305  {
19306  // We don't have to do anything with this element since it's
19307  // already been processed earlier, but check that it's scheduled
19308  // to go onto the same processor as its root.
19309  if ((target_plus_one_for_root[root_el_pt]-1)!=target_domain)
19310  {
19311  std::ostringstream error_message;
19312  error_message
19313  << "All elements associated with same root must have "
19314  << "same target. during load balancing\n";
19315  throw OomphLibError(
19316  error_message.str(),
19317  OOMPH_CURRENT_FUNCTION,
19318  OOMPH_EXCEPTION_LOCATION);
19319  }
19320  }
19321 #endif
19322  } // else if (ref_el_pt==0)
19323  } // if (!ele_pt->is_halo())
19324  } // for (e < nele)
19325  } // if (!(sub_mesh_pt!=0))
19326  } // for (i_mesh < n_mesh)
19327 
19328 #ifdef PARANOID
19329  // Have we processed all target domains?
19330  if (target_domain_for_local_non_halo_element.size()!=count_non_halo_el)
19331  {
19332  std::ostringstream error_message;
19333  error_message
19334  << "Have processed " << count_non_halo_el << " of "
19335  << target_domain_for_local_non_halo_element.size()
19336  << " target domains for local non-halo elelemts. \n "
19337  << "Very Odd -- we do (now) strip out the information for elements\n"
19338  << "that are removed in actions_before_distribute()...\n";
19339  throw OomphLibError(
19340  error_message.str(),
19341  OOMPH_CURRENT_FUNCTION,
19342  OOMPH_EXCEPTION_LOCATION);
19343  }
19344 #endif
19345 
19346  // Determine max. refinement level and origin/destination scheme
19347  // -------------------------------------------------------------
19348  // for all root/base elements
19349  // --------------------------
19350 
19351  // Allreduce to work out max max refinement level across all processors
19352  max_refinement_level_overall=0;
19353 
19354  // Only perform this communications if necessary (it means if there
19355  // are structured meshes as part of the problem)
19356  if (are_there_structured_meshes)
19357  {
19358  MPI_Allreduce(&max_refinement_level,&max_refinement_level_overall,1,
19359  MPI_UNSIGNED,MPI_MAX,comm_pt->mpi_comm());
19360  } // if (are_there_structured_meshes)
19361 
19362  // Allreduce to tell everybody about the original and new domains
19363  // for root elements
19364  Vector<int> tmp_old_domain_for_base_element(n_base_element);
19365 
19366  // Only perform this communications if necessary (it means if there
19367  // are structured meshes as part of the problem)
19368  if (are_there_structured_meshes)
19369  {
19370  MPI_Allreduce(&old_domain_for_base_element_local[0],
19371  &tmp_old_domain_for_base_element[0],n_base_element,
19372  MPI_INT,MPI_MAX,comm_pt->mpi_comm());
19373  } // if (are_there_structured_meshes)
19374 
19375  Vector<int> tmp_new_domain_for_base_element(n_base_element);
19376  // Only perform this communications if necessary (it means if there
19377  // are structured meshes as part of the problem)
19378  if (are_there_structured_meshes)
19379  {
19380  MPI_Allreduce(&new_domain_for_base_element_local[0],
19381  &tmp_new_domain_for_base_element[0],n_base_element,
19382  MPI_INT,MPI_MAX,comm_pt->mpi_comm());
19383  } // if (are_there_structured_meshes)
19384 
19385  // Copy across (after optional sanity check)
19386  old_domain_for_base_element.resize(n_base_element);
19387  new_domain_for_base_element.resize(n_base_element);
19388  for(unsigned j=0;j<n_base_element;j++)
19389  {
19390 #ifdef PARANOID
19391  if (tmp_old_domain_for_base_element[j]==-1)
19392  {
19393  std::ostringstream error_message;
19394  error_message
19395  << "Old domain for base element " << j
19396  << ": " << Base_mesh_element_pt[j]
19397  << "or its incarnation as refineable el: "
19398  << dynamic_cast<RefineableElement*>(Base_mesh_element_pt[j])
19399  << " which is of type "
19400  << typeid(*Base_mesh_element_pt[j]).name() << " does not\n"
19401  << "appear to have been assigned by any processor\n";
19402  throw OomphLibError(
19403  error_message.str(),
19404  OOMPH_CURRENT_FUNCTION,
19405  OOMPH_EXCEPTION_LOCATION);
19406  }
19407 #endif
19408  old_domain_for_base_element[j]=tmp_old_domain_for_base_element[j];
19409 #ifdef PARANOID
19410  if (tmp_new_domain_for_base_element[j]==-1)
19411  {
19412  std::ostringstream error_message;
19413  error_message
19414  << "New domain for base element " << j << "which is of type "
19415  << typeid(*Base_mesh_element_pt[j]).name() << " does not\n"
19416  << "appear to have been assigned by any processor\n";
19417  throw OomphLibError(
19418  error_message.str(),
19419  OOMPH_CURRENT_FUNCTION,
19420  OOMPH_EXCEPTION_LOCATION);
19421  }
19422 #endif
19423  new_domain_for_base_element[j]=tmp_new_domain_for_base_element[j];
19424  }
19425 
19426 
19427 
19428 
19429  // Loop over all processors and accumulate data to be sent
19430  //--------------------------------------------------------
19431  send_data.clear();
19432 
19433  // Only do each node once (per processor!)
19434  Vector<std::map<Node*,bool> > node_done(n_proc);
19435 
19436  // Loop over all processors. NOTE: We include current processor
19437  // since we have to refine local elements too -- store their data
19438  // in same data structure as the one used for off-processor elements.
19439  for(int rank=0;rank<n_proc;rank++)
19440  {
19441  //Set the offset for the current processor
19442  send_displacement[rank] = send_data.size();
19443 
19444 #ifdef PARANOID
19445  // Check that total number of elements processed matches those
19446  // in individual batches
19447  unsigned total_nel=element_for_processor[rank].size();
19448 #endif
19449 
19450  // Counter for number of elements
19451  unsigned el_count=0;
19452 
19453  // How many baches are there for current rank?
19454  unsigned nbatch=nelement_batch_for_processor[rank].size();
19455 
19456  // Add to vector of doubles to save on number of comms
19457  send_data.push_back(double(nbatch));
19458 
19459  // Loop over batches of elemnts associated with same root
19460  for (unsigned b=0;b<nbatch;b++)
19461  {
19462  // How many elements are to be sent in this batch?
19463  unsigned nel=nelement_batch_for_processor[rank][b];
19464 
19465  // Get the unique number of the root element in unrefined mesh for
19466  // all the elements in this batch
19467  unsigned base_el_no=
19468  base_element_for_element_batch_for_processor[rank][b];
19469 
19470  // Add unsigneds to send data to minimise number of
19471  // communications
19472  send_data.push_back(double(nel));
19473  send_data.push_back(double(base_el_no));
19474 
19475  // Loop over batch of elements
19476  for (unsigned e=0;e<nel;e++)
19477  {
19478  // Get element
19479  GeneralisedElement* el_pt=element_for_processor[rank][el_count];
19480 
19481  // FE?
19482  FiniteElement* fe_pt=dynamic_cast<FiniteElement*>(el_pt);
19483  if (fe_pt!=0)
19484  {
19485  // Loop over nodes
19486  unsigned nnod=fe_pt->nnode();
19487  for (unsigned j=0;j<nnod;j++)
19488  {
19489  Node* nod_pt=fe_pt->node_pt(j);
19490 
19491  // Reconstruct the nodal values/position from the node's
19492  // possible hanging node representation to be on the safe side
19493  unsigned n_value=nod_pt->nvalue();
19494  unsigned nt=nod_pt->ntstorage();
19495  Vector<double> values(n_value);
19496  unsigned n_dim=nod_pt->ndim();
19497  Vector<double> position(n_dim);
19498 
19499  // Loop over all history values
19500  for(unsigned t=0;t<nt;t++)
19501  {
19502  nod_pt->value(t,values);
19503  for(unsigned i=0;i<n_value;i++)
19504  {
19505  nod_pt->set_value(t,i,values[i]);
19506  }
19507  nod_pt->position(t,position);
19508  for(unsigned i=0;i<n_dim;i++)
19509  {
19510  nod_pt->x(t,i)=position[i];
19511  }
19512  }
19513 
19514 
19515  // Has the node already been done for current rank?
19516  if (!node_done[rank][nod_pt])
19517  {
19518  // Now it has been done
19519  node_done[rank][nod_pt]=true;
19520 
19521  // Store number of values (as double) to allow for resizing
19522  // before read (req'd in case we store data that
19523  // got introduced by attaching FaceElements to bulk)
19524  send_data.push_back(double(n_value));
19525 
19526  // Check if it's a boundary node
19527  BoundaryNodeBase *bnod_pt =
19528  dynamic_cast<BoundaryNodeBase*>(nod_pt);
19529 
19530  // Not a boundary node
19531  if (bnod_pt==0)
19532  {
19533 #ifdef PARANOID
19534  // Record status for checking
19535  send_data.push_back(double(0));
19536 #endif
19537  }
19538  // Yes it's a boundary node
19539  else
19540  {
19541 #ifdef PARANOID
19542  // Record status for checking
19543  send_data.push_back(double(1));
19544 #endif
19545  // Get pointer to the map of indices associated with
19546  // additional values created by face elements
19547  std::map<unsigned, unsigned>* map_pt=
19549 
19550  // No additional values created
19551  if (map_pt==0)
19552  {
19553  send_data.push_back(double(0));
19554  }
19555  // Created additional values
19556  else
19557  {
19558  // How many?
19559  send_data.push_back(double(map_pt->size()));
19560 
19561  // Loop over entries in map and add to send data
19562  for (std::map<unsigned, unsigned>::iterator p=
19563  map_pt->begin();
19564  p!=map_pt->end();p++)
19565  {
19566  send_data.push_back(double((*p).first));
19567  send_data.push_back(double((*p).second));
19568  }
19569  }
19570  }
19571 
19572  // Add the actual values
19573  nod_pt->add_values_to_vector(send_data);
19574  }
19575  }
19576  }
19577 
19578  // Now add internal data
19579  el_pt->add_internal_data_values_to_vector(send_data);
19580 
19581  // Bump up counter in long vector of elements
19582  el_count++;
19583  }
19584 
19585  }
19586 
19587 
19588 #ifdef PARANOID
19589  // Check that total number of elements matches the total of those
19590  // in batches
19591  if (total_nel!=el_count)
19592  {
19593  std::ostringstream error_message;
19594  error_message
19595  << "total_nel: " << total_nel << " "
19596  << " doesn't match total number of elements sent in batch: "
19597  << el_count << "\n";
19598  throw OomphLibError(
19599  error_message.str(),
19600  OOMPH_CURRENT_FUNCTION,
19601  OOMPH_EXCEPTION_LOCATION);
19602  }
19603 #endif
19604 
19605  //Find the number of data added to the vector
19606  send_n[rank] = send_data.size() - send_displacement[rank];
19607  }
19608 }
19609 
19610 
19611 //==========================================================================
19612 /// Get flat-packed refinement pattern for each root element in current
19613 /// mesh (labeled by unique number of root element in unrefined base mesh).
19614 /// The vector stored for each root element contains the following
19615 /// information:
19616 /// - First entry: Number of tree nodes (not just leaves!) in refinement
19617 /// tree emanating from this root [Zero if root element is not refineable]
19618 /// - Loop over all refinement levels
19619 /// - Loop over all tree nodes (not just leaves!)
19620 /// - If associated element exists when the mesh has been refined to
19621 /// this level (either because it has been refined to this level or
19622 /// because it's less refined): 1
19623 /// - If the element is to be refined: 1; else: 0
19624 /// - else (element doesn't exist when mesh is refined to this level
19625 /// (because it's more refined): 0
19626 /// .
19627 /// .
19628 /// .
19629 //==========================================================================
19631  const Vector<unsigned>& old_domain_for_base_element,
19632  const Vector<unsigned>& new_domain_for_base_element,
19633  const unsigned& max_refinement_level_overall,
19634  std::map<unsigned, Vector<unsigned> >& flat_packed_refinement_info_for_root)
19635 {
19636  // Map to store whether the root element has been visited yet
19637  std::map<RefineableElement*,bool> root_el_done;
19638 
19639  // Get the number of submeshs, if there are no submeshes, then
19640  // increase the counter so that the loop below also work for the only
19641  // one mesh in the problem
19642  unsigned n_mesh = nsub_mesh();
19643  if (n_mesh == 0)
19644  {n_mesh = 1;}
19645  // Go for the nonhalo elements only in the TreeBaseMeshes
19646  for (unsigned i_mesh = 0; i_mesh < n_mesh; i_mesh++)
19647  {
19648  // Only work with structured
19649  TriangleMeshBase* sub_mesh_pt =
19650  dynamic_cast<TriangleMeshBase*>(mesh_pt(i_mesh));
19651  if (!(sub_mesh_pt!=0))
19652  {
19653  const unsigned nele_submesh = mesh_pt(i_mesh)->nelement();
19654  for (unsigned e = 0; e < nele_submesh; e++)
19655  {
19656  // Get pointer to element
19657  GeneralisedElement* el_pt=mesh_pt(i_mesh)->element_pt(e);
19658 
19659  // Ignore halos
19660  if (!el_pt->is_halo())
19661  {
19662  // Is it refineable? No!
19663  RefineableElement* ref_el_pt=dynamic_cast<RefineableElement*>(el_pt);
19664  if (ref_el_pt==0)
19665  {
19666  // The element is not refineable - stick a zero in refinement_info
19667  // indicating that there are no tree nodes following
19668  unsigned e=Base_mesh_element_number_plus_one[el_pt];
19669 #ifdef PARANOID
19670  if (e==0)
19671  {
19672  throw OomphLibError(
19673  "Base_mesh_element_number_plus_one[...]=0",
19674  OOMPH_CURRENT_FUNCTION,
19675  OOMPH_EXCEPTION_LOCATION);
19676  }
19677 #endif
19678  e-=1;
19679  flat_packed_refinement_info_for_root[e].push_back(0);
19680  }
19681  // Refineable
19682  else
19683  {
19684  // Get the root element
19685  RefineableElement* root_el_pt=ref_el_pt->root_element_pt();
19686 
19687  // Has this root been visited yet?
19688  if (!root_el_done[root_el_pt])
19689  {
19690  // Get unique number of root element in base mesh
19691  unsigned root_element_number=
19693 
19694 #ifdef PARANOID
19695  if (root_element_number==0)
19696  {
19697  throw OomphLibError(
19698  "Base_mesh_element_number_plus_one[...]=0",
19699  OOMPH_CURRENT_FUNCTION,
19700  OOMPH_EXCEPTION_LOCATION);
19701  }
19702 #endif
19703  root_element_number-=1;
19704 
19705  // Get all the nodes associated with this root element
19706  Vector<Tree*> all_tree_nodes_pt;
19707  root_el_pt->tree_pt()->
19708  stick_all_tree_nodes_into_vector(all_tree_nodes_pt);
19709 
19710  // How many tree nodes are there?
19711  unsigned n_tree_nodes=all_tree_nodes_pt.size();
19712  flat_packed_refinement_info_for_root[root_element_number].
19713  push_back(n_tree_nodes);
19714 
19715  // Loop over all levels
19716  for (unsigned current_level=0;
19717  current_level<max_refinement_level_overall;
19718  current_level++)
19719  {
19720  // Loop over all tree nodes
19721  for (unsigned e=0;e<n_tree_nodes;e++)
19722  {
19723  // What's the level of this tree node?
19724  unsigned level=all_tree_nodes_pt[e]->level();
19725 
19726  // Element exists at this refinement level of the mesh
19727  // if it's at this level or it's at a lower level and a leaf
19728  if ((level==current_level) ||
19729  ((level<current_level) &&
19730  (all_tree_nodes_pt[e]->is_leaf())))
19731  {
19732  flat_packed_refinement_info_for_root[root_element_number].
19733  push_back(1);
19734 
19735  // If it's at this level, and not a leaf, then it will
19736  // need to be refined in the new mesh
19737  if ((level==current_level) &&
19738  (!all_tree_nodes_pt[e]->is_leaf()))
19739  {
19740  flat_packed_refinement_info_for_root[root_element_number].
19741  push_back(1);
19742  }
19743  // Element exists at this level and is a leaf so it doesn't
19744  // have to be refined
19745  else
19746  {
19747  flat_packed_refinement_info_for_root[root_element_number].
19748  push_back(0);
19749  }
19750  }
19751  // Element does not exist at this level so it doesn't have
19752  // to be refined
19753  else
19754  {
19755  flat_packed_refinement_info_for_root[root_element_number].
19756  push_back(0);
19757  }
19758  }
19759  }
19760  // Now we've done it
19761  root_el_done[root_el_pt]=true;
19762  }
19763  }
19764 
19765  } // if (!el_pt->is_halo())
19766  } // for (e < nele_submesh)
19767  } // if (!(sub_mesh_pt!=0))
19768  } // for (i_mesh < n_mesh)
19769 }
19770 
19771 //==========================================================================
19772 /// Load balance helper routine: Function performs max_level_overall
19773 /// successive refinements of the problem's mesh(es) using the following
19774 /// procdure: Given ID of root element, root_element_id, and current
19775 /// refinement level, level, the e-th entry in
19776 /// refinement_info_for_root_elements[root_element_id][level][e] is equal
19777 /// to 2 if the e-th element (using the enumeration when the mesh has been
19778 /// refined to the level-th level) is to be refined during the next
19779 /// refinement; it's 1 if it's not to be refined.
19780 //==========================================================================
19782 (Vector<Vector<Vector<unsigned> > >& refinement_info_for_root_elements,
19783  const unsigned& max_level_overall)
19784 {
19785  // Loop over sub meshes
19786  unsigned n_sub_mesh=nsub_mesh();
19787  unsigned max_mesh=std::max(n_sub_mesh,unsigned(1));
19788  for (unsigned i_mesh=0;i_mesh<max_mesh;i_mesh++)
19789  {
19790  // Choose the right mesh
19791  Mesh* my_mesh_pt=0;
19792  if (n_sub_mesh==0)
19793  {
19794  my_mesh_pt=mesh_pt();
19795  }
19796  else
19797  {
19798  my_mesh_pt=mesh_pt(i_mesh);
19799  }
19800 
19801  // Number of elements on this processor -- currently all elements
19802  // are "base" elements since the mesh hasn't been refined.
19803  unsigned n_el_on_this_proc=my_mesh_pt->nelement();
19804 
19805  // Storage for actual refinement pattern:
19806  // to_be_refined_on_this_proc[level][e] contains the element number
19807  // of the e-th element that is to refined at the level-th refinement level
19808  Vector<Vector<unsigned> > to_be_refined_on_this_proc(max_level_overall);
19809 
19810  // Count, at each level, the total number of elements in the mesh
19811  // (we can accumulate this because we know that elements are
19812  // enumerated tree by tree).
19813  Vector<unsigned> el_count_on_this_proc(max_level_overall,0);
19814 
19815  // Loop over levels where refinement is taking place
19816  for (unsigned level=0;level<max_level_overall;level++)
19817  {
19818  // Loop over roots = unrefined elements on this processor in order.
19819  // Note that this loops over the trees in unique order
19820  for (unsigned e=0;e<n_el_on_this_proc;e++)
19821  {
19822  // Get the (root) element
19823  FiniteElement* el_pt=my_mesh_pt->finite_element_pt(e);
19824 
19825  // What is its unique number in the base mesh
19826  unsigned root_el_no=Base_mesh_element_number_plus_one[el_pt];
19827 #ifdef PARANOID
19828  if (root_el_no==0)
19829  {
19830  throw OomphLibError(
19831  "Base_mesh_element_number_plus_one[...]=0",
19832  OOMPH_CURRENT_FUNCTION,
19833  OOMPH_EXCEPTION_LOCATION);
19834  }
19835 #endif
19836  root_el_no-=1;
19837 
19838  // Number of refinements to be performed starting from current
19839  // root element
19840  unsigned n_refinements=
19841  refinement_info_for_root_elements[root_el_no].size();
19842 
19843  // Perform refinement?
19844  if (level<n_refinements)
19845  {
19846  // Loop over elements at this level
19847  unsigned n_el=
19848  refinement_info_for_root_elements[root_el_no][level].size();
19849  for (unsigned ee=0;ee<n_el;ee++)
19850  {
19851  // Refinement code 2: Element is to be refined at this
19852  // level
19853  if (refinement_info_for_root_elements[root_el_no][level][ee]==2)
19854  {
19855  to_be_refined_on_this_proc[level].
19856  push_back(el_count_on_this_proc[level]);
19857  el_count_on_this_proc[level]++;
19858  }
19859  // Refinement code 1: Element should not be refined at this
19860  // level -- keep going
19861  else if(refinement_info_for_root_elements[root_el_no][level][ee]==1)
19862  {
19863  el_count_on_this_proc[level]++;
19864  }
19865  }
19866  }
19867 
19868  } // end of loop over elements on proc; all of which should be root
19869 
19870  }
19871 
19872  // Now do the actual refinement
19873  TreeBasedRefineableMeshBase* ref_mesh_pt=
19874  dynamic_cast<TreeBasedRefineableMeshBase*>(my_mesh_pt);
19875  if (ref_mesh_pt!=0)
19876  {
19877  ref_mesh_pt->refine_base_mesh(to_be_refined_on_this_proc);
19878  }
19879  }
19880 
19881  // Rebuild global mesh after refinement
19882  if (n_sub_mesh!=0)
19883  {
19884  // Rebuild the global mesh
19886  }
19887 }
19888 
19889 
19890 
19891 //====================================================================
19892 /// Helper function to re-setup the Base_mesh enumeration
19893 /// (used during load balancing) after pruning.
19894 //====================================================================
19896 {
19897  // Storage for number of processors and current processor
19898  int n_proc=this->communicator_pt()->nproc();
19899  int my_rank=this->communicator_pt()->my_rank();
19900 
19901  // Loop over sub meshes
19902  unsigned n_sub_mesh=nsub_mesh();
19903  unsigned max_mesh=std::max(n_sub_mesh,unsigned(1));
19904  for (unsigned i_mesh=0;i_mesh<max_mesh;i_mesh++)
19905  {
19906  // Choose the right mesh
19907  Mesh* my_mesh_pt=0;
19908  if (n_sub_mesh==0)
19909  {
19910  my_mesh_pt=mesh_pt();
19911  }
19912  else
19913  {
19914  my_mesh_pt=mesh_pt(i_mesh);
19915  }
19916 
19917  // Only work with structured meshes
19918  TriangleMeshBase* sub_mesh_pt =
19919  dynamic_cast<TriangleMeshBase*>(my_mesh_pt);
19920  if (!(sub_mesh_pt!=0))
19921  {
19922  // Storage for number of data to be sent to each processor
19923  Vector<int> send_n(n_proc,0);
19924 
19925  // Storage for all values to be sent to all processors
19926  Vector<unsigned> send_data;
19927 
19928  // Start location within send_data for data to be sent to each processor
19929  Vector<int> send_displacement(n_proc,0);
19930 
19931  // Loop over all processors
19932  for(int rank=0;rank<n_proc;rank++)
19933  {
19934  //Set the offset for the current processor
19935  send_displacement[rank] = send_data.size();
19936 
19937  //Don't bother to do anything if the processor in the loop is the
19938  //current processor
19939  if(rank!=my_rank)
19940  {
19941  // Get root haloed elements with that processor
19942  Vector<GeneralisedElement*> root_haloed_elements_pt=
19943  my_mesh_pt->root_haloed_element_pt(rank);
19944  unsigned nel=root_haloed_elements_pt.size();
19945 
19946  // Store element numbers for send
19947  for (unsigned e=0;e<nel;e++)
19948  {
19949  GeneralisedElement* el_pt=root_haloed_elements_pt[e];
19950  send_data.push_back(Base_mesh_element_number_plus_one[el_pt]);
19951  }
19952 
19953  }
19954 
19955  //Find the number of data added to the vector
19956  send_n[rank] = send_data.size() - send_displacement[rank];
19957  }
19958 
19959  //Storage for the number of data to be received from each processor
19960  Vector<int> receive_n(n_proc,0);
19961 
19962  //Now send numbers of data to be sent between all processors
19963  MPI_Alltoall(&send_n[0],1,MPI_INT,&receive_n[0],1,MPI_INT,
19964  this->communicator_pt()->mpi_comm());
19965 
19966  //We now prepare the data to be received
19967  //by working out the displacements from the received data
19968  Vector<int> receive_displacement(n_proc,0);
19969  int receive_data_count=0;
19970  for(int rank=0;rank<n_proc;++rank)
19971  {
19972  //Displacement is number of data received so far
19973  receive_displacement[rank] = receive_data_count;
19974  receive_data_count += receive_n[rank];
19975  }
19976 
19977  //Now resize the receive buffer for all data from all processors
19978  //Make sure that it has a size of at least one
19979  if(receive_data_count==0) {++receive_data_count;}
19980  Vector<unsigned> receive_data(receive_data_count);
19981 
19982  //Make sure that the send buffer has size at least one
19983  //so that we don't get a segmentation fault
19984  if(send_data.size()==0) {send_data.resize(1);}
19985 
19986  //Now send the data between all the processors
19987  MPI_Alltoallv(&send_data[0],&send_n[0],&send_displacement[0],
19988  MPI_UNSIGNED,
19989  &receive_data[0],&receive_n[0],
19990  &receive_displacement[0],
19991  MPI_UNSIGNED,
19992  this->communicator_pt()->mpi_comm());
19993 
19994  //Now use the received data to update the halo element numbers in
19995  //base mesh
19996  for (int send_rank=0;send_rank<n_proc;send_rank++)
19997  {
19998  //Don't bother to do anything for the processor corresponding to the
19999  //current processor or if no data were received from this processor
20000  if((send_rank != my_rank) && (receive_n[send_rank] != 0))
20001  {
20002  //Counter for the data within the large array
20003  unsigned count=receive_displacement[send_rank];
20004 
20005  // Get root halo elements with that processor
20006  Vector<GeneralisedElement*> root_halo_elements_pt=
20007  my_mesh_pt->root_halo_element_pt(send_rank);
20008  unsigned nel=root_halo_elements_pt.size();
20009 
20010  // Read in element numbers
20011  for (unsigned e=0;e<nel;e++)
20012  {
20013  GeneralisedElement* el_pt=root_halo_elements_pt[e];
20014  unsigned el_number_plus_one=receive_data[count++];
20015  Base_mesh_element_number_plus_one[el_pt]=el_number_plus_one;
20016  Base_mesh_element_pt[el_number_plus_one-1]=el_pt;
20017  }
20018 
20019  }
20020 
20021  } //End of data is received
20022 
20023  } // if (!(sub_mesh_pt!=0))
20024 
20025  } // for (i_mesh<max_mesh)
20026 
20027 }
20028 
20029 #endif
20030 
20031  /// Instantiation of public flag to allow suppression of warning
20032  /// messages re reading in unstructured meshes during restart.
20034  false;
20035 
20036 
20037 
20038 }
virtual void build_mesh()
Function to build the Problem&#39;s base mesh; this must be supplied by the user if they wish to use the ...
Definition: problem.h:1325
void assign_local_eqn_numbers(const bool &store_local_dof_pt)
Assign the local equation numbers in all elements If the boolean argument is true then also store poi...
Definition: mesh.cc:702
A Generalised Element class.
Definition: elements.h:76
A class to handle errors in the Newton solver.
Definition: problem.h:2827
void make_steady()
Function to make the time stepper temporarily steady. This is trivially achieved by setting all the w...
Definition: timesteppers.h:360
virtual void distribute(OomphCommunicator *comm_pt, const Vector< unsigned > &element_domain, Vector< GeneralisedElement *> &deleted_element_pt, DocInfo &doc_info, const bool &report_stats, const bool &overrule_keep_as_halo_element_status)
Distribute the problem and doc; make this virtual to allow overloading for particular meshes where fu...
Definition: mesh.cc:4620
void set_communicator_pt(OomphCommunicator *comm_pt)
Definition: mesh.h:1293
Node * haloed_node_pt(const unsigned &p, const unsigned &j)
Access fct to the j-th haloed node in this Mesh whose halo counterpart is held on processor p...
Definition: mesh.h:1673
virtual void solve(Problem *const &problem_pt, DoubleVector &result)=0
Solver: Takes pointer to problem and returns the results vector which contains the solution of the li...
void steady_newton_solve(unsigned const &max_adapt=0)
Solve a steady problem using adaptive Newton&#39;s method, but in the context of an overall unstady probl...
Definition: problem.cc:9236
Vector< double * > Halo_dof_pt
Storage for the halo degrees of freedom (only required) when accessing via the global equation number...
Definition: problem.h:578
EigenSolver * Default_eigen_solver_pt
Pointer to the default eigensolver.
Definition: problem.h:194
Class of matrices containing doubles, and stored as a DenseMatrix<double>, but with solving functiona...
Definition: matrices.h:1234
void partition_distributed_mesh(Problem *problem_pt, const unsigned &objective, Vector< unsigned > &element_domain_on_this_proc, const bool &bypass_metis=false)
Use METIS to assign each element in an already-distributed mesh to a domain. On return, element_domain_on_this_proc[e] contains the number of the domain [0,1,...,ndomain-1] to which non-halo element e on THE CURRENT PROCESSOR ONLY has been assigned. The order of the non-halo elements is the same as in the Problem&#39;s mesh, with the halo elements being skipped.
void remove_duplicate_data(Mesh *const &mesh_pt, bool &actually_removed_some_data)
Private helper function to remove repeated data in external haloed elements in specified mesh...
Definition: problem.cc:2548
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:291
Vector< double > Max_res
Maximum residuals at start and after each newton iteration.
Definition: problem.h:603
virtual void actions_after_parameter_increase(double *const &parameter_pt)
Empty virtual function; provides hook to perform actions after the increase in the arclength paramete...
Definition: problem.h:1144
void read_internal_data_values_from_vector(const Vector< double > &vector_of_values, unsigned &index)
Read all internal data and time history values from the vector starting from index. On return the index will be set to the value at the end of the data that has been read in.
Definition: elements.cc:612
bool is_dparameter_calculated_analytically(double *const &parameter_pt)
Function to determine whether the parameter derivatives are calculated analytically.
Definition: problem.h:280
void get_dofs(DoubleVector &dofs) const
Return the vector of dofs, i.e. a vector containing the current values of all unknowns.
Definition: problem.cc:2455
void newton_solve()
Use Newton method to solve the problem.
Definition: problem.cc:8742
unsigned Sparse_assembly_method
Flag to determine which sparse assembly method to use By default we use assembly by vectors of pairs...
Definition: problem.h:638
double Ds_current
Storage for the current step value.
Definition: problem.h:769
friend class FoldHandler
Definition: problem.h:155
virtual void get_inverse_mass_matrix_times_residuals(DoubleVector &Mres)
Return the residual vector multiplied by the inverse mass matrix Virtual so that it can be overloaded...
Definition: problem.cc:3536
Vector< TimeStepper * > Time_stepper_pt
The Vector of time steppers (there could be many different ones in multiphysics problems) ...
Definition: problem.h:204
void delete_all_external_storage()
Wrapper function to delete external storage for each submesh of the problem.
Definition: problem.cc:16255
double * global_dof_pt(const unsigned &i)
Return a pointer to the dof, indexed by global equation number which may be haloed or stored locally...
Definition: problem.h:1648
void p_refine_selected_elements(const Vector< unsigned > &elements_to_be_refined)
p-refine (one and only!) mesh by refining the elements identified by their numbers relative to the pr...
Definition: problem.cc:15023
double Relaxation_factor
Relaxation fator for Newton method (only a fractional Newton correction is applied if this is less th...
Definition: problem.h:589
bool Must_recompute_load_balance_for_assembly
Boolean indicating that the division of elements over processors during the assembly process must be ...
Definition: problem.h:525
virtual void actions_after_timestep(Problem *problem_pt)
Definition: timesteppers.h:618
void remove_null_pointers_from_external_halo_node_storage()
Consolidate external halo node storage by removing nulled out pointers in external halo and haloed sc...
Definition: problem.cc:3143
virtual void actions_before_adapt()
Actions that are to be performed before a mesh adaptation. These might include removing any additiona...
Definition: problem.h:1004
void get_hessian_vector_products(DoubleVectorWithHaloEntries const &Y, Vector< DoubleVectorWithHaloEntries > const &C, Vector< DoubleVectorWithHaloEntries > &product)
Return the product of the global hessian (derivative of Jacobian matrix with respect to all variables...
Definition: problem.cc:7968
virtual ~Problem()
Virtual destructor to clean up memory.
Definition: problem.cc:174
void initialise(const T &val)
Initialize all values in the matrix to val.
Definition: matrices.h:523
void refine_uniformly_aux(const Vector< unsigned > &nrefine_for_mesh, DocInfo &doc_info, const bool &prune)
Helper function to do compund refinement of (all) refineable (sub)mesh(es) uniformly as many times as...
Definition: problem.cc:15325
void adapt_based_on_error_estimates(unsigned &n_refined, unsigned &n_unrefined, Vector< Vector< double > > &elemental_error)
Adapt problem: Perform mesh adaptation for (all) refineable (sub)mesh(es), based on the error estimat...
Definition: problem.cc:14352
virtual void sparse_assemble_row_or_column_compressed(Vector< int * > &column_or_row_index, Vector< int * > &row_or_column_start, Vector< double * > &value, Vector< unsigned > &nnz, Vector< double * > &residual, bool compressed_row_flag)
Protected helper function that is used to assemble the Jacobian matrix in the case when the storage i...
Definition: problem.cc:4461
LinearSolver * Linear_solver_pt
Pointer to the linear solver for the problem.
Definition: problem.h:176
void doc_errors()
Get max and min error for all elements in submeshes.
Definition: problem.h:2808
unsigned nexternal_halo_element()
Total number of external halo elements in this Mesh.
Definition: mesh.h:1874
bool distributed() const
Definition: problem.h:798
void clear()
wipes the DoubleVector
virtual void sparse_assemble_row_or_column_compressed_with_maps(Vector< int * > &column_or_row_index, Vector< int * > &row_or_column_start, Vector< double * > &value, Vector< unsigned > &nnz, Vector< double * > &residual, bool compressed_row_flag)
Private helper function that is used to assemble the Jacobian matrix in the case when the storage is ...
Definition: problem.cc:4568
void set_default_first_and_last_element_for_assembly()
Set default first and last elements for parallel assembly of non-distributed problem.
Definition: problem.cc:1598
double arc_length_step_solve_helper(double *const &parameter_pt, const double &ds, const unsigned &max_adapt)
Private helper function that actually contains the guts of the arc-length stepping, parameter_pt is a pointer to the parameter that is traded for the arc-length constraint, ds is the desired arc length and max_adapt is the maximum number of spatial adaptations. The pointer to the parameter may be changed if this is called from the Data-based interface.
Definition: problem.cc:10440
double Newton_solver_tolerance
The Tolerance below which the Newton Method is deemed to have converged.
Definition: problem.h:593
bool is_doc_enabled() const
Are we documenting?
void refine_base_mesh(Vector< Vector< unsigned > > &to_be_refined)
Refine base mesh according to specified refinement pattern.
virtual Problem * make_copy()
Make and return a pointer to the copy of the problem. A virtual function that must be filled in by th...
Definition: problem.cc:11891
virtual double global_temporal_error_norm()
Function to calculate a global error norm, used in adaptive timestepping to control the change in tim...
Definition: problem.h:1202
virtual void dump(std::ofstream &dump_file) const
Dump refinement pattern of all refineable meshes and all generic Problem data to file for restart...
Definition: problem.cc:11910
void redistribute(const LinearAlgebraDistribution *const &dist_pt)
Definition: matrices.cc:2580
void redistribute(const LinearAlgebraDistribution *const &dist_pt)
The contents of the vector are redistributed to match the new distribution. In a non-MPI rebuild this...
virtual void output(std::ostream &outfile)
Output the element data — typically the values at the nodes in a format suitable for post-processing...
Definition: elements.h:2884
static ContinuationStorageScheme Continuation_time_stepper
Storage for the single static continuation timestorage object.
Definition: problem.h:750
virtual void sparse_assemble_row_or_column_compressed_with_lists(Vector< int * > &column_or_row_index, Vector< int * > &row_or_column_start, Vector< double * > &value, Vector< unsigned > &nnz, Vector< double * > &residual, bool compressed_row_flag)
Private helper function that is used to assemble the Jacobian matrix in the case when the storage is ...
Definition: problem.cc:4913
virtual void get_eigenproblem_matrices(CRDoubleMatrix &mass_matrix, CRDoubleMatrix &main_matrix, const double &shift=0.0)
Get the matrices required by a eigensolver. If the shift parameter is non-zero the second matrix will...
Definition: problem.cc:8338
bool First_jacobian_sign_change
Boolean to indicate whether a sign change has occured in the Jacobian.
Definition: problem.h:785
HangInfo *const & hanging_pt() const
Return pointer to hanging node data (this refers to the geometric hanging node status) (const version...
Definition: nodes.h:1148
double * values_pt()
access function to the underlying values
void synchronise_dofs(const bool &do_halos, const bool &do_external_halos)
Synchronise the degrees of freedom by overwriting the haloed values with their non-halo counterparts ...
Definition: problem.cc:16363
unsigned iterations
Max. # of iterations performed when the Newton solver died.
Definition: problem.h:2835
void assign_eigenvector_to_dofs(DoubleVector &eigenvector)
Assign the eigenvector passed to the function to the dofs in the problem so that it can be output by ...
Definition: problem.cc:8672
Information for documentation of results: Directory and file number to enable output in the form RESL...
unsigned Max_newton_iterations
Maximum number of newton iterations.
Definition: elements.cc:1627
virtual unsigned ndt() const =0
Number of timestep increments that are required by the scheme.
void enable_mass_matrix_reuse()
Enable recycling of the mass matrix in explicit timestepping schemes. Useful for timestepping on fixe...
Definition: problem.cc:11682
virtual void actions_after_read_unstructured_meshes()
Actions that are to be performed before reading in restart data for problems involving unstructured b...
Definition: problem.h:1092
std::string directory() const
Output directory.
bool Bypass_increase_in_dof_check_during_pruning
Boolean to bypass check of increase in dofs during pruning.
Definition: problem.h:954
cstr elem_len * i
Definition: cfortran.h:607
double * value_pt(const unsigned &i) const
Return the pointer to the i-the stored value. Typically this is required when direct access to the st...
Definition: nodes.h:322
unsigned long set_timestepper_for_all_data(TimeStepper *const &time_stepper_pt, const bool &preserve_existing_data=false)
Set all problem data to have the same timestepper (timestepper_pt) Return the new number of dofs in t...
Definition: problem.cc:11446
virtual void set_predictor_weights()
Set the weights for the predictor previous timestep (currently empty – overwrite for specific scheme...
Definition: timesteppers.h:586
int & face_index()
Index of the face (a number that uniquely identifies the face in the element)
Definition: elements.h:4412
double adaptive_unsteady_newton_solve(const double &dt_desired, const double &epsilon)
Attempt to advance timestep by dt_desired. If the solution fails the timestep will be halved until co...
Definition: problem.cc:10942
Class to keep track of discrete/continous time. It is essential to have a single Time object when usi...
Definition: timesteppers.h:67
void shift_time_values()
Shift time-dependent data along for next timestep: Deal with nodal Data/positions and the element&#39;s i...
Definition: mesh.cc:2059
unsigned ntime_stepper() const
Return the number of time steppers.
Definition: problem.h:1578
void describe_local_dofs(std::ostream &out, const std::string &current_string) const
Function to describe the local dofs of the elements. The ostream specifies the output stream to which...
Definition: mesh.cc:683
void copy(Problem *orig_problem_pt)
Copy Data values, nodal positions etc from specified problem. Note: This is not a copy constructor...
Definition: problem.cc:11740
double DTSF_max_increase
Maximum possible increase of dt between time-steps in adaptive schemes.
Definition: problem.h:706
virtual void get_refinement_levels(unsigned &min_refinement_level, unsigned &max_refinement_level)
Get max/min refinement levels in mesh.
void set_consistent_pinned_values_for_continuation()
Private helper function that is used to set the appropriate pinned values for continuation.
Definition: problem.cc:10390
Vector< Mesh * > Sub_mesh_pt
Vector of pointers to submeshes.
Definition: problem.h:173
void build_without_copy(T *value, int *row_index, int *column_start, const unsigned long &nnz, const unsigned long &n, const unsigned long &m)
Function to build matrix from pointers to arrays which hold the column starts, row indices and non-ze...
Definition: matrices.h:2981
virtual void solve_eigenproblem(Problem *const &problem_pt, const int &n_eval, Vector< std::complex< double > > &eigenvalue, Vector< DoubleVector > &eigenvector)=0
Actual eigensolver. This takes a pointer to a problem and returns a vector of complex numbers represe...
GeneralisedElement *& external_halo_element_pt(const unsigned &p, const unsigned &e)
Access fct to the e-th external halo element in this Mesh whose non-halo counterpart is held on proce...
Definition: mesh.h:1902
virtual void actions_after_adapt()
Actions that are to be performed after a mesh adaptation.
Definition: problem.h:1007
bool Arc_length_step_taken
Boolean to indicate whether an arc-length step has been taken.
Definition: problem.h:788
double Max_permitted_error_for_halo_check
Threshold for error throwing in Problem::check_halo_schemes()
Definition: problem.h:2053
bool Discontinuous_element_formulation
Is the problem a discontinuous one, i.e. can the elemental contributions be treated independently...
Definition: problem.h:693
The Problem class.
Definition: problem.h:152
virtual void read_distributed_info_for_restart(std::istream &restart_file)
OomphCommunicator * communicator_pt() const
const access to the communicator pointer
A class that is used to assemble the residuals in parallel by overloading the get_all_vectors_and_mat...
unsigned nexternal_halo_node()
Total number of external halo nodes in this Mesh.
Definition: mesh.h:1958
void partition_mesh(Problem *problem_pt, const unsigned &ndomain, const unsigned &objective, Vector< unsigned > &element_domain)
Use METIS to assign each element to a domain. On return, element_domain[ielem] contains the number of...
LinearAlgebraDistribution * Dof_distribution_pt
The distribution of the DOFs in this problem. This object is created in the Problem constructor and s...
Definition: problem.h:457
virtual void resolve(const DoubleVector &rhs, DoubleVector &result)
Resolve the system defined by the last assembled jacobian and the rhs vector. Solution is returned in...
Vector< Data * > Global_data_pt
Vector of global data: "Nobody" (i.e. none of the elements etc.) is "in charge" of this Data so it wo...
Definition: problem.h:423
std::string & label()
String used (e.g.) for labeling output files.
void shift_dt()
Update all stored values of dt by shifting each value along the array. This function must be called b...
Definition: timesteppers.h:166
double *& dof_pt(const unsigned &i)
Pointer to i-th dof in the problem.
Definition: problem.h:1708
friend class PitchForkHandler
Definition: problem.h:156
void set_explicit_time_stepper_pt(ExplicitTimeStepper *const &explicit_time_stepper_pt)
Set the explicit timestepper for the problem. The function will automatically create or resize the Ti...
Definition: problem.cc:1570
double & global_value(const unsigned &i)
Direct access to global entry.
A general Finite Element class.
Definition: elements.h:1274
Vector< double > Dof_current
Storage for the present values of the variables.
Definition: problem.h:766
virtual void get_hessian_vector_products(GeneralisedElement *const &elem_pt, Vector< double > const &Y, DenseMatrix< double > const &C, DenseMatrix< double > &product)
Calculate the product of the Hessian (derivative of Jacobian with respect to all variables) an eigenv...
void refine_distributed_base_mesh(Vector< Vector< Vector< unsigned > > > &to_be_refined_on_each_root, const unsigned &max_level_overall)
Load balance helper routine: refine each new base (sub)mesh based upon the elements to be refined wit...
Definition: problem.cc:19782
bool Jacobian_reuse_is_enabled
Is re-use of Jacobian in Newton iteration enabled? Default: false.
Definition: problem.h:615
LinearAlgebraDistribution *const & dof_distribution_pt() const
Return the pointer to the dof distribution (read-only)
Definition: problem.h:1570
bool Shut_up_in_newton_solve
Boolean to indicate if all output is suppressed in Problem::newton_solve(). Defaults to false...
Definition: problem.h:2069
void build(const OomphCommunicator *const comm_pt, const unsigned &first_row, const unsigned &nrow_local, const unsigned &nrow=0)
Sets the distribution. Takes first_row, nrow_local and nrow as arguments. If nrow is not provided or ...
virtual void set_mesh_level_time_stepper(TimeStepper *const &time_stepper_pt, const bool &preserve_existing_data)
Function that can be used to set any additional timestepper data stored at the Mesh (as opposed to no...
Definition: mesh.cc:2132
char t
Definition: cfortran.h:572
virtual void actions_after_implicit_timestep_and_error_estimation()
Actions that should be performed after each implicit time step. This is needed if your actions_after_...
Definition: problem.h:1058
double const & master_weight(const unsigned &i) const
Return weight for dofs on i-th master node.
Definition: nodes.h:753
A Base class for DGElements.
Definition: dg_elements.h:160
virtual void synchronise()
Function that is used to perform any synchronisation required during the solution.
Vector< GeneralisedElement * > Base_mesh_element_pt
Vector to store the correspondence between a root element and its element number within the global me...
Definition: problem.h:544
friend class BlockHopfLinearSolver
Definition: problem.h:164
virtual void dump_distributed_info_for_restart(std::ostream &dump_file)
bool is_halo() const
Is this element a halo?
Definition: elements.h:1141
bool Use_globally_convergent_newton_method
Use the globally convergent newton method.
Definition: problem.h:217
Base class for spatial error estimators.
virtual void read(std::ifstream &restart_file, bool &unsteady_restart)
Read refinement pattern of all refineable meshes and refine them accordingly, then read all Data and ...
Definition: problem.cc:12134
void get_derivative_wrt_global_parameter(double *const &parameter_pt, DoubleVector &result)
Get the derivative of the entire residuals vector wrt a global parameter, used in continuation proble...
Definition: problem.cc:7869
Nodes are derived from Data, but, in addition, have a definite (Eulerian) position in a space of a gi...
Definition: nodes.h:852
void add_values_to_vector(Vector< double > &vector_of_values)
Add all data and time history values to the vector. Overloaded to add the position information as wel...
Definition: nodes.cc:2671
unsigned nmaster() const
Return the number of master nodes.
Definition: nodes.h:733
double Max_residuals
Maximum desired residual: if the maximum residual exceeds this value, the program will exit...
Definition: problem.h:607
static OomphCommunicator * communicator_pt()
access to the global oomph-lib communicator
void describe_dofs(std::ostream &out=*(oomph_info.stream_pt())) const
Function to describe the dofs in terms of the global equation number, i.e. what type of value (nodal ...
Definition: problem.cc:2335
virtual void sparse_assemble_row_or_column_compressed_with_vectors_of_pairs(Vector< int * > &column_or_row_index, Vector< int * > &row_or_column_start, Vector< double * > &value, Vector< unsigned > &nnz, Vector< double * > &residual, bool compressed_row_flag)
Private helper function that is used to assemble the Jacobian matrix in the case when the storage is ...
Definition: problem.cc:5328
bool Empty_actions_after_read_unstructured_meshes_has_been_called
Boolean to indicate that empty actions_after_read_unstructured_meshes() function has been called...
Definition: problem.h:225
unsigned nrow() const
access function to the number of global rows.
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:1445
friend class AugmentedBlockFoldLinearSolver
Definition: problem.h:162
double Parameter_current
Storage for the present value of the global parameter.
Definition: problem.h:744
bool Empty_actions_before_read_unstructured_meshes_has_been_called
Boolean to indicate that empty actions_before_read_unstructured_meshes() function has been called...
Definition: problem.h:221
unsigned first_row() const
access function for the first row on this processor. If not distributed then this is just zero...
Mesh *& mesh_pt()
Return a pointer to the global mesh.
Definition: problem.h:1264
void p_refine_uniformly_aux(const Vector< unsigned > &nrefine_for_mesh, DocInfo &doc_info, const bool &prune)
Helper function to do compund p-refinement of (all) p-refineable (sub)mesh(es) uniformly as many time...
Definition: problem.cc:15476
OomphInfo oomph_info
virtual void dump(std::ofstream &dump_file, const bool &use_old_ordering=true) const
Dump the data in the mesh into a file for restart.
Definition: mesh.cc:1027
bool distributed() const
access function to the distributed - indicates whether the distribution is serial or distributed ...
bool is_hanging() const
Test whether the node is geometrically hanging.
Definition: nodes.h:1207
. SuperLU Project Solver class. This is a combined wrapper for both SuperLU and SuperLU Dist...
long & eqn_number(const unsigned &i)
Return the equation number of the i-th stored variable.
Definition: nodes.h:365
void calculate_predictions()
Calculate predictions.
Definition: problem.cc:11530
unsigned nvalue() const
Return number of values stored in data object (incl pinned ones).
Definition: nodes.h:448
friend class HopfHandler
Definition: problem.h:157
void copy_haloed_eqn_numbers_helper(const bool &do_halos, const bool &do_external_halos)
A private helper function to copy the haloed equation numbers into the halo equation numbers...
Definition: problem.cc:16826
void set_dofs(const DoubleVector &dofs)
Set the values of the dofs.
Definition: problem.cc:3362
static bool mpi_has_been_initialised()
return true if MPI has been initialised
void disable_mass_matrix_reuse()
Function that disables the reuse of the mass matrix.
Definition: dg_elements.h:224
e
Definition: cfortran.h:575
void synchronise_all_dofs()
Perform all required synchronisation in solvers.
Definition: problem.cc:16339
Distributed_problem_matrix_distribution Dist_problem_matrix_distribution
The distributed matrix distribution method 1 - Automatic - the Problem distribution is employed...
Definition: problem.h:942
unsigned nnon_halo_element()
Total number of non-halo elements in this mesh (Costly call computes result on the fly) ...
Definition: mesh.h:1483
unsigned unrefine_uniformly()
Refine (all) refineable (sub)mesh(es) uniformly and rebuild problem. Return 0 for success...
Definition: problem.cc:15727
OomphCommunicator * communicator_pt()
access function to the oomph-lib communicator
Definition: problem.h:1221
virtual void actions_after_change_in_global_parameter(double *const &parameter_pt)
Actions that are to be performed when the global parameter addressed by parameter_pt has been changed...
Definition: problem.h:1116
bool Doc_comprehensive_timings
Global boolean to switch on comprehensive timing – can probably be declared const false when develop...
double Minimum_dt
Minimum desired dt: if dt falls below this value, exit.
Definition: problem.h:699
virtual void shift_time_values()
Shift all values along to prepare for next timestep.
Definition: problem.cc:11507
void recompute_load_balanced_assembly()
Helper function to re-assign the first and last elements to be assembled by each processor during par...
Definition: problem.cc:1677
LinearSolver *& mass_matrix_solver_for_explicit_timestepper_pt()
Definition: problem.h:1431
virtual RefineableElement * root_element_pt()
Pointer to the root element in refinement hierarchy (must be implemented in specific elements that do...
bool Use_default_partition_in_load_balance
Flag to use "default partition" during load balance. Should only be set to true when run in validatio...
Definition: problem.h:510
virtual void symmetrise_eigenfunction_for_adaptive_pitchfork_tracking()
Virtual function that is used to symmetrise the problem so that the current solution exactly satisfie...
Definition: problem.cc:9970
ExplicitTimeStepper * Explicit_time_stepper_pt
Pointer to a single explicit timestepper.
Definition: problem.h:207
unsigned & number()
Number used (e.g.) for labeling output files.
void remove_boundary_node(const unsigned &b, Node *const &node_pt)
Definition: mesh.cc:222
bool built() const
virtual void get_eigenfunction(Vector< DoubleVector > &eigenfunction)
Return the eigenfunction(s) associated with the bifurcation that has been detected in bifurcation tra...
void p_unrefine_uniformly(DocInfo &doc_info)
p-unrefine (all) p-refineable (sub)mesh(es) uniformly and rebuild problem.
Definition: problem.cc:15861
bool Mass_matrix_reuse_is_enabled
Is re-use of the mass matrix in explicit timestepping enabled Default:false.
Definition: problem.h:684
unsigned long nelement() const
Return number of elements in the mesh.
Definition: mesh.h:587
bool & use_predictor_values_as_initial_guess()
Definition: problem.h:1871
virtual void sparse_assemble_row_or_column_compressed_with_two_vectors(Vector< int * > &column_or_row_index, Vector< int * > &row_or_column_start, Vector< double * > &value, Vector< unsigned > &nnz, Vector< double * > &residual, bool compressed_row_flag)
Private helper function that is used to assemble the Jacobian matrix in the case when the storage is ...
Definition: problem.cc:5687
void output(std::ostream &outfile)
Output for all elements.
Definition: mesh.cc:1761
void assign_initial_values_impulsive()
Assign initial values for an impulsive start.
Definition: mesh.cc:2023
void add_time_stepper_pt(TimeStepper *const &time_stepper_pt)
Add a timestepper to the problem. The function will automatically create or resize the Time object so...
Definition: problem.cc:1529
unsigned ndim() const
Return (Eulerian) spatial dimension of the node.
Definition: nodes.h:992
Mesh * Mesh_pt
The mesh pointer.
Definition: problem.h:170
bool Bisect_to_find_bifurcation
Boolean to control wheter bisection is used to located bifurcation.
Definition: problem.h:782
double Continuation_direction
The direction of the change in parameter that will ensure that a branch is followed in one direction ...
Definition: problem.h:738
void setup()
Setup terminate helper.
bool linear_solver_error
Error in the linear solver.
Definition: problem.h:2832
void get_gradient(DoubleVector &gradient)
function to access the gradient, provided it has been computed
AssemblyHandler *& assembly_handler_pt()
Return a pointer to the assembly handler object.
Definition: problem.h:1502
Time * Time_pt
Pointer to global time for the problem.
Definition: problem.h:200
unsigned ndof() const
Return the number of equations/dofs in the element.
Definition: elements.h:834
void reset_gradient()
function to reset the size of the gradient before each Newton solve
void calculate_continuation_derivatives_helper(const DoubleVector &z)
A function that performs the guts of the continuation derivative calculation in arc length continuati...
Definition: problem.cc:9835
unsigned setup_element_count_per_dof()
Function that populates the Element_counter_per_dof vector with the number of elements that contribut...
Definition: problem.cc:219
virtual void get_dvaluesdt(DoubleVector &f)
Get the time derivative of all values (using get_inverse_mass_matrix_times_residuals(..) with all time steppers set to steady) e.g. for use in explicit time steps. The approach used is slighty hacky, beware if you have a residual which is non-linear or implicit in the derivative or if you have overloaded get_jacobian(...).
Definition: problem.cc:3641
double Numerical_zero_for_sparse_assembly
A tolerance used to determine whether the entry in a sparse matrix is zero. If it is then storage nee...
Definition: problem.h:665
EigenSolver * Eigen_solver_pt
Pointer to the eigen solver for the problem.
Definition: problem.h:185
virtual unsigned ndof(GeneralisedElement *const &elem_pt)
Return the number of degrees of freedom in the element elem_pt.
void calculate_predictions()
Calculate predictions for all Data and positions associated with the mesh, usually used in adaptive t...
Definition: mesh.cc:2098
double Desired_proportion_of_arc_length
Proportion of the arc-length to taken by the parameter.
Definition: problem.h:727
double & x(const unsigned &i)
Return the i-th nodal coordinate.
Definition: nodes.h:995
Describes the distribution of a distributable linear algebra type object. Typically this is a contain...
A class that contains the information required by Nodes that are located on Mesh boundaries. A BoundaryNode of a particular type is obtained by combining a given Node with this class. By differentiating between Nodes and BoundaryNodes we avoid a lot of un-necessary storage in the bulk Nodes.
Definition: nodes.h:1855
void build(const DoubleVector &old_vector)
Just copys the argument DoubleVector.
void refine_selected_elements(const Vector< unsigned > &elements_to_be_refined)
Refine (one and only!) mesh by splitting the elements identified by their numbers relative to the pro...
Definition: problem.cc:14744
void null_external_halo_node(const unsigned &p, Node *nod_pt)
Null out specified external halo node (used when deleting duplicates)
Definition: mesh.cc:8206
void assign_initial_values_impulsive()
Initialise data and nodal positions to simulate impulsive start from initial configuration/solution.
Definition: problem.cc:11380
void set_nodal_and_elemental_time_stepper(TimeStepper *const &time_stepper_pt, const bool &preserve_existing_data)
Set the timestepper associated with all nodal and elemental data stored in the mesh.
Definition: mesh.h:993
double & dof(const unsigned &i)
i-th dof in the problem
Definition: problem.h:1696
virtual double * bifurcation_parameter_pt() const
Return a pointer to the bifurcation parameter in bifurcation tracking problems.
unsigned nhalo_node()
Total number of halo nodes in this Mesh.
Definition: mesh.h:1542
double & dof_derivative(const unsigned &i)
Access function to the derivative of the i-th (local) dof with respect to the arc length...
Definition: problem.h:1150
double doubly_adaptive_unsteady_newton_solve_helper(const double &dt, const double &epsilon, const unsigned &max_adapt, const unsigned &suppress_resolve_after_spatial_adapt, const bool &first, const bool &shift=true)
Private helper function that actually performs the unsteady "doubly" adaptive Newton solve...
Definition: problem.cc:11264
void prune_halo_elements_and_nodes(DocInfo &doc_info, const bool &report_stats)
(Irreversibly) prune halo(ed) elements and nodes, usually after another round of refinement, to get rid of excessively wide halo layers. Note that the current mesh will be now regarded as the base mesh and no unrefinement relative to it will be possible once this function has been called.
Definition: problem.cc:1033
OomphCommunicator * Communicator_pt
The communicator for this problem.
Definition: problem.h:1216
void store_current_dof_values()
Store the current values of the degrees of freedom.
Definition: problem.cc:8568
unsigned Sparse_assemble_with_arrays_initial_allocation
the number of elements to initially allocate for a matrix row within the sparse_assembly_with_two_arr...
Definition: problem.h:652
std::ostream *& stream_pt()
Access function for the stream pointer.
GeneralisedElement *& element_pt(const unsigned long &e)
Return pointer to element e.
Definition: mesh.h:462
void set_pinned_values_to_zero()
Set all pinned values to zero. Used to set boundary conditions to be homogeneous in the copy of the p...
Definition: problem.cc:4295
void calculate_continuation_derivatives_fd(double *const &parameter_pt)
A function to calculate the derivatives with respect to the arc-length required for continuation by f...
Definition: problem.cc:9749
ExplicitTimeStepper * explicit_predictor_pt()
Definition: timesteppers.h:389
void explicit_timestep(const double &dt, const bool &shift_values=true)
Take an explicit timestep of size dt and optionally shift any stored values of the time history...
Definition: problem.cc:10815
bool is_resolve_enabled() const
Boolean flag indicating if resolves are enabled.
double Theta_squared
Value of the scaling parameter required so that the parameter occupies the desired proportion of the ...
Definition: problem.h:731
bool is_mesh_distributed() const
Boolean to indicate if Mesh has been distributed.
Definition: mesh.h:1266
void copy(SolidNode *orig_node_pt)
Copy nodal positions and associated data from specified node object.
Definition: nodes.cc:3330
Base class for tree-based refineable meshes.
void update_predicted_time(const double &new_time)
Definition: timesteppers.h:400
void build_halo_scheme(DoubleVectorHaloScheme *const &halo_scheme_pt)
Construct the halo scheme and storage for the halo data.
double Parameter_derivative
Storage for the derivative of the global parameter wrt arc-length.
Definition: problem.h:741
bool Doc_time_in_distribute
Protected boolean flag to provide comprehensive timimings during problem distribution. Initialised to false.
Definition: problem.h:634
virtual void set_weights()=0
Function to set the weights for present timestep (don&#39;t need to pass present timestep or previous tim...
void initialise(const double &v)
initialise the whole vector with value v
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:2025
virtual void get_inverse_mass_matrix_times_residuals(Vector< double > &minv_res)
Function that returns the current value of the residuals multiplied by the inverse mass matrix (virtu...
Definition: dg_elements.cc:589
unsigned nexternal_haloed_element()
Total number of external haloed elements in this Mesh.
Definition: mesh.h:1918
void copy(Data *orig_data_pt)
Copy Data values from specified Data object.
Definition: nodes.cc:561
bool Time_adaptive_newton_crash_on_solve_fail
Bool to specify what to do if a Newton solve fails within a time adaptive solve. Default (false) is t...
Definition: problem.h:612
void reset_assembly_handler_to_default()
Reset the system to the standard non-augemented state.
Definition: problem.cc:10192
void get_all_error_estimates(Vector< Vector< double > > &elemental_error)
Return the error estimates computed by (all) refineable (sub)mesh(es) in the elemental_error structur...
Definition: problem.cc:14455
void remesh_from_triangulateio(std::istream &restart_file)
Regenerate the mesh from a dumped triangulateio file and dumped boundary coordinates of boundary node...
unsigned nexternal_haloed_node()
Total number of external haloed nodes in this Mesh.
Definition: mesh.h:2060
Tree * tree_pt()
Access function: Pointer to quadtree representation of this element.
double Maximum_dt
Maximum desired dt.
Definition: problem.h:702
unsigned Sparse_assemble_with_arrays_allocation_increment
the number of elements to add to a matrix row when the initial allocation is exceeded within the spar...
Definition: problem.h:657
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:267
double * bifurcation_parameter_pt() const
Return pointer to the parameter that is used in the bifurcation detection. If we are not tracking a b...
Definition: problem.cc:9992
double & dt(const unsigned &t=0)
Definition: timesteppers.h:137
Data *& internal_data_pt(const unsigned &i)
Return a pointer to i-th internal data object.
Definition: elements.h:623
bool Mass_matrix_has_been_computed
Has the mass matrix been computed (and can therefore be reused) Default: false.
Definition: problem.h:688
virtual void actions_after_distribute()
Actions to be performed after a (mesh) distribution.
Definition: problem.h:1102
unsigned long eqn_number(const unsigned &ieqn_local) const
Return the global equation number corresponding to the ieqn_local-th local equation number...
Definition: elements.h:709
unsigned nposition_type() const
Number of coordinate types needed in the mapping between local and global coordinates.
Definition: nodes.h:966
virtual void actions_before_timestep(Problem *problem_pt)
Definition: timesteppers.h:614
void position(Vector< double > &pos) const
Compute Vector of nodal positions either directly or via hanging node representation.
Definition: nodes.cc:2413
unsigned Nnewton_iter_taken
Actual number of Newton iterations taken during the most recent iteration.
Definition: problem.h:600
void get_flat_packed_refinement_pattern_for_load_balancing(const Vector< unsigned > &old_domain_for_base_element, const Vector< unsigned > &new_domain_for_base_element, const unsigned &max_refinement_level_overall, std::map< unsigned, Vector< unsigned > > &flat_packed_refinement_info_for_root)
Get flat-packed refinement pattern for each root element in current mesh (labeled by unique number of...
Definition: problem.cc:19630
std::map< GeneralisedElement *, unsigned > Base_mesh_element_number_plus_one
Map which stores the correspondence between a root element and its element number (plus one) within t...
Definition: problem.h:534
bool predict_by_explicit_step() const
Flag: is adaptivity done by taking a separate step using an ExplicitTimeStepper object?
Definition: timesteppers.h:382
unsigned nrow_local() const
access function for the num of local rows on this processor. If no MPI then Nrow is returned...
DoubleVectorWithHaloEntries Element_count_per_dof
Counter that records how many elements contribute to each dof. Used to determine the (discrete) arc-l...
Definition: problem.h:557
virtual void reestablish_distribution_info_for_restart(OomphCommunicator *comm_pt, std::istream &restart_file)
unsigned ndt() const
Return the number of timesteps stored.
Definition: timesteppers.h:133
Vector< GeneralisedElement * > root_halo_element_pt(const unsigned &p)
Vector of pointers to root halo elements in this Mesh whose non-halo counterpart is held on processor...
Definition: mesh.h:1518
Time *& time_pt()
Return a pointer to the global time object.
Definition: problem.h:1446
A class that represents a collection of data; each Data object may contain many different individual ...
Definition: nodes.h:89
void activate_pitchfork_tracking(double *const &parameter_pt, const DoubleVector &symmetry_vector, const bool &block_solve=true)
Turn on pitchfork tracking using the augmented system specified in the PitchForkHandler class...
Definition: problem.cc:10103
void prune_halo_elements_and_nodes(Vector< GeneralisedElement *> &deleted_element_pt, const bool &report_stats=false)
(Irreversibly) prune halo(ed) elements and nodes, usually after another round of refinement, to get rid of excessively wide halo layers. Note that the current mesh will be now regarded as the base mesh and no unrefinement relative to it will be possible once this function has been called.
Definition: mesh.h:1333
void activate_fold_tracking(double *const &parameter_pt, const bool &block_solve=true)
Turn on fold tracking using the augmented system specified in the FoldHandler class. After a call to this function subsequent calls of the standard solution methods will converge to a fold (limit) point at a particular value of the variable addressed by parameter_pt. The system may not converge if the initial guess is sufficiently poor or, alternatively, if finite differencing is used to calculate the jacobian matrix in the elements. If the boolean flag block_solver is true (the default) then a block factorisation is used to solve the augmented system which is both faster and uses less memory.
Definition: problem.cc:10012
double & x_gen(const unsigned &k, const unsigned &i)
Reference to the generalised position x(k,i).
Definition: nodes.h:1055
void set_external_haloed_node_pt(const unsigned &p, const Vector< Node *> &external_haloed_node_pt)
Set vector of external haloed node in this Mesh whose halo external counterpart is held on processor ...
Definition: mesh.h:2112
virtual void get_residuals(DoubleVector &residuals)
Return the fully-assembled residuals Vector for the problem: Virtual so it can be overloaded in for m...
Definition: problem.cc:3672
Data *const & variable_position_pt() const
Pointer to variable_position data (const version)
Definition: nodes.h:1655
A class that is used to define the functions used to assemble the elemental contributions to the mass...
bool Pause_at_end_of_sparse_assembly
Protected boolean flag to halt program execution during sparse assemble process to assess peak memory...
Definition: problem.h:630
TimeStepper *& time_stepper_pt()
Access function for the pointer to the first (presumably only) timestepper.
Definition: problem.h:1460
void setup_dof_halo_scheme()
Function that is used to setup the halo scheme.
Definition: problem.cc:285
Vector< unsigned > First_el_for_assembly
First element to be assembled by given processor for non-distributed problem (only kept up to date wh...
Definition: problem.h:515
void get_fd_jacobian(DoubleVector &residuals, DenseMatrix< double > &jacobian)
Return the fully-assembled Jacobian and residuals, generated by finite differences.
Definition: problem.cc:7805
Node *& external_haloed_node_pt(const unsigned &p, const unsigned &j)
Access fct to the j-th external haloed node in this Mesh whose halo external counterpart is held on p...
Definition: mesh.h:2087
double & time()
Return the current value of the continuous time.
Definition: timesteppers.h:130
Node *const & master_node_pt(const unsigned &i) const
Return a pointer to the i-th master node.
Definition: nodes.h:736
virtual int bifurcation_type() const
Return an unsigned integer to indicate whether the handler is a bifurcation tracking handler...
void check_halo_schemes(DocInfo &doc_info, double &max_permitted_error_for_halo_check)
Check halo and shared schemes on the mesh.
Definition: mesh.cc:6551
LinearSolver * Default_linear_solver_pt
Pointer to the default linear solver.
Definition: problem.h:191
void p_adapt()
p-adapt problem: Perform mesh adaptation for (all) refineable (sub)mesh(es), based on their own error...
Definition: problem.h:2763
Vector< double > Elemental_assembly_time
Storage for assembly times (used for load balancing)
Definition: problem.h:570
void p_refine_uniformly()
p-refine (all) p-refineable (sub)mesh(es) uniformly and rebuild problem
Definition: problem.h:2617
Node *& node_pt(const unsigned &n)
Return a pointer to the local node n.
Definition: elements.h:2109
int Sign_of_jacobian
Storage for the sign of the global Jacobian.
Definition: problem.h:734
double timer()
returns the time in seconds after some point in past
void get_element_errors(Mesh *&mesh_pt, Vector< double > &elemental_error)
Compute the elemental error-measures for a given mesh and store them in a vector. ...
A class that is used to define the functions used to assemble the elemental contributions to the resi...
virtual void sparse_assemble_row_or_column_compressed_with_two_arrays(Vector< int * > &column_or_row_index, Vector< int * > &row_or_column_start, Vector< double * > &value, Vector< unsigned > &nnz, Vector< double * > &residual, bool compressed_row_flag)
Private helper function that is used to assemble the Jacobian matrix in the case when the storage is ...
Definition: problem.cc:6053
void initialise(const _Tp &__value)
Iterate over all values and set to the desired value.
Definition: Vector.h:171
double & dof_current(const unsigned &i)
Access function to the current value of the i-th (local) dof at the start of a continuation step...
Definition: problem.h:1160
static bool Suppress_warning_about_actions_before_read_unstructured_meshes
Flag to allow suppression of warning messages re reading in unstructured meshes during restart...
Definition: problem.h:313
bool Use_continuation_timestepper
Boolean to control original or new storage of dof stuff.
Definition: problem.h:747
virtual void actions_before_newton_solve()
Any actions that are to be performed before a complete Newton solve (e.g. adjust boundary conditions)...
Definition: problem.h:1015
virtual void enable_resolve()
Enable resolve (i.e. store matrix and/or LU decomposition, say) Virtual so it can be overloaded to pe...
Vector< Vector< unsigned > > Sparse_assemble_with_arrays_previous_allocation
the number of elements in each row of a compressed matrix in the previous matrix assembly.
Definition: problem.h:661
unsigned nhaloed_node()
Total number of haloed nodes in this Mesh.
Definition: mesh.h:1645
void disable_mass_matrix_reuse()
Turn off recyling of the mass matrix in explicit timestepping schemes.
Definition: problem.cc:11707
Vector< double > * Saved_dof_pt
Pointer to vector for backup of dofs.
Definition: problem.h:210
Class that contains data for hanging nodes.
Definition: nodes.h:684
Node *& node_pt(const unsigned long &n)
Return pointer to global node n.
Definition: mesh.h:456
virtual void get_residuals(GeneralisedElement *const &elem_pt, Vector< double > &residuals)
Return the contribution to the residuals of the element elem_pt.
void get_my_eqns(AssemblyHandler *const &assembly_handler_pt, const unsigned &el_lo, const unsigned &el_hi, Vector< unsigned > &my_eqns)
Helper method that returns the (unique) global equations to which the elements in the range el_lo to ...
Definition: problem.cc:6505
void get_data_to_be_sent_during_load_balancing(const Vector< unsigned > &element_domain_on_this_proc, Vector< int > &send_n, Vector< double > &send_data, Vector< int > &send_displacement, Vector< unsigned > &old_domain_for_base_element, Vector< unsigned > &new_domain_for_base_element, unsigned &max_refinement_level_overall)
Load balance helper routine: Get data to be sent to other processors during load balancing and other ...
Definition: problem.cc:19083
bool does_pointer_correspond_to_problem_data(double *const &parameter_pt)
Return a boolean flag to indicate whether the pointer parameter_pt refers to values stored in a Data ...
Definition: problem.cc:9778
double Timestep_reduction_factor_after_nonconvergence
What it says: If temporally adaptive Newton solver fails to to converge, reduce timestep by this fact...
Definition: problem.h:2081
Vector< GeneralisedElement * > root_haloed_element_pt(const unsigned &p)
Vector of pointers to root haloed elements in this Mesh whose non-halo counterpart is held on process...
Definition: mesh.h:1612
void check_halo_schemes()
Check the halo/haloed node/element schemes.
Definition: problem.h:1995
void initialise_dt(const double &dt)
Set all timesteps to the same value, dt, and assign weights for all timesteppers in the problem...
Definition: problem.cc:13035
double FD_step_used_in_get_hessian_vector_products
Definition: problem.h:679
unsigned long ndof() const
Return the number of dofs.
Definition: problem.h:1574
void add_internal_data_values_to_vector(Vector< double > &vector_of_values)
Add all internal data and time history values to the vector in the internal storage order...
Definition: elements.cc:600
unsigned rank_of_global_row(const unsigned i) const
return the processor rank of the global row number i
unsigned long nnode() const
Return number of nodes in the mesh.
Definition: mesh.h:590
A class for compressed column matrices that store doubles.
Definition: matrices.h:2573
unsigned ntstorage() const
Return total number of doubles stored per value to record time history of each value (one for steady ...
Definition: nodes.cc:847
virtual void disable_resolve()
Disable resolve (i.e. store matrix and/or LU decomposition, say) This function simply resets an inter...
virtual void undo_make_steady()
Reset the is_steady status of a specific TimeStepper to its default and re-assign the weights...
Definition: timesteppers.h:457
void calculate_continuation_derivatives_fd_helper(double *const &parameter_pt)
A function that performs the guts of the continuation derivative calculation in arc-length continuati...
Definition: problem.cc:9918
FiniteElement * finite_element_pt(const unsigned &e) const
Upcast (downcast?) to FiniteElement (needed to access FiniteElement member functions).
Definition: mesh.h:477
virtual void get_jacobian(DoubleVector &residuals, DenseDoubleMatrix &jacobian)
Return the fully-assembled Jacobian and residuals for the problem Interface for the case when the Jac...
Definition: problem.cc:3852
virtual void enable_computation_of_gradient()
function to enable the computation of the gradient required for the globally convergent Newton method...
void merge_meshes(const Vector< Mesh *> &sub_mesh_pt)
Merge meshes. Note: This simply merges the meshes&#39; elements and nodes (ignoring duplicates; no bounda...
Definition: mesh.cc:69
bool Keep_temporal_error_below_tolerance
Boolean to decide if a timestep is to be rejected if the error estimate post-solve (computed by globa...
Definition: problem.h:2088
void copy(Node *orig_node_pt)
Copy all nodal data from specified Node object.
Definition: nodes.cc:1834
void add_eigenvector_to_dofs(const double &epsilon, const DoubleVector &eigenvector)
Add the eigenvector passed to the function scaled by the constat epsilon to the dofs in the problem s...
Definition: problem.cc:8705
friend class BlockPitchForkLinearSolver
Definition: problem.h:161
void disable_doc()
Disable documentation.
void set_external_halo_node_pt(const unsigned &p, const Vector< Node *> &external_halo_node_pt)
Set vector of external halo node in this Mesh whose non-halo external counterpart is held on processo...
Definition: mesh.h:2046
unsigned ninternal_data() const
Return the number of internal data objects.
Definition: elements.h:828
double maxres
Max. residual when Newton solver died.
Definition: problem.h:2838
A Class for nodes that deform elastically (i.e. position is an unknown in the problem). The idea is that the Eulerian positions are stored in a Data object and the Lagrangian coordinates are stored in addition. The pointer that addresses the Eulerian positions is set to the pointer to Value in the Data object. Hence, SolidNode uses knowledge of the internal structure of Data and must be a friend of the Data class. In order to allow a mesh to deform via an elastic-style equation in deforming-domain problems, the positions are stored separately from the values, so that elastic problems may be combined with any other type of problem.
Definition: nodes.h:1569
void flush_sub_meshes()
Flush the problem&#39;s collection of sub-meshes. Must be followed by call to rebuild_global_mesh().
Definition: problem.h:1302
A class that is used to define the functions used when assembling the derivatives of the residuals wi...
Problem()
Constructor: Allocate space for one time stepper and set all pointers to NULL and set defaults for al...
Definition: problem.cc:75
void refine_uniformly(DocInfo &doc_info)
Refine mesh uniformly and doc process.
double arc_length_step_solve(double *const &parameter_pt, const double &ds, const unsigned &max_adapt=0)
Solve a steady problem using arc-length continuation, when the parameter that becomes a variable corr...
Definition: problem.cc:10211
void initialise_dt(const double &dt_)
Set all timesteps to the same value, dt.
Definition: timesteppers.h:109
void stick_all_tree_nodes_into_vector(Vector< Tree * > &)
Traverse and stick pointers to all "nodes" into Vector.
Definition: tree.cc:276
void set_external_values(const LinearAlgebraDistribution *const &dist_pt, double *external_values, bool delete_external_values)
Allows are external data to be used by this vector. WARNING: The size of the external data must corre...
Class for the ARPACK eigensolver.
Definition: eigen_solver.h:103
void send_data_to_be_sent_during_load_balancing(Vector< int > &send_n, Vector< double > &send_data, Vector< int > &send_displacement)
Load balance helper routine: Send data to other processors during load balancing. ...
Definition: problem.cc:18761
unsigned nrow() const
access function to the number of global rows.
bool Bifurcation_detection
Boolean to control bifurcation detection via determinant of Jacobian.
Definition: problem.h:779
double dot(const DoubleVector &vec) const
compute the dot product of this vector with the vector vec.
bool Use_finite_differences_for_continuation_derivatives
Definition: problem.h:792
virtual void get_jacobian(GeneralisedElement *const &elem_pt, Vector< double > &residuals, DenseMatrix< double > &jacobian)
Calculate the elemental Jacobian matrix "d equation / d variable" for elem_pt.
void set_consistent_pinned_values_for_continuation(ContinuationStorageScheme *const &continuation_stepper_pt)
Set consistent values for pinned data in continuation.
Definition: mesh.cc:2167
void resize(const unsigned &n_dt)
Resize the vector holding the number of previous timesteps and initialise the new values to zero...
Definition: timesteppers.h:106
bool Default_set_initial_condition_called
Has default set_initial_condition function been called? Default: false.
Definition: problem.h:214
Time *const & time_pt() const
Access function for the pointer to time (const version)
Definition: timesteppers.h:540
bool Jacobian_has_been_computed
Has a Jacobian been computed (and can therefore be re-used if required)? Default: false...
Definition: problem.h:619
void set_consistent_pinned_values(Data *const &data_pt)
Set consistent values of the derivatives and current value when the data is pinned. This must be done by the "timestepper" because only it knows the local storage scheme.
std::string string(const unsigned &i)
Return the i-th string or "" if the relevant string hasn&#39;t been defined.
Vector< double > Dof_derivative
Storage for the derivative of the problem variables wrt arc-length.
Definition: problem.h:763
unsigned Max_newton_iterations
Maximum number of Newton iterations.
Definition: problem.h:596
FiniteElement *& bulk_element_pt()
Pointer to higher-dimensional "bulk" element.
Definition: elements.h:4515
Data *& global_data_pt(const unsigned &i)
Return a pointer to the the i-th global data object.
Definition: problem.h:1557
bool does_pointer_correspond_to_mesh_data(double *const &parameter_pt)
Does the double pointer correspond to any mesh data.
Definition: mesh.cc:2201
bool is_pinned(const unsigned &i) const
Test whether the i-th variable is pinned (1: true; 0: false).
Definition: nodes.h:403
void get_all_halo_data(std::map< unsigned, double *> &map_of_halo_data)
Get all the halo data stored in the mesh and add pointers to the data to the map, indexed by global e...
Definition: mesh.cc:4418
void unsteady_newton_solve(const double &dt)
Advance time by dt and solve by Newton&#39;s method. This version always shifts time values.
Definition: problem.cc:10844
void bifurcation_adapt_helper(unsigned &n_refined, unsigned &n_unrefined, const unsigned &bifurcation_type, const bool &actually_adapt=true)
A function that is used to adapt a bifurcation-tracking problem, which requires separate interpolatio...
Definition: problem.cc:13153
unsigned nglobal_data() const
Return the number of global data values.
Definition: problem.h:1581
virtual void partition_global_mesh(Mesh *&global_mesh_pt, DocInfo &doc_info, Vector< unsigned > &element_domain, const bool &report_stats=false)
Definition: problem.cc:864
Vector< unsigned > distribute(const Vector< unsigned > &element_partition, DocInfo &doc_info, const bool &report_stats=false)
Distribute the problem and doc, using the specified partition; returns a vector which details the par...
Definition: problem.cc:391
bool Use_predictor_values_as_initial_guess
Use values from the time stepper predictor as an initial guess.
Definition: problem.h:232
unsigned newton_solve_continuation(double *const &parameter_pt)
Perform a basic arc-length continuation step using Newton&#39;s method. Returns number of Newton steps ta...
Definition: problem.cc:9324
virtual unsigned long eqn_number(GeneralisedElement *const &elem_pt, const unsigned &ieqn_local)
Return the global equation number of the local unknown ieqn_local in elem_pt.
void refine_uniformly()
Refine (all) refineable (sub)mesh(es) uniformly and rebuild problem.
Definition: problem.h:2520
virtual void timestep(ExplicitTimeSteppableObject *const &object_pt, const double &dt)=0
Pure virtual function that is used to advance time in the object.
void pause(std::string message)
Pause and display message.
void build_global_mesh()
Build the global mesh by combining the all the submeshes. Note: The nodes boundary information refers...
Definition: problem.cc:1473
unsigned long assign_eqn_numbers(const bool &assign_local_eqn_numbers=true)
Assign all equation numbers for problem: Deals with global data (= data that isn&#39;t attached to any el...
Definition: problem.cc:1966
virtual void actions_before_newton_step()
Any actions that are to be performed before each individual Newton step. Most likely to be used for d...
Definition: problem.h:1036
AssemblyHandler * Default_assembly_handler_pt
Pointer to the default assembly handler.
Definition: problem.h:197
bool Always_take_one_newton_step
Boolean to indicate whether a Newton step should be taken even if the initial residuals are below the...
Definition: problem.h:2076
void setup_base_mesh_info_after_pruning()
Helper function to re-setup the Base_mesh enumeration (used during load balancing) after pruning...
Definition: problem.cc:19895
Vector< double * > Dof_pt
Vector of pointers to dofs.
Definition: problem.h:551
void load_balance()
Balance the load of a (possibly non-uniformly refined) problem that has already been distributed...
Definition: problem.h:1343
unsigned nrow_local() const
access function for the num of local rows on this processor.
DoubleVectorHaloScheme * Halo_scheme_pt
Pointer to the halo scheme for any global vectors that have the Dof_distribution. ...
Definition: problem.h:574
virtual void set_initial_condition()
Set initial condition (incl previous timesteps). We need to establish this interface because I...
Definition: problem.h:1172
void enable_mass_matrix_reuse()
Function that allows the reuse of the mass matrix.
Definition: dg_elements.h:217
virtual void actions_before_newton_convergence_check()
Any actions that are to be performed before the residual is checked in the Newton method...
Definition: problem.h:1031
void setup_halo_dofs(const std::map< unsigned, double *> &halo_data_pt, Vector< double *> &halo_dof_pt)
Function that sets up a vector of pointers to halo data, index using the scheme in Local_index...
double DTSF_min_decrease
Minimum allowed decrease of dt between time-steps in adaptive schemes. Lower scaling values will reje...
Definition: problem.h:711
virtual void actions_before_read_unstructured_meshes()
Actions that are to be performed before reading in restart data for problems involving unstructured b...
Definition: problem.h:1078
bool is_steady() const
Flag to indicate if a timestepper has been made steady (possibly temporarily to switch off time-depen...
Definition: timesteppers.h:375
unsigned self_test()
Self-test: Check meshes and global data. Return 0 for OK.
Definition: problem.cc:13080
bool position_is_pinned(const unsigned &i)
Test whether the i-th coordinate is pinned, 0: false; 1: true.
Definition: nodes.h:1685
A vector in the mathematical sense, initially developed for linear algebra type applications. If MPI then this vector can be distributed - its distribution is described by the LinearAlgebraDistribution object at Distribution_pt. Data is stored in a C-style pointer vector (double*)
Definition: double_vector.h:61
virtual void actions_before_distribute()
Actions to be performed before a (mesh) distribution.
Definition: problem.h:1099
unsigned long nrow() const
Return the number of rows of the matrix.
Definition: matrices.h:992
void describe_dofs(std::ostream &out, const std::string &current_string) const
Function to describe the dofs of the Mesh. The ostream specifies the output stream to which the descr...
Definition: mesh.cc:648
bool are_hessian_products_calculated_analytically()
Function to determine whether the hessian products are calculated analytically.
Definition: problem.h:301
LinearSolver * Mass_matrix_solver_for_explicit_timestepper_pt
Definition: problem.h:182
bool Problem_is_nonlinear
Boolean flag indicating if we&#39;re dealing with a linear or nonlinear Problem – if set to false the Ne...
Definition: problem.h:625
void get_bifurcation_eigenfunction(Vector< DoubleVector > &eigenfunction)
Return the eigenfunction calculated as part of a bifurcation tracking process. If we are not tracking...
Definition: problem.cc:10000
long synchronise_eqn_numbers(const bool &assign_local_eqn_numbers=true)
Classify any non-classified nodes into halo/haloed and synchronise equation numbers. Return the total number of degrees of freedom in the overall problem.
Definition: problem.cc:16589
A class that is used to define the functions used to assemble and invert the mass matrix when taking ...
RefineableElement * object_pt() const
Return the pointer to the object (RefineableElement) represented by the tree.
Definition: tree.h:102
unsigned nsub_mesh() const
Return number of submeshes.
Definition: problem.h:1289
bool Store_local_dof_pt_in_elements
Boolean to indicate whether local dof pointers should be stored in the elements.
Definition: problem.h:229
unsigned long assign_global_eqn_numbers(Vector< double *> &Dof_pt)
Assign the global equation numbers in the Data stored at the nodes and also internal element Data...
Definition: mesh.cc:614
virtual void actions_after_newton_step()
Any actions that are to be performed after each individual Newton step. Most likely to be used for di...
Definition: problem.h:1041
void get_all_halo_data(std::map< unsigned, double *> &map_of_halo_data)
Get pointers to all possible halo data indexed by global equation number in a map.
Definition: problem.cc:16277
bool use_triangulateio_restart() const
const access for Use_triangulateio_restart.
AssemblyHandler * Assembly_handler_pt
Definition: problem.h:188
void flush_element_and_node_storage()
Flush storage for elements and nodes by emptying the vectors that store the pointers to them...
Definition: mesh.h:427
void activate_bifurcation_tracking(double *const &parameter_pt, const DoubleVector &eigenvector, const bool &block_solve=true)
Activate generic bifurcation tracking for a single (real) eigenvalue where the initial guess for the ...
Definition: problem.cc:10038
unsigned Desired_newton_iterations_ds
The desired number of Newton Steps to reach convergence at each step along the arc.
Definition: problem.h:773
void calculate_continuation_derivatives(double *const &parameter_pt)
A function to calculate the derivatives wrt the arc-length. This version of the function actually doe...
Definition: problem.cc:9645
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:1910
void activate_hopf_tracking(double *const &parameter_pt, const bool &block_solve=true)
Turn on Hopf bifurcation tracking using the augmented system specified in the HopfHandler class...
Definition: problem.cc:10136
ExplicitTimeStepper *& explicit_time_stepper_pt()
Return a pointer to the explicit timestepper.
Definition: problem.h:1489
A Base class for explicit timesteppers.
void deactivate_bifurcation_tracking()
Deactivate all bifuraction tracking, by reseting the assembly handler to the default.
Definition: problem.h:2235
virtual void actions_after_newton_solve()
Any actions that are to be performed after a complete Newton solve, e.g. post processing. CAREFUL: This step should (and if the FD-based LinearSolver FD_LU is used, must) only update values that are pinned!
Definition: problem.h:1021
Vector< Problem * > Copy_of_problem_pt
Vector of pointers to copies of the problem used in adaptive bifurcation tracking problems (ALH: TEMP...
Definition: problem.h:238
void dump_triangulateio(std::ostream &dump_file)
Dump the triangulateio structure to a dump file and record boundary coordinates of boundary nodes...
unsigned Parallel_sparse_assemble_previous_allocation
The amount of data allocated during the previous parallel sparse assemble. Used to optimise the next ...
Definition: problem.h:946
void solve_eigenproblem(const unsigned &n_eval, Vector< std::complex< double > > &eigenvalue, Vector< DoubleVector > &eigenvector, const bool &steady=true)
Get derivative of an element in the problem wrt a global parameter, used in continuation problems...
Definition: problem.cc:8283
void parallel_sparse_assemble(const LinearAlgebraDistribution *const &dist_pt, Vector< int * > &column_or_row_index, Vector< int * > &row_or_column_start, Vector< double * > &value, Vector< unsigned > &nnz, Vector< double * > &residuals)
Helper method to assemble CRDoubleMatrices from distributed on multiple processors.
Definition: problem.cc:6552
unsigned nnode() const
Return the number of nodes.
Definition: elements.h:2146
Base class for time-stepping schemes. Timestepper provides an approximation of the temporal derivativ...
Definition: timesteppers.h:219
void delete_all_external_storage()
Wipe the storage for all externally-based elements.
Definition: mesh.cc:8816
Vector< unsigned > Last_el_plus_one_for_assembly
Last element (plus one) to be assembled by given processor for non-distributed problem (only kept up ...
Definition: problem.h:520
A class for compressed row matrices. This is a distributable object.
Definition: matrices.h:872
bool Problem_has_been_distributed
Has the problem been distributed amongst multiple processors?
Definition: problem.h:951
LinearAlgebraDistribution * distribution_pt() const
access to the LinearAlgebraDistribution
void globally_convergent_line_search(const Vector< double > &x_old, const double &half_residual_squared_old, DoubleVector &gradient, DoubleVector &newton_dir, double &half_residual_squared, const double &stpmax)
Line search helper for globally convergent Newton method.
Definition: problem.cc:9096
virtual void actions_after_implicit_timestep()
Actions that should be performed after each implicit time step. This is needed when one wants to solv...
Definition: problem.h:1053
void send_refinement_info_helper(Vector< unsigned > &old_domain_for_base_element, Vector< unsigned > &new_domain_for_base_element, const unsigned &max_refinement_level_overall, std::map< unsigned, Vector< unsigned > > &refinement_info_for_root_local, Vector< Vector< Vector< unsigned > > > &refinement_info_for_root_elements)
Send refinement information between processors.
Definition: problem.cc:18169
bool Doc_imbalance_in_parallel_assembly
Boolean to switch on assessment of load imbalance in parallel assembly of distributed problem...
Definition: problem.h:506
A general mesh class.
Definition: mesh.h:74
unsigned uniform_refinement_level_when_pruned() const
Level to which the mesh was uniformly refined when it was pruned (const version)
virtual void set_error_weights()
Set the weights for the error computation, (currently empty – overwrite for specific scheme) ...
Definition: timesteppers.h:598
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:1946
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:2328
void restore_dof_values()
Restore the stored values of the degrees of freedom.
Definition: problem.cc:8610
void adapt()
Adapt problem: Perform mesh adaptation for (all) refineable (sub)mesh(es), based on their own error e...
Definition: problem.h:2741
void build(const LinearAlgebraDistribution *distribution_pt, const unsigned &ncol, const Vector< double > &value, const Vector< int > &column_index, const Vector< int > &row_start)
build method: vector of values, vector of column indices, vector of row starts and number of rows and...
Definition: matrices.cc:1692
double Minimum_dt_but_still_proceed
If Minimum_dt_but_still_proceed positive, then dt will not be reduced below this value during adaptiv...
Definition: problem.h:718
TreeRoot *& root_pt()
Return pointer to root of the tree.
Definition: tree.h:143
void rebuild_global_mesh()
If one of the submeshes has changed (e.g. by mesh adaptation) we need to update the global mesh...
Definition: problem.cc:1517
double & time()
Return the current value of continuous time.
Definition: problem.cc:11411
Node * halo_node_pt(const unsigned &p, const unsigned &j)
Access fct to the j-th halo node in this Mesh whose non-halo counterpart is held on processor p...
Definition: mesh.h:1577
virtual void actions_before_implicit_timestep()
Actions that should be performed before each implicit time step. This is needed when one wants to sol...
Definition: problem.h:1047
std::string to_string(T object, unsigned float_precision=8)
Conversion function that should work for anything with operator<< defined (at least all basic types)...
bool Scale_arc_length
Boolean to control whether arc-length should be scaled.
Definition: problem.h:724
double Minimum_ds
Minimum desired value of arc-length.
Definition: problem.h:776
void add_to_dofs(const double &lambda, const DoubleVector &increment_dofs)
Add lambda x incremenet_dofs[l] to the l-th dof.
Definition: problem.cc:3520
virtual void complete_setup_of_dependencies()
Complete the setup of any additional dependencies that the element may have. Empty virtual function t...
Definition: elements.h:969
double max() const
returns the maximum coefficient
void build_without_copy(const unsigned &ncol, const unsigned &nnz, double *value, int *column_index, int *row_start)
keeps the existing distribution and just matrix that is stored without copying the matrix data ...
Definition: matrices.cc:1731
virtual void read(std::ifstream &restart_file)
Read solution from restart file.
Definition: mesh.cc:1070
An oomph-lib wrapper to the MPI_Comm communicator object. Just contains an MPI_Comm object (which is ...
Definition: communicator.h:57
void resize(const unsigned long &n)
Definition: matrices.h:511
void bifurcation_adapt_doc_errors(const unsigned &bifurcation_type)
A function that is used to document the errors used in the adaptive solution of bifurcation problems...
Definition: problem.cc:13444