helmholtz_geometric_multigrid.h
Go to the documentation of this file.
1 // Include guards
2 #ifndef OOMPH_HELMHOLTZ_GEOMETRIC_MULTIGRID_HEADER
3 #define OOMPH_HELMHOLTZ_GEOMETRIC_MULTIGRID_HEADER
4 
5 // Config header generated by autoconfig
6 #ifdef HAVE_CONFIG_H
7 #include <config.h>
8 #endif
9 
10 // Oomph-lib headers
11 #include "generic/problem.h"
12 #include "generic/matrices.h"
13 #include "generic/preconditioner.h"
14 
15 // Include the complex smoother
16 #include "complex_smoother.h"
17 
18 // Namespace extension
19 namespace oomph
20 {
21 
22 //======================================================================
23 /// HelmholtzMGProblem class; subclass of Problem
24 //======================================================================
25  class HelmholtzMGProblem : public virtual Problem
26  {
27 
28  public:
29 
30  /// Constructor. Initialise pointers to coarser and finer levels
32  {}
33 
34  /// Destructor (empty)
36  {}
37 
38  /// \short This function needs to be implemented in the derived problem:
39  /// Returns a pointer to a new object of the same type as the derived
40  /// problem
42 
43  /// \short Function to get a pointer to the mesh we will be working
44  /// with. If there are flux elements present in the mesh this will
45  /// be overloaded to return a pointer to the bulk mesh otherwise
46  /// it can be overloaded to point to the global mesh but it must
47  /// be of type RefineableMeshBase
49 
50  }; // End of HelmholtzMGProblem class
51 
52 
53 //////////////////////////////////////////////////////////
54 //////////////////////////////////////////////////////////
55 //////////////////////////////////////////////////////////
56 
57 
58 //======================================================================
59 // MG solver class
60 //======================================================================
61  template<unsigned DIM>
62  class HelmholtzMGPreconditioner : public BlockPreconditioner<CRDoubleMatrix>
63  {
64 
65  public:
66 
67  /// \short typedef for a function that returns a pointer to an object
68  /// of the class HelmholtzSmoother to be used as the pre-smoother
69  typedef HelmholtzSmoother* (*PreSmootherFactoryFctPt)();
70 
71  /// \short typedef for a function that returns a pointer to an object
72  /// of the class HelmholtzSmoother to be used as the post-smoother
73  typedef HelmholtzSmoother* (*PostSmootherFactoryFctPt)();
74 
75  /// Access function to set the pre-smoother creation function.
77  PreSmootherFactoryFctPt pre_smoother_fn)
78  {
79  // Assign the function pointer
80  Pre_smoother_factory_function_pt=pre_smoother_fn;
81  }
82 
83  /// Access function to set the post-smoother creation function.
85  PostSmootherFactoryFctPt post_smoother_fn)
86  {
87  // Assign the function pointer
88  Post_smoother_factory_function_pt=post_smoother_fn;
89  }
90 
91  /// \short Constructor: Set up default values for number of V-cycles
92  /// and pre- and post-smoothing steps.
95  Pre_smoother_factory_function_pt(0),
96  Post_smoother_factory_function_pt(0),
97  Mg_problem_pt(mg_problem_pt),
98  Tolerance(1.0e-09),
99  Npre_smooth(2),
100  Npost_smooth(2),
101  Nvcycle(1),
102  Doc_time(true),
103  Suppress_v_cycle_output(false),
104  Suppress_all_output(false),
105  Has_been_setup(false),
106  Has_been_solved(false),
107  Stream_pt(0),
108  Alpha_shift(0.0)
109  {
110  // Set the pointer to the finest level as the first
111  // entry in Mg_hierarchy_pt
112  Mg_hierarchy_pt.push_back(Mg_problem_pt);
113  } // End of HelmholtzMGPreconditioner
114 
115  /// Delete any dynamically allocated data
117  {
118  // Run the function written to clean up the memory
119  clean_up_memory();
120  } // End of ~HelmholtzMGPreconditioner
121 
122  /// Clean up anything that needs to be cleaned up
124  {
125  // We only need to destroy data if the solver has been set up and
126  // the data hasn't already been cleared
127  if (Has_been_setup)
128  {
129  // Loop over all of the levels in the hierarchy
130  for (unsigned i=0;i<Nlevel-1;i++)
131  {
132  // Delete the pre-smoother associated with this level
133  delete Pre_smoothers_storage_pt[i];
134 
135  // Make it a null pointer
136  Pre_smoothers_storage_pt[i]=0;
137 
138  // Delete the post-smoother associated with this level
139  delete Post_smoothers_storage_pt[i];
140 
141  // Make it a null pointer
142  Post_smoothers_storage_pt[i]=0;
143 
144  // Loop over the real and imaginary parts of the system matrix
145  // associated with the i-th level
146  for (unsigned j=0;j<2;j++)
147  {
148  // Delete the real/imaginary part of the system matrix
149  delete Mg_matrices_storage_pt[i][j];
150 
151  // Make it a null pointer
152  Mg_matrices_storage_pt[i][j]=0;
153  }
154  }
155 
156  // Delete the expanded matrix associated with the problem on the
157  // coarsest level
158  delete Coarsest_matrix_mg_pt;
159 
160  // Make it a null pointer
161  Coarsest_matrix_mg_pt=0;
162 
163  // Loop over all but the coarsest of the levels in the hierarchy
164  for (unsigned i=0;i<Nlevel-1;i++)
165  {
166  // Delete the interpolation matrix associated with the i-th level
167  delete Interpolation_matrices_storage_pt[i];
168 
169  // Make it a null pointer
170  Interpolation_matrices_storage_pt[i]=0;
171 
172  // Delete the restriction matrix associated with the i-th level
173  delete Restriction_matrices_storage_pt[i];
174 
175  // Make it a null pointer
176  Restriction_matrices_storage_pt[i]=0;
177  }
178 
179  // Everything has been deleted now so we need to indicate that the
180  // solver is not set up
181  Has_been_setup=false;
182  }
183  } // End of clean_up_memory
184 
185  /// Access function for the variable Tolerance (lvalue)
186  double& tolerance()
187  {
188  // Return the variable, Tolerance
189  return Tolerance;
190  } // End of tolerance
191 
192  /// Function to change the value of the shift
193  double& alpha_shift()
194  {
195  // Return the alpha shift value
196  return Alpha_shift;
197  } // End of alpha_shift
198 
199  /// Disable time documentation
201  {
202  // Set the value of Doc_time to false
203  Doc_time=false;
204  } // End of disable_doc_time
205 
206  /// \short Disable all output from mg_solve apart from the number of
207  /// V-cycles used to solve the problem
209  {
210  // Set the value of Doc_time to false
211  Doc_time=false;
212 
213  // Enable the suppression of output from the V-cycle
214  Suppress_v_cycle_output=true;
215  } // End of disable_v_cycle_output
216 
217  /// \short Suppress anything that can be suppressed, i.e. any timings.
218  /// Things like mesh adaptation can not however be silenced using this
220  {
221  // Set the value of Doc_time to false
222  Doc_time=false;
223 
224  // Enable the suppression of output from the V-cycle
225  Suppress_v_cycle_output=true;
226 
227  // Enable the suppression of everything
228  Suppress_all_output=true;
229 
230  // Store the output stream pointer
231  Stream_pt=oomph_info.stream_pt();
232 
233  // Now set the oomph_info stream pointer to the null stream to
234  // disable all possible output
236  } // End of disable_output
237 
238  /// Enable time documentation
240  {
241  // Set the value of Doc_time to true
242  Doc_time=true;
243  } // End of enable_doc_time
244 
245  /// \short Enable the output of the V-cycle timings and other output
247  {
248  // Enable time documentation
249  Doc_time=true;
250 
251  // Enable output from the MG solver
252  Suppress_v_cycle_output=false;
253  } // End of enable_v_cycle_output
254 
255  /// \short Enable the output from anything that could have been suppressed
257  {
258  // Enable time documentation
259  Doc_time=true;
260 
261  // Enable output from everything during the full setup of the solver
262  Suppress_all_output=false;
263 
264  // Enable output from the MG solver
265  Suppress_v_cycle_output=false;
266  } // End of enable_output
267 
268  /// \short Suppress the output of both smoothers and SuperLU
270  {
271  // Loop over all levels of the hierarchy
272  for (unsigned i=0;i<Nlevel-1;i++)
273  {
274  // Disable time documentation on each level (for each pre-smoother)
275  Pre_smoothers_storage_pt[i]->disable_doc_time();
276 
277  // Disable time documentation on each level (for each post-smoother)
278  Post_smoothers_storage_pt[i]->disable_doc_time();
279  }
280 
281  // We only need a direct solve on the coarsest level so this is the
282  // only place we need to silence SuperLU
283  Coarsest_matrix_mg_pt->linear_solver_pt()->disable_doc_time();
284  } // End of disable_smoother_and_superlu_doc_time
285 
286  /// Return the number of post-smoothing iterations (lvalue)
287  unsigned& npost_smooth()
288  {
289  // Return the number of post-smoothing iterations to be done on each
290  // level of the hierarchy
291  return Npost_smooth;
292  } // End of npost_smooth
293 
294  /// Return the number of pre-smoothing iterations (lvalue)
295  unsigned& npre_smooth()
296  {
297  // Return the number of pre-smoothing iterations to be done on each
298  // level of the hierarchy
299  return Npre_smooth;
300  } // End of npre_smooth
301 
302  /// \short Pre-smoother: Perform 'max_iter' smoothing steps on the
303  /// linear system Ax=b with current RHS vector, b, starting with
304  /// current solution vector, x. Return the residual vector r=b-Ax.
305  /// Uses the default smoother (set in the HelmholtzMGProblem constructor)
306  /// which can be overloaded for a specific problem.
307  void pre_smooth(const unsigned& level)
308  {
309  // Run pre-smoother 'max_iter' times
310  Pre_smoothers_storage_pt[level]->
311  complex_smoother_solve(Rhs_mg_vectors_storage[level],
312  X_mg_vectors_storage[level]);
313 
314  // Calculate the residual vector on this level
315  residual_norm(level);
316  } // End of pre_smooth
317 
318  /// \short Post-smoother: Perform max_iter smoothing steps on the
319  /// linear system Ax=b with current RHS vector, b, starting with
320  /// current solution vector, x. Uses the default smoother (set in
321  /// the HelmholtzMGProblem constructor) which can be overloaded for specific
322  /// problem.
323  void post_smooth(const unsigned& level)
324  {
325  // Run post-smoother 'max_iter' times
326  Post_smoothers_storage_pt[level]->
327  complex_smoother_solve(Rhs_mg_vectors_storage[level],
328  X_mg_vectors_storage[level]);
329 
330  // Calculate the residual vector on this level
331  residual_norm(level);
332  } // End of post_smooth
333 
334  /// \short Return norm of residual r=b-Ax and the residual vector itself
335  /// on the level-th level
336  double residual_norm(const unsigned& level)
337  {
338  // Loop over the real and imaginary part of the residual vector on
339  // the given level
340  for (unsigned j=0;j<2;j++)
341  {
342  // And zero the entries of residual
343  Residual_mg_vectors_storage[level][j].initialise(0.0);
344  }
345 
346  // Return the norm of the residual
347  return residual_norm(level,Residual_mg_vectors_storage[level]);
348  } // End of residual_norm
349 
350  /// Calculate the norm of the residual vector, r=b-Ax
351  double residual_norm(const unsigned& level,Vector<DoubleVector>& residual);
352 
353  /// \short Function to create the fully expanded system matrix on the
354  /// coarsest level
355  void setup_coarsest_level_structures();
356 
357  /// \short Call the direct solver (SuperLU) to solve the problem exactly.
358  // The result is placed in X_mg
360  {
361  // Concatenate the vectors in X_mg into one vector, coarsest_x_mg
362  DoubleVectorHelpers::concatenate(X_mg_vectors_storage[Nlevel-1],
363  Coarsest_x_mg);
364 
365  // Concatenate the vectors in Rhs_mg into one vector, coarsest_rhs_mg
366  DoubleVectorHelpers::concatenate(Rhs_mg_vectors_storage[Nlevel-1],
367  Coarsest_rhs_mg);
368 
369  // Get solution by direct solve:
370  Coarsest_matrix_mg_pt->solve(Coarsest_rhs_mg,Coarsest_x_mg);
371 
372  // Split the solution vector into a vector of DoubleVectors and store it
373  DoubleVectorHelpers::split(Coarsest_x_mg,X_mg_vectors_storage[Nlevel-1]);
374  } // End of direct_solve
375 
376  /// \short Builds a CRDoubleMatrix that is used to interpolate the
377  /// residual between levels. The transpose can be used as the full
378  /// weighting restriction.
379  void interpolation_matrix_set(const unsigned& level,
380  double* value,
381  int* col_index,
382  int* row_st,
383  unsigned& ncol,
384  unsigned& nnz)
385  {
386  // Dynamically allocate the interpolation matrix
387  Interpolation_matrices_storage_pt[level]=new CRDoubleMatrix;
388 
389  // Build the matrix
390  Interpolation_matrices_storage_pt[level]->
391  build_without_copy(ncol,nnz,value,col_index,row_st);
392 
393  } // End of interpolation_matrix_set
394 
395  /// \short Builds a CRDoubleMatrix that is used to interpolate the
396  /// residual between levels. The transpose can be used as the full
397  /// weighting restriction.
398  void interpolation_matrix_set(const unsigned& level,
399  Vector<double>& value,
400  Vector<int>& col_index,
401  Vector<int>& row_st,
402  unsigned& ncol,
403  unsigned& nrow)
404  {
405  // Dynamically allocate the interpolation matrix
406  Interpolation_matrices_storage_pt[level]=new CRDoubleMatrix;
407 
408  // Make the distribution pointer
409  LinearAlgebraDistribution* dist_pt=
410  new LinearAlgebraDistribution(Mg_hierarchy_pt[level]->
411  communicator_pt(),nrow,false);
412 
413 #ifdef PARANOID
414 #ifdef OOMPH_HAS_MPI
415  // Set up the warning messages
416  std::string warning_message="Setup of interpolation matrix distribution ";
417  warning_message+="has not been tested with MPI.";
418 
419  // If we're not running the code in serial
420  if (dist_pt->communicator_pt()->nproc()>1)
421  {
422  // Throw a warning
423  OomphLibWarning(warning_message,
424  OOMPH_CURRENT_FUNCTION,
425  OOMPH_EXCEPTION_LOCATION);
426  }
427 #endif
428 #endif
429 
430  // Build the matrix itself
431  Interpolation_matrices_storage_pt[level]->
432  build(dist_pt,ncol,value,col_index,row_st);
433 
434  // Delete the newly created distribution pointer
435  delete dist_pt;
436 
437  // Make it a null pointer
438  dist_pt=0;
439 
440  } // End of interpolation_matrix_set
441 
442  /// \short Builds a CRDoubleMatrix on each level that is used to
443  /// restrict the residual between levels. The transpose can be used
444  /// as the interpolation matrix
446  {
447  for (unsigned i=0;i<Nlevel-1;i++)
448  {
449  // Dynamically allocate the restriction matrix
450  Restriction_matrices_storage_pt[i]=new CRDoubleMatrix;
451 
452  // Set the restriction matrix to be the transpose of the
453  // interpolation matrix
454  Interpolation_matrices_storage_pt[i]->
455  get_matrix_transpose(Restriction_matrices_storage_pt[i]);
456  }
457  } // End of set_restriction_matrices_as_interpolation_transposes
458 
459  /// \short Restrict residual (computed on level-th MG level) to the next
460  /// coarser mesh and stick it into the coarse mesh RHS vector.
461  void restrict_residual(const unsigned& level);
462 
463  /// \short Interpolate solution at current level onto next finer mesh
464  /// and correct the solution x at that level
465  void interpolate_and_correct(const unsigned& level);
466 
467  /// \short Given the son_type of an element and a local node number
468  /// j in that element with nnode_1d nodes per coordinate direction,
469  /// return the local coordinate s in its father element. Needed in
470  /// the setup of the interpolation matrices
471  void level_up_local_coord_of_node(const int& son_type,
472  Vector<double>& s);
473 
474  /// \short Setup the interpolation matrix on each level
475  void setup_interpolation_matrices();
476 
477  /// \short Setup the interpolation matrix on each level (used for
478  /// unstructured meshes)
479  void setup_interpolation_matrices_unstructured();
480 
481  /// \short Setup the transfer matrices on each level
482  void setup_transfer_matrices();
483 
484  /// \short Do a full setup (assumes everything will be setup around the
485  /// HelmholtzMGProblem pointer given in the constructor)
486  void full_setup();
487 
488  /// \short Function applies MG to the vector r for a full solve
490  {
491  // Split up the RHS vector into DoubleVectors, whose entries are
492  // arranged to match the matrix blocks and assign it
493  this->get_block_vectors(r,Rhs_mg_vectors_storage[0]);
494 
495  // Split up the solution vector into DoubleVectors, whose entries are
496  // arranged to match the matrix blocks and assign it
497  this->get_block_vectors(z,X_mg_vectors_storage[0]);
498 
499  // Run the MG method and assign the solution to z
500  this->mg_solve(X_mg_vectors_storage[0]);
501 
502  // Copy solution in block vectors block_z back to z
503  this->return_block_vectors(X_mg_vectors_storage[0],z);
504 
505  // Only output if the V-cycle output isn't suppressed
506  if (!(this->Suppress_v_cycle_output))
507  {
508  // Notify user that the hierarchy of levels is complete
509  oomph_info << "\n=========="
510  << "Multigrid Preconditioner Solve Complete"
511  << "=========" << std::endl;
512  }
513 
514  // Only enable and assign the stream pointer again if we originally
515  // suppressed everything otherwise it won't be set yet
516  if (this->Suppress_all_output)
517  {
518  // Now enable the stream pointer again
519  oomph_info.stream_pt()=this->Stream_pt;
520  }
521  } // End of preconditioner_solve
522 
523  /// Number of iterations
524  unsigned iterations() const
525  {
526  // Return the number of V-cycles which have been done
527  return V_cycle_counter;
528  } // End of iterations
529 
530  /// \short Use the version in the Preconditioner base class for the
531  /// alternative setup function that takes a matrix pointer as an argument.
532  using Preconditioner::setup;
533 
534  private:
535 
536  /// Function to create pre-smoothers
537  PreSmootherFactoryFctPt Pre_smoother_factory_function_pt;
538 
539  /// Function to create post-smoothers
540  PostSmootherFactoryFctPt Post_smoother_factory_function_pt;
541 
542  /// \short Do the actual solve -- this is called through the pure virtual
543  /// solve function in the LinearSolver base class. The function is stored
544  /// as protected to allow the HelmholtzMGPreconditioner derived class to use the
545  /// solver
546  void mg_solve(Vector<DoubleVector>& result);
547 
548  /// \short Function to ensure the block form of the Jacobian matches
549  /// the form described, i.e. we should have:
550  /// |-----|------|
551  /// | A_r | -A_c |
552  /// A = |-----|------|.
553  /// | A_c | A_r |
554  /// |-----|------|
555  void block_preconditioner_self_test();
556 
557  /// \short Function to set up the hierachy of levels. Creates a vector
558  /// of pointers to each MG level
559  void setup()
560  {
561  // Run the full setup
562  this->full_setup();
563 
564  // Only enable and assign the stream pointer again if we originally
565  // suppressed everything otherwise it won't be set yet
566  if (this->Suppress_all_output)
567  {
568  // Now enable the stream pointer again
569  oomph_info.stream_pt()=this->Stream_pt;
570  }
571  } // End of setup
572 
573  /// \short Function to set up the hierachy of levels. Creates a vector
574  /// of pointers to each MG level
575  void setup_mg_hierarchy();
576 
577  /// \short Function to set up the hierachy of levels. Creates a vector
578  /// of pointers to each MG level
579  void setup_mg_structures();
580 
581  /// \short Estimate the value of the parameter h on the level-th problem
582  /// in the hierarchy.
583  void maximum_edge_width(const unsigned& level,double& h);
584 
585  /// \short Function to set up all of the smoothers once the system matrices
586  /// have been set up
587  void setup_smoothers();
588 
589  /// Pointer to the MG problem (deep copy)
591 
592  /// Vector containing pointers to problems in hierarchy
594 
595  /// \short Vector of vectors to store the system matrices. The i-th
596  /// entry in this vector contains a vector of length two. The first
597  /// entry of which contains the real part of the system matrix which
598  /// we refer to as A_r and the second entry contains the imaginary
599  /// part of the system matrix which we refer to as A_c. That is to say,
600  /// the true system matrix is given by A = A_r + iA_c
602 
603  /// \short Stores the system matrix on the coarest level in the fully
604  /// expanded format:
605  /// |-----|------|
606  /// | A_r | -A_c |
607  /// A = |-----|------|.
608  /// | A_c | A_r |
609  /// |-----|------|
610  /// Note: this is NOT the same as A = A_r + iA_c
612 
613  /// \short Assuming we're solving the system Ax=b, this vector will
614  /// contain the expanded solution vector on the coarsest level of the
615  /// heirarchy. This will have the form:
616  /// |-----|
617  /// | x_r |
618  /// x = |-----|.
619  /// | x_c |
620  /// |-----|
622 
623  /// \short Assuming we're solving the system Ax=b, this vector will
624  /// contain the expanded solution vector on the coarsest level of the
625  /// heirarchy. This will have the form:
626  /// |-----|
627  /// | b_r |
628  /// b = |-----|.
629  /// | b_c |
630  /// |-----|
632 
633  /// Vector to store the interpolation matrices
635 
636  /// Vector to store the restriction matrices
638 
639  /// \short Vector of vectors to store the solution vectors (X_mg) in two
640  /// parts; the real and imaginary. To access the real part of the solution
641  /// vector on the i-th level we need to access X_mg_vectors_storage[i][0]
642  /// while accessing X_mg_vectors_storage[i][1] will give us the
643  /// corresponding imaginary part
645 
646  /// \short Vector of vectors to store the RHS vectors. This uses the same
647  /// format as the X_mg_vectors_storage vector
649 
650  /// \short Vector to vectors to store the residual vectors. This uses
651  /// the same format as the X_mg_vectors_storage vector
653 
654  /// Vector to store the pre-smoothers
656 
657  /// Vector to store the post-smoothers
659 
660  /// \short Vector to storage the maximum edge width of each mesh. We only
661  /// need the maximum edge width on levels where we use a smoother to
662  /// determine the value of kh
664 
665  /// The value of the wavenumber, k
666  double Wavenumber;
667 
668  /// The tolerance of the multigrid preconditioner
669  double Tolerance;
670 
671  /// The number of levels in the multigrid heirachy
672  unsigned Nlevel;
673 
674  /// Number of pre-smoothing steps
675  unsigned Npre_smooth;
676 
677  /// Number of post-smoothing steps
678  unsigned Npost_smooth;
679 
680  /// Maximum number of V-cycles
681  unsigned Nvcycle;
682 
683  /// Pointer to counter for V-cycles
684  unsigned V_cycle_counter;
685 
686  /// Indicates whether or not time documentation should be used
687  bool Doc_time;
688 
689  /// Indicates whether or not the V-cycle output should be suppressed.
691 
692  /// If this is set to true then all output from the solver is suppressed
694 
695  /// Boolean variable to indicate whether or not the solver has been setup
697 
698  /// \short Boolean variable to indicate whether or not the problem was
699  /// successfully solved
701 
702  /// Pointer to the output stream -- defaults to std::cout
703  std::ostream* Stream_pt;
704 
705  /// Temporary version of the shift -- to run bash scripts
706  double Alpha_shift;
707  };
708 
709  //========================================================================
710  /// \short Calculating the residual r=b-Ax in the complex case requires
711  /// more care than the real case. To calculate the residual vector we
712  /// split A, x and b into their complex components:
713  /// r = b - A*x,
714  /// = (b_r + i*b_c) - (A_r + i*A_c)*(x_r + i*x_c),
715  /// = [b_r - A_r*x_r + A_c*x_c] + i*[b_c - A_r*x_c - A_c*x_r],
716  /// ==> real(r) = b_r - A_r*x_r + A_c*x_c,
717  /// & imag(r) = b_c - A_r*x_c - A_c*x_r.
718  //========================================================================
719  template<unsigned DIM>
721  residual_norm(const unsigned& level,Vector<DoubleVector>& residual)
722  {
723  // Number of rows in each block vector
724  unsigned n_rows=X_mg_vectors_storage[level][0].nrow();
725 
726 #ifdef PARANOID
727  // PARANOID check - if the residual vector doesn't have length 2 it cannot
728  // be used here since we need two vectors corresponding to the real and
729  // imaginary part of the residual
730  if (residual.size()!=2)
731  {
732  // Throw an error if the residual vector doesn't have the correct length
733  throw OomphLibError("This residual vector must have length 2. ",
734  OOMPH_CURRENT_FUNCTION,
735  OOMPH_EXCEPTION_LOCATION);
736  }
737  if (residual[0].nrow()!=residual[1].nrow())
738  {
739  // Create an output stream
740  std::ostringstream error_message_stream;
741 
742  // Store the error message
743  error_message_stream << "Residual[0] has length: " << residual[0].nrow()
744  << "\nResidual[1] has length: " << residual[1].nrow()
745  << "\nThis method requires that the constituent "
746  << "DoubleVectors in residual have the same length. "
747  << std::endl;
748 
749  // Throw an error
750  throw OomphLibError(error_message_stream.str(),
751  OOMPH_CURRENT_FUNCTION,
752  OOMPH_EXCEPTION_LOCATION);
753  }
754 #endif
755 
756  // Loop over the block vectors
757  for (unsigned i=0;i<2;i++)
758  {
759  // Start by setting the distribution of the residuals vector if
760  // it is not set up
761  if (!residual[i].distribution_built())
762  {
763  // Set up distribution
764  LinearAlgebraDistribution dist(Mg_hierarchy_pt[level]->
765  communicator_pt(),n_rows,false);
766 
767  // Build the distribution
768  residual[i].build(&dist,0.0);
769  }
770  // Otherwise just zero the entries of residual
771  else
772  {
773 #ifdef PARANOID
774  // PARANOID check - if the residuals are distributed then this method
775  // cannot be used, a distributed residuals can only be assembled by
776  // get_jacobian(...) for CRDoubleMatrices
777  if (residual[i].distributed())
778  {
779  throw OomphLibError
780  ("This method can only assemble a non-distributed residuals vector ",
781  OOMPH_CURRENT_FUNCTION,
782  OOMPH_EXCEPTION_LOCATION);
783  }
784 #endif
785 
786  // And zero the entries of residual
787  residual[i].initialise(0.0);
788  }
789  }
790 
791  // Store the pointer to the distribution of Matrix_real_pt (the same as
792  // Matrix_imag_pt presumably so we only need to use one)
793  LinearAlgebraDistribution* dist_pt=
794  Mg_matrices_storage_pt[level][0]->distribution_pt();
795 
796  // Create 4 temporary vectors to store the various matrix-vector products
797  // required. The appropriate combination has been appended to the name of
798  // the vector:
799  // Vector to store A_r*x_r:
800  DoubleVector temp_vec_rr(dist_pt,0.0);
801 
802  // Vector to store A_r*x_c:
803  DoubleVector temp_vec_rc(dist_pt,0.0);
804 
805  // Vector to store A_c*x_r:
806  DoubleVector temp_vec_cr(dist_pt,0.0);
807 
808  // Vector to store A_c*x_c:
809  DoubleVector temp_vec_cc(dist_pt,0.0);
810 
811  // We can't delete the distribution pointer because the Jacobian on the
812  // finest level is using it but we can make it a null pointer
813  dist_pt=0;
814 
815  // Calculate the different combinations of A*x (or A*e depending on the
816  // level in the heirarchy) in the complex case:
817  // A_r*x_r:
818  Mg_matrices_storage_pt[level][0]->
819  multiply(X_mg_vectors_storage[level][0],temp_vec_rr);
820 
821  // A_r*x_c:
822  Mg_matrices_storage_pt[level][0]->
823  multiply(X_mg_vectors_storage[level][1],temp_vec_rc);
824 
825  // A_c*x_r:
826  Mg_matrices_storage_pt[level][1]->
827  multiply(X_mg_vectors_storage[level][0],temp_vec_cr);
828 
829  // A_c*x_c:
830  Mg_matrices_storage_pt[level][1]->
831  multiply(X_mg_vectors_storage[level][1],temp_vec_cc);
832 
833  // Real part of the residual:
834  residual[0]=Rhs_mg_vectors_storage[level][0];
835  residual[0]-=temp_vec_rr;
836  residual[0]+=temp_vec_cc;
837 
838  // Imaginary part of the residual:
839  residual[1]=Rhs_mg_vectors_storage[level][1];
840  residual[1]-=temp_vec_rc;
841  residual[1]-=temp_vec_cr;
842 
843  // Get the residual norm of the real part of the residual vector
844  double norm_r=residual[0].norm();
845 
846  // Get the residual norm of the imaginary part of the residual vector
847  double norm_c=residual[1].norm();
848 
849  // Return the true norm of the residual vector which depends on the
850  // norm of the real part and the norm of the imaginary part
851  return sqrt(norm_r*norm_r+norm_c*norm_c);
852  }
853 
854  //=====================================================================
855  /// \short Check the block preconditioner framework returns the correct
856  /// system matrix
857  //=====================================================================
858  template<unsigned DIM>
860  {
861  // Start clock
862  clock_t t_bl_start=clock();
863 
864 #ifdef PARANOID
865  if (Mg_hierarchy_pt[0]->mesh_pt()==0)
866  {
867  std::stringstream err;
868  err << "Please set pointer to mesh using set_bulk_helmholtz_mesh(...).\n";
869  throw OomphLibError(err.str(),
870  OOMPH_CURRENT_FUNCTION,
871  OOMPH_EXCEPTION_LOCATION);
872  }
873 #endif
874 
875  // The preconditioner works with one mesh; set it! Since we only use the
876  // block preconditioner on the finest level, we use the mesh from that level
877  this->set_nmesh(1);
878 
879  // Elements in actual pml layer are trivially wrapped versions of
880  // their bulk counterparts. Technically they are different
881  // elements so we have to allow different element types
882  bool allow_different_element_types_in_mesh=true;
883  this->set_mesh(0,Mg_problem_pt->mesh_pt(),
884  allow_different_element_types_in_mesh);
885 
886 #ifdef PARANOID
887  // This preconditioner only works for 2 dof types
888  unsigned n_dof_types=this->ndof_types();
889  if (n_dof_types!=2)
890  {
891  std::stringstream tmp;
892  tmp << "This preconditioner only works for problems with 2 dof types\n"
893  << "Yours has " << n_dof_types;
894  throw OomphLibError(tmp.str(),
895  OOMPH_CURRENT_FUNCTION,
896  OOMPH_EXCEPTION_LOCATION);
897  }
898 #endif
899 
900  // Set up the generic block look up scheme
901  this->block_setup();
902 
903  // Extract the number of blocks.
904  unsigned nblock_types=this->nblock_types();
905 #ifdef PARANOID
906  if (nblock_types!=2)
907  {
908  std::stringstream tmp;
909  tmp << "There are supposed to be two block types.\n"
910  << "Yours has " << nblock_types;
911  throw OomphLibError(tmp.str(),
912  OOMPH_CURRENT_FUNCTION,
913  OOMPH_EXCEPTION_LOCATION);
914  }
915 #endif
916 
917  // This is how the 2x2 block matrices are extracted. We retain the sanity
918  // check (i.e. the diagonals are the same and the off-diagonals are negatives
919  // of each other in PARANOID mode. Otherwise we only extract 2 matrices
920  DenseMatrix<CRDoubleMatrix*> block_pt(nblock_types,nblock_types,0);
921  for (unsigned i=0;i<nblock_types;i++)
922  {
923  for (unsigned j=0;j<nblock_types;j++)
924  {
925  // we want...
926  block_pt(i,j)=new CRDoubleMatrix;
927  this->get_block(i,j,*block_pt(i,j));
928  }
929  }
930 
931  // Check that diagonal matrices are the same
932  //------------------------------------------
933  {
934  unsigned nnz1=block_pt(0,0)->nnz();
935  unsigned nnz2=block_pt(1,1)->nnz();
936  if (nnz1!=nnz2)
937  {
938  std::stringstream tmp;
939  tmp << "nnz of diagonal blocks doesn't match: "
940  << nnz1 << " != " << nnz2 << std::endl;
941  throw OomphLibError(tmp.str(),
942  OOMPH_CURRENT_FUNCTION,
943  OOMPH_EXCEPTION_LOCATION);
944  }
945  unsigned nrow1=block_pt(0,0)->nrow();
946  unsigned nrow2=block_pt(1,1)->nrow();
947  if (nrow1!=nrow2)
948  {
949  std::stringstream tmp;
950  tmp << "nrow of diagonal blocks doesn't match: "
951  << nrow1 << " != " << nrow2 << std::endl;
952  throw OomphLibError(tmp.str(),
953  OOMPH_CURRENT_FUNCTION,
954  OOMPH_EXCEPTION_LOCATION);
955  }
956 
957  // Check entries
958  bool fail=false;
959  double tol=1.0e-15;
960  std::stringstream tmp;
961 
962  // Check row starts
963  for (unsigned i=0;i<nrow1+1;i++)
964  {
965  if (block_pt(0,0)->row_start()[i]!=
966  block_pt(1,1)->row_start()[i])
967  {
968  fail=true;
969  tmp << "Row starts of diag matrices don't match for row " << i
970  << " : "
971  << block_pt(0,0)->row_start()[i] << " "
972  << block_pt(1,1)->row_start()[i] << " "
973  << std::endl;
974  }
975  }
976 
977  // Check values and column indices
978  for (unsigned i=0;i<nnz1;i++)
979  {
980  if (block_pt(0,0)->column_index()[i]!=
981  block_pt(1,1)->column_index()[i])
982  {
983  fail=true;
984  tmp << "Column of diag matrices indices don't match for entry " << i
985  << " : "
986  << block_pt(0,0)->column_index()[i] << " "
987  << block_pt(1,1)->column_index()[i] << " "
988  << std::endl;
989  }
990 
991  if (fabs(block_pt(0,0)->value()[i]-
992  block_pt(1,1)->value()[i])>tol)
993  {
994  fail=true;
995  tmp << "Values of diag matrices don't match for entry " << i
996  << " : "
997  << block_pt(0,0)->value()[i] << " "
998  << block_pt(1,1)->value()[i] << " "
999  << std::endl;
1000  }
1001  }
1002  if (fail)
1003  {
1004  throw OomphLibError(tmp.str(),
1005  OOMPH_CURRENT_FUNCTION,
1006  OOMPH_EXCEPTION_LOCATION);
1007  }
1008  }
1009 
1010  // Check that off-diagonal matrices are negatives
1011  //-----------------------------------------------
1012  {
1013  unsigned nnz1=block_pt(0,1)->nnz();
1014  unsigned nnz2=block_pt(1,0)->nnz();
1015  if (nnz1!=nnz2)
1016  {
1017  std::stringstream tmp;
1018  tmp << "nnz of diagonal blocks doesn't match: "
1019  << nnz1 << " != " << nnz2 << std::endl;
1020  throw OomphLibError(tmp.str(),
1021  OOMPH_CURRENT_FUNCTION,
1022  OOMPH_EXCEPTION_LOCATION);
1023  }
1024  unsigned nrow1=block_pt(0,1)->nrow();
1025  unsigned nrow2=block_pt(1,0)->nrow();
1026  if (nrow1!=nrow2)
1027  {
1028  std::stringstream tmp;
1029  tmp << "nrow of off-diagonal blocks doesn't match: "
1030  << nrow1 << " != " << nrow2 << std::endl;
1031  throw OomphLibError(tmp.str(),
1032  OOMPH_CURRENT_FUNCTION,
1033  OOMPH_EXCEPTION_LOCATION);
1034  }
1035 
1036 
1037  // Check entries
1038  bool fail=false;
1039  double tol=1.0e-15;
1040  std::stringstream tmp;
1041 
1042  // Check row starts
1043  for (unsigned i=0;i<nrow1+1;i++)
1044  {
1045  if (block_pt(0,1)->row_start()[i]!=
1046  block_pt(1,0)->row_start()[i])
1047  {
1048  fail=true;
1049  tmp << "Row starts of off-diag matrices don't match for row " << i
1050  << " : "
1051  << block_pt(0,1)->row_start()[i] << " "
1052  << block_pt(1,0)->row_start()[i] << " "
1053  << std::endl;
1054  }
1055  }
1056 
1057  // Check values and column indices
1058  for (unsigned i=0;i<nnz1;i++)
1059  {
1060  if (block_pt(0,1)->column_index()[i]!=
1061  block_pt(1,0)->column_index()[i])
1062  {
1063  fail=true;
1064  tmp << "Column indices of off-diag matrices don't match for entry " << i
1065  << " : "
1066  << block_pt(0,1)->column_index()[i] << " "
1067  << block_pt(1,0)->column_index()[i] << " "
1068  << std::endl;
1069  }
1070 
1071  if (fabs(block_pt(0,1)->value()[i]+
1072  block_pt(1,0)->value()[i])>tol)
1073  {
1074  fail=true;
1075  tmp << "Values of off-diag matrices aren't negatives of "
1076  << "each other for entry " << i
1077  << " : "
1078  << block_pt(0,1)->value()[i] << " "
1079  << block_pt(1,0)->value()[i] << " "
1080  << std::endl;
1081  }
1082  }
1083  if (fail)
1084  {
1085  throw OomphLibError(tmp.str(),
1086  OOMPH_CURRENT_FUNCTION,
1087  OOMPH_EXCEPTION_LOCATION);
1088  }
1089  }
1090 
1091  // Clean up
1092  for (unsigned i=0;i<nblock_types;i++)
1093  {
1094  for (unsigned j=0;j<nblock_types;j++)
1095  {
1096  delete block_pt(i,j);
1097  block_pt(i,j)=0;
1098  }
1099  }
1100 
1101  // Stop clock
1102  clock_t t_bl_end=clock();
1103  double total_setup_time=double(t_bl_end-t_bl_start)/CLOCKS_PER_SEC;
1104  oomph_info << "CPU time for block preconditioner self-test [sec]: "
1105  << total_setup_time << "\n" << std::endl;
1106 
1107  }
1108 
1109 //===================================================================
1110 /// Runs a full setup of the MG solver
1111 //===================================================================
1112  template<unsigned DIM>
1114  {
1115 #ifdef OOMPH_HAS_MPI
1116  // Make sure that this is running in serial. Can't guarantee it'll
1117  // work when the problem is distributed over several processors
1118  if (MPI_Helpers::communicator_pt()->nproc()>1)
1119  {
1120  // Throw a warning
1121  OomphLibWarning("Can't guarantee the MG solver will work in parallel!",
1122  OOMPH_CURRENT_FUNCTION,
1123  OOMPH_EXCEPTION_LOCATION);
1124  }
1125 #endif
1126 
1127  // Initialise the timer start variable
1128  double t_fs_start=0.0;
1129 
1130  // If we're allowed to output
1131  if (!Suppress_all_output)
1132  {
1133  // Start the timer
1134  t_fs_start=TimingHelpers::timer();
1135 
1136  // Notify user that the hierarchy of levels is complete
1137  oomph_info << "\n========Starting Setup of Multigrid Preconditioner========"
1138  << std::endl;
1139 
1140  // Notify user that the hierarchy of levels is complete
1141  oomph_info << "\nStarting the full setup of the Helmholtz multigrid solver."
1142  << std::endl;
1143  }
1144 
1145 #ifdef PARANOID
1146  // PARANOID check - Make sure the dimension of the solver matches the
1147  // dimension of the elements used in the problems mesh
1148  if (dynamic_cast<FiniteElement*>
1149  (Mg_problem_pt->mesh_pt()->element_pt(0))->dim()!=DIM)
1150  {
1151  // Create an error message
1152  std::string err_strng="The dimension of the elements used in the mesh ";
1153  err_strng+="does not match the dimension of the solver.";
1154 
1155  // Throw the error
1156  throw OomphLibError(err_strng,
1157  OOMPH_CURRENT_FUNCTION,
1158  OOMPH_EXCEPTION_LOCATION);
1159  }
1160 
1161  // PARANOID check - The elements of the bulk mesh must all be refineable
1162  // elements otherwise we cannot deal with this
1163  if (Mg_problem_pt->mg_bulk_mesh_pt()!=0)
1164  {
1165  // Find the number of elements in the bulk mesh
1166  unsigned n_elements=Mg_problem_pt->mg_bulk_mesh_pt()->nelement();
1167 
1168  // Loop over the elements in the mesh and ensure that they are
1169  // all refineable elements
1170  for (unsigned el_counter=0;el_counter<n_elements;el_counter++)
1171  {
1172  // Upcast global mesh element to a refineable element
1173  RefineableElement* el_pt=dynamic_cast<RefineableElement*>
1174  (Mg_problem_pt->mg_bulk_mesh_pt()->element_pt(el_counter));
1175 
1176  // Check if the upcast worked or not; if el_pt is a null pointer the
1177  // element is not refineable
1178  if (el_pt==0)
1179  {
1180  // Create an output steam
1181  std::ostringstream error_message_stream;
1182 
1183  // Store the error message
1184  error_message_stream << "Element in bulk mesh could not be upcast to "
1185  << "a refineable element." << std::endl;
1186 
1187  // Throw an error
1188  throw OomphLibError(error_message_stream.str(),
1189  OOMPH_CURRENT_FUNCTION,
1190  OOMPH_EXCEPTION_LOCATION);
1191  }
1192  } // for (unsigned el_counter=0;el_counter<n_elements;el_counter++)
1193  }
1194  // If the provided bulk mesh pointer is a null pointer
1195  else
1196  {
1197  // Create an output steam
1198  std::ostringstream error_message_stream;
1199 
1200  // Store the error message
1201  error_message_stream << "The provided bulk mesh pointer is a null pointer. "
1202  << std::endl;
1203 
1204  // Throw an error
1205  throw OomphLibError(error_message_stream.str(),
1206  OOMPH_CURRENT_FUNCTION,
1207  OOMPH_EXCEPTION_LOCATION);
1208  } // if (Mg_problem_pt->mg_bulk_mesh_pt()!=0)
1209 #endif
1210 
1211  // If this is not the first Newton step then we will already have things
1212  // in storage. If this is the case, delete them
1213  clean_up_memory();
1214 
1215  // Resize the Mg_hierarchy_pt vector
1216  Mg_hierarchy_pt.resize(1,0);
1217 
1218  // Set the pointer to the finest level as the first entry in Mg_hierarchy_pt
1219  Mg_hierarchy_pt[0]=Mg_problem_pt;
1220 
1221  // Create the hierarchy of levels
1222  setup_mg_hierarchy();
1223 
1224  // Set up the interpolation and restriction matrices
1225  setup_transfer_matrices();
1226 
1227  // Set up the data structures on each level, i.e. the system matrix,
1228  // LHS and RHS vectors and smoothers
1229  setup_mg_structures();
1230 
1231  // Set up the smoothers on all of the levels
1232  setup_smoothers();
1233 
1234  // Loop over all of the coarser levels
1235  for (unsigned i=1;i<Nlevel;i++)
1236  {
1237  // Delete the i-th coarse-grid HelmholtzMGProblem object
1238  delete Mg_hierarchy_pt[i];
1239 
1240  // Set it to be a null pointer
1241  Mg_hierarchy_pt[i]=0;
1242  }
1243 
1244  // Indicate that the full setup has been completed
1245  Has_been_setup=true;
1246 
1247  // If we're allowed to output to the screen
1248  if (!Suppress_all_output)
1249  {
1250  // Output the time taken to complete the full setup
1251  double t_fs_end=TimingHelpers::timer();
1252  double full_setup_time=t_fs_end-t_fs_start;
1253 
1254  // Output the CPU time
1255  oomph_info << "\nCPU time for full setup [sec]: "
1256  << full_setup_time << std::endl;
1257 
1258  // Notify user that the hierarchy of levels is complete
1259  oomph_info << "\n==============="
1260  << "Multigrid Full Setup Complete"
1261  << "==============\n" << std::endl;
1262  } // if (!Suppress_all_output)
1263  } // End of full_setup
1264 
1265  //===================================================================
1266  /// \short Set up the MG hierarchy. Creates a vector of pointers to
1267  /// each MG level and resizes internal storage for multigrid data
1268  //===================================================================
1269  // Function to set up the hierachy of levels.
1270  template<unsigned DIM>
1272  {
1273  // Initialise the timer start variable
1274  double t_m_start=0.0;
1275 
1276  // Notify the user if it is allowed
1277  if (!Suppress_all_output)
1278  {
1279  // Notify user of progress
1280  oomph_info << "\n==============="
1281  << "Creating Multigrid Hierarchy"
1282  << "===============\n" << std::endl;
1283 
1284  // Start clock
1285  t_m_start=TimingHelpers::timer();
1286  }
1287 
1288  // Create a bool to indicate whether or not we could create an unrefined
1289  // copy. This bool will be assigned the value FALSE when the current copy
1290  // is the last level of the multigrid hierarchy
1291  bool managed_to_create_unrefined_copy=true;
1292 
1293  // Now keep making copies and try to make an unrefined copy of
1294  // the mesh
1295  unsigned level=0;
1296 
1297  // Set up all of the levels by making a completely unrefined copy
1298  // of the problem using the function make_new_problem
1299  while (managed_to_create_unrefined_copy)
1300  {
1301  // Make a new object of the same type as the derived problem
1302  HelmholtzMGProblem* new_problem_pt=Mg_problem_pt->make_new_problem();
1303 
1304  // Do anything that needs to be done before we can refine the mesh
1305  new_problem_pt->actions_before_adapt();
1306 
1307  // To create the next level in the hierarchy we need to create a mesh
1308  // which matches the refinement of the current problem and then unrefine
1309  // the mesh. This can alternatively be done using the function
1310  // refine_base_mesh_as_in_reference_mesh_minus_one which takes a
1311  // reference mesh to do precisely the above with
1312  managed_to_create_unrefined_copy=
1313  new_problem_pt->mg_bulk_mesh_pt()->
1314  refine_base_mesh_as_in_reference_mesh_minus_one(
1315  Mg_hierarchy_pt[level]->mg_bulk_mesh_pt());
1316 
1317  // If we were able to unrefine the problem on the current level
1318  // then add the unrefined problem to a vector of the levels
1319  if (managed_to_create_unrefined_copy)
1320  {
1321  // Another level has been created so increment the level counter
1322  level++;
1323 
1324  // If the documentation of everything has not been suppressed
1325  // then tell the user we managed to create another level
1326  if (!Suppress_all_output)
1327  {
1328  // Notify user that unrefinement was successful
1329  oomph_info << "Success! Level " << level
1330  << " has been created:" << std::endl;
1331  }
1332 
1333  // Do anything that needs to be done after refinement
1334  new_problem_pt->actions_after_adapt();
1335 
1336  // Do the equation numbering for the new problem
1337  oomph_info << "\n - Number of equations: "
1338  << new_problem_pt->assign_eqn_numbers()
1339  << "\n" << std::endl;
1340 
1341  // Add the new problem pointer onto the vector of MG levels
1342  // and increment the value of level by 1
1343  Mg_hierarchy_pt.push_back(new_problem_pt);
1344  }
1345  // If we weren't able to create an unrefined copy
1346  else
1347  {
1348  // Delete the new problem
1349  delete new_problem_pt;
1350 
1351  // Make it a null pointer
1352  new_problem_pt=0;
1353 
1354  // Assign the number of levels to Nlevel
1355  Nlevel=Mg_hierarchy_pt.size();
1356 
1357  // If we're allowed to document then tell the user we've reached
1358  // the coarsest level of the hierarchy
1359  if (!Suppress_all_output)
1360  {
1361  // Notify the user
1362  oomph_info << "Reached the coarsest level! "
1363  << "Number of levels: " << Nlevel << std::endl;
1364  }
1365  } // if (managed_to_create_unrefined_copy)
1366  } // while (managed_to_create_unrefined_copy)
1367 
1368  //------------------------------------------------------------------
1369  // Given that we know the number of levels in the hierarchy we can
1370  // resize the vectors which will store all the information required
1371  // for our solver:
1372  //------------------------------------------------------------------
1373  // Resize the vector storing all of the system matrices
1374  Mg_matrices_storage_pt.resize(Nlevel);
1375 
1376  // Resize the vector storing all of the solution vectors (X_mg)
1377  X_mg_vectors_storage.resize(Nlevel);
1378 
1379  // Resize the vector storing all of the RHS vectors (Rhs_mg)
1380  Rhs_mg_vectors_storage.resize(Nlevel);
1381 
1382  // Resize the vector storing all of the residual vectors
1383  Residual_mg_vectors_storage.resize(Nlevel);
1384 
1385  // Allocate space for the Max_edge_width vector, we only need the value
1386  // of h on the levels where we use a smoother
1387  Max_edge_width.resize(Nlevel-1,0.0);
1388 
1389  // Allocate space for the pre-smoother storage vector (remember, we do
1390  // not need a smoother on the coarsest level; we use a direct solve there)
1391  Pre_smoothers_storage_pt.resize(Nlevel-1,0);
1392 
1393  // Allocate space for the post-smoother storage vector (remember, we do
1394  // not need a smoother on the coarsest level; we use a direct solve there)
1395  Post_smoothers_storage_pt.resize(Nlevel-1,0);
1396 
1397  // Resize the vector storing all of the interpolation matrices
1398  Interpolation_matrices_storage_pt.resize(Nlevel-1,0);
1399 
1400  // Resize the vector storing all of the restriction matrices
1401  Restriction_matrices_storage_pt.resize(Nlevel-1,0);
1402 
1403  // If we're allowed to output information to the screen
1404  if (!Suppress_all_output)
1405  {
1406  // Stop clock
1407  double t_m_end=TimingHelpers::timer();
1408  double total_setup_time=double(t_m_end-t_m_start);
1409  oomph_info << "\nCPU time for creation of hierarchy of MG problems [sec]: "
1410  << total_setup_time << std::endl;
1411 
1412  // Notify user that the hierarchy of levels is complete
1413  oomph_info << "\n==============="
1414  << "Hierarchy Creation Complete"
1415  << "================\n" << std::endl;
1416  }
1417  } // End of setup_mg_hierarchy
1418 
1419  //===================================================================
1420  /// \short Set up the transfer matrices. Both the pure injection and
1421  /// full weighting method have been implemented here but it is highly
1422  /// recommended that full weighting is used in general. In both
1423  /// methods the transpose of the transfer matrix is used to transfer
1424  /// a vector back
1425  //===================================================================
1426  template<unsigned DIM>
1428  {
1429  // Initialise the timer start variable
1430  double t_r_start=0.0;
1431 
1432  // Notify the user (if we're allowed)
1433  if (!Suppress_all_output)
1434  {
1435  // Notify user of progress
1436  oomph_info << "Creating the transfer matrices." << std::endl;
1437 
1438  // Start the clock!
1439  t_r_start=TimingHelpers::timer();
1440  }
1441 
1442  // Using full weighting so use setup_interpolation_matrices.
1443  // Note: There are two methods to choose from here, the ideal choice is
1444  // setup_interpolation_matrices() but that requires a refineable mesh base
1445  if (dynamic_cast<TreeBasedRefineableMeshBase*>
1446  (Mg_problem_pt->mg_bulk_mesh_pt()))
1447  {
1448  setup_interpolation_matrices();
1449  }
1450  // If the mesh is unstructured we have to use the locate_zeta function
1451  // to set up the interpolation matrices
1452  else
1453  {
1454  setup_interpolation_matrices_unstructured();
1455  }
1456 
1457  // Loop over all levels that will be assigned a restriction matrix
1458  set_restriction_matrices_as_interpolation_transposes();
1459 
1460  // If we're allowed
1461  if (!Suppress_all_output)
1462  {
1463  // Stop the clock
1464  double t_r_end=TimingHelpers::timer();
1465  double total_G_setup_time=double(t_r_end-t_r_start);
1466  oomph_info << "\nCPU time for transfer matrices setup [sec]: "
1467  << total_G_setup_time << std::endl;
1468 
1469  // Notify user that the hierarchy of levels is complete
1470  oomph_info << "\n============Transfer Matrices Setup Complete=============="
1471  << "\n" << std::endl;
1472  }
1473  } // End of setup_transfer_matrices function
1474 
1475  //===================================================================
1476  /// \short Set up the MG structures on each level
1477  //===================================================================
1478  template<unsigned DIM>
1480  {
1481  // Initialise the timer start variable
1482  double t_m_start=0.0;
1483 
1484  // Start the clock (if we're allowed to time things)
1485  if (!Suppress_all_output)
1486  {
1487  // Start the clock
1488  t_m_start=TimingHelpers::timer();
1489  }
1490 
1491  // Calculate the wavenumber value:
1492  //--------------------------------
1493  // Reset the value of Wavenumber
1494  Wavenumber=0.0;
1495 
1496  // Upcast the first element in the bulk mesh
1499  (Mg_problem_pt->mg_bulk_mesh_pt()->element_pt(0));
1500 
1501  // Grab the k_squared value from the element pointer and square root.
1502  // Note, we assume the wavenumber is the same everywhere in the mesh
1503  // and it is also the same on every level.
1504  Wavenumber=sqrt(pml_helmholtz_el_pt->k_squared());
1505 
1506  // We don't need the pointer anymore so make it a null pointer but don't
1507  // delete the underlying element data
1508  pml_helmholtz_el_pt=0;
1509 
1510  // Set up the system matrix and accompanying vectors on each level:
1511  //-----------------------------------------------------------------
1512  // Loop over each level and extract the system matrix, solution vector
1513  // right-hand side vector and residual vector (to store the value of r=b-Ax)
1514  for (unsigned i=0;i<Nlevel;i++)
1515  {
1516  // If we're allowed to output
1517  if (!Suppress_all_output)
1518  {
1519  // Output the level we're working on
1520  oomph_info << "Setting up MG structures on level: " << i
1521  << "\n" << std::endl;
1522  }
1523 
1524  // Resize the solution and RHS vector
1525  unsigned n_dof_per_block=Mg_hierarchy_pt[i]->ndof()/2;
1526 
1527  // Create the linear algebra distribution to build the vectors
1528  LinearAlgebraDistribution* dist_pt=
1529  new LinearAlgebraDistribution(Mg_hierarchy_pt[i]->communicator_pt(),
1530  n_dof_per_block,false);
1531 
1532 #ifdef PARANOID
1533 #ifdef OOMPH_HAS_MPI
1534  // Set up the warning messages
1535  std::string warning_message="Setup of distribution has not been ";
1536  warning_message+="tested with MPI.";
1537 
1538  // If we're not running the code in serial
1539  if (dist_pt->communicator_pt()->nproc()>1)
1540  {
1541  // Throw a warning
1542  OomphLibWarning(warning_message,
1543  OOMPH_CURRENT_FUNCTION,
1544  OOMPH_EXCEPTION_LOCATION);
1545  }
1546 #endif
1547 #endif
1548 
1549  // Create storage for the i-th level system matrix:
1550  //-------------------------------------------------
1551  // Resize the i-th entry of the matrix storage vector to contain two
1552  // CRDoubleMatrix pointers
1553  Mg_matrices_storage_pt[i].resize(2,0);
1554 
1555  // Loop over the real and imaginary part
1556  for (unsigned j=0;j<2;j++)
1557  {
1558  // Dynamically allocate a new CRDoubleMatrix
1559  Mg_matrices_storage_pt[i][j]=new CRDoubleMatrix;
1560  }
1561 
1562  // Build the matrix distribution (real part)
1563  Mg_matrices_storage_pt[i][0]->build(dist_pt);
1564 
1565  // Build the matrix distribution (imaginary part)
1566  Mg_matrices_storage_pt[i][1]->build(dist_pt);
1567 
1568  // Create storage for the i-th level solution vector:
1569  //---------------------------------------------------
1570  // Resize the i-th level solution vector to contain two DoubleVectors
1571  X_mg_vectors_storage[i].resize(2);
1572 
1573  // Build the approximate solution (real part)
1574  X_mg_vectors_storage[i][0].build(dist_pt);
1575 
1576  // Build the approximate solution (imaginary part)
1577  X_mg_vectors_storage[i][1].build(dist_pt);
1578 
1579  // Create storage for the i-th level RHS vector:
1580  //----------------------------------------------
1581  // Resize the i-th level RHS vector to contain two DoubleVectors
1582  Rhs_mg_vectors_storage[i].resize(2);
1583 
1584  // Build the point source function (real part)
1585  Rhs_mg_vectors_storage[i][0].build(dist_pt);
1586 
1587  // Build the point source function (imaginary part)
1588  Rhs_mg_vectors_storage[i][1].build(dist_pt);
1589 
1590  // Create storage for the i-th level residual vector:
1591  //---------------------------------------------------
1592  // Resize the i-th level residual vector to contain two DoubleVectors
1593  Residual_mg_vectors_storage[i].resize(2);
1594 
1595  // Build the residual vector, r=b-Ax (real part)
1596  Residual_mg_vectors_storage[i][0].build(dist_pt);
1597 
1598  // Build the residual vector, r=b-Ax (imaginary part)
1599  Residual_mg_vectors_storage[i][1].build(dist_pt);
1600 
1601  // Delete the distribution pointer
1602  delete dist_pt;
1603 
1604  // Make it a null pointer
1605  dist_pt=0;
1606 
1607  // Compute system matrix on the current level. On the finest level of the
1608  // hierarchy the system matrix is given by the complex-shifted Laplacian
1609  // preconditioner. On the subsequent levels the Galerkin approximation
1610  // is used to give us a coarse-grid representation of the problem
1611  if (i==0)
1612  {
1613  // Initialise the timer start variable
1614  double t_jac_start=0.0;
1615 
1616  // If we're allowed to output things
1617  if (!Suppress_all_output)
1618  {
1619  // Start timer for Jacobian setup
1620  t_jac_start=TimingHelpers::timer();
1621  }
1622 
1623 #ifdef PARANOID
1624  // Make sure the block preconditioner returns the correct sort of matrix
1625  block_preconditioner_self_test();
1626 #endif
1627 
1628  // The preconditioner works with one mesh; set it!
1629  this->set_nmesh(1);
1630 
1631  // Elements in actual pml layer are trivially wrapped versions of their
1632  // bulk counterparts. Technically they are different elements so we have
1633  // to allow different element types
1634  bool allow_different_element_types_in_mesh=true;
1635  this->set_mesh(0,Mg_hierarchy_pt[0]->mesh_pt(),
1636  allow_different_element_types_in_mesh);
1637 
1638 #ifdef PARANOID
1639  // Find the number of dof types we're dealing with
1640  unsigned n_dof_types=this->ndof_types();
1641 
1642  // This preconditioner only works for 2 dof types
1643  if (n_dof_types!=2)
1644  {
1645  // Create an error message
1646  std::stringstream tmp;
1647  tmp << "This preconditioner only works for problems with 2 dof types\n"
1648  << "Yours has " << n_dof_types;
1649 
1650  // Throw an error
1651  throw OomphLibError(tmp.str(),
1652  OOMPH_CURRENT_FUNCTION,
1653  OOMPH_EXCEPTION_LOCATION);
1654  }
1655 #endif
1656 
1657  // If we're not using a value of zero for the alpha shift
1658  if (Alpha_shift!=0.0)
1659  {
1660  //------------------------------------------------------------------
1661  // Set the damping in all of the PML elements to create the complex-
1662  // shifted Laplacian preconditioner:
1663  //------------------------------------------------------------------
1664  // Create a pointer which will contain the value of the shift given
1665  // by Alpha_shift
1666  double* alpha_shift_pt=new double(Alpha_shift);
1667 
1668  // Calculate the number of elements in the mesh
1669  unsigned n_element=Mg_hierarchy_pt[0]->mesh_pt()->nelement();
1670 
1671  // To grab the static variable used to set the value of alpha we first
1672  // need to get an element of type PMLHelmholtzEquations (we arbitrarily
1673  // chose the first element in the mesh)
1676  (Mg_hierarchy_pt[0]->mesh_pt()->element_pt(0));
1677 
1678  // Now grab the pointer from the element
1679  static double* default_physical_constant_value_pt=el_pt->alpha_pt();
1680 
1681  // Loop over all of the elements
1682  for (unsigned i_el=0;i_el<n_element;i_el++)
1683  {
1684  // Upcast from a GeneralisedElement to a PmlHelmholtzElement
1687  (Mg_hierarchy_pt[0]->mesh_pt()->element_pt(i_el));
1688 
1689  // Make the internal element alpha pointer point to Alpha_shift (the
1690  // chosen value of the shift to be applied to the problem)
1691  el_pt->alpha_pt()=alpha_shift_pt;
1692  }
1693 
1694  //---------------------------------------------------------------------
1695  // We want to solve the problem with the modified Jacobian but the
1696  // Preconditioner will have a handle to pointer which points to the
1697  // system matrix. If we wish to use the block preconditioner to extract
1698  // the appropriate blocks of the matrix we need to assign it. To avoid
1699  // losing the original system matrix we will create a temporary pointer
1700  // to it which will be reassigned after we have use the block
1701  // preconditioner to extract the blocks of the shifted matrix:
1702  //---------------------------------------------------------------------
1703  // Create a temporary pointer to the Jacobian
1704  CRDoubleMatrix* jacobian_pt=this->matrix_pt();
1705 
1706  // Create a new CRDoubleMatrix to hold the shifted Jacobian matrix
1707  CRDoubleMatrix* shifted_jacobian_pt=new CRDoubleMatrix;
1708 
1709  // Allocate space for the residuals
1710  DoubleVector residuals;
1711 
1712  // Get the residuals vector and the shifted Jacobian. Note, we do
1713  // not need to assign the residuals vector; we're simply using
1714  // MG as a preconditioner
1715  Mg_hierarchy_pt[0]->get_jacobian(residuals,*shifted_jacobian_pt);
1716 
1717  // Replace the current matrix used in Preconditioner by the new matrix
1718  this->set_matrix_pt(shifted_jacobian_pt);
1719 
1720  // Set up the generic block look up scheme
1721  this->block_setup();
1722 
1723  // Extract the number of blocks.
1724  unsigned nblock_types=this->nblock_types();
1725 
1726 #ifdef PARANOID
1727  // PARANOID check - there must only be two block types
1728  if (nblock_types!=2)
1729  {
1730  // Create the error message
1731  std::stringstream tmp;
1732  tmp << "There are supposed to be two block types.\nYours has "
1733  << nblock_types << std::endl;
1734 
1735  // Throw an error
1736  throw OomphLibError(tmp.str(),
1737  OOMPH_CURRENT_FUNCTION,
1738  OOMPH_EXCEPTION_LOCATION);
1739  }
1740 #endif
1741 
1742  // Store the level
1743  unsigned level=0;
1744 
1745  // Loop over the rows of the block matrix
1746  for (unsigned i_row=0;i_row<nblock_types;i_row++)
1747  {
1748  // Fix the column index
1749  unsigned j_col=0;
1750 
1751  // Extract the required blocks, i.e. the first column
1752  this->get_block(i_row,j_col,*Mg_matrices_storage_pt[level][i_row]);
1753  }
1754 
1755  // The blocks have been extracted from the shifted Jacobian therefore
1756  // we no longer have any use for it
1757  delete shifted_jacobian_pt;
1758 
1759  // Now the appropriate blocks have been extracted from the shifted
1760  // Jacobian we reset the matrix pointer in Preconditioner to the
1761  // original Jacobian so the linear solver isn't affected
1762  this->set_matrix_pt(jacobian_pt);
1763 
1764  //--------------------------------------------------------
1765  // Reassign the damping factor in all of the PML elements:
1766  //--------------------------------------------------------
1767  // Loop over all of the elements
1768  for (unsigned i_el=0;i_el<n_element;i_el++)
1769  {
1770  // Upcast from a GeneralisedElement to a PmlHelmholtzElement
1773  (Mg_hierarchy_pt[0]->mesh_pt()->element_pt(i_el));
1774 
1775  // Set the value of alpha
1776  el_pt->alpha_pt()=default_physical_constant_value_pt;
1777  }
1778 
1779  // We've finished using the alpha_shift_pt pointer so delete it
1780  // as it was dynamically allocated
1781  delete alpha_shift_pt;
1782 
1783  // Make it a null pointer
1784  alpha_shift_pt=0;
1785  }
1786  // If the value of the shift is zero then we use the original
1787  // Jacobian matrix
1788  else
1789  {
1790  // The Jacobian has already been provided so now we just need to set
1791  // up the generic block look up scheme
1792  this->block_setup();
1793 
1794  // Extract the number of blocks.
1795  unsigned nblock_types=this->nblock_types();
1796 
1797 #ifdef PARANOID
1798  // If there are not only two block types we have a problem
1799  if (nblock_types!=2)
1800  {
1801  std::stringstream tmp;
1802  tmp << "There are supposed to be two block types.\n"
1803  << "Yours has " << nblock_types;
1804  throw OomphLibError(tmp.str(),
1805  OOMPH_CURRENT_FUNCTION,
1806  OOMPH_EXCEPTION_LOCATION);
1807  }
1808 #endif
1809 
1810  // Store the level
1811  unsigned level=0;
1812 
1813  // Loop over the rows of the block matrix
1814  for (unsigned i_row=0;i_row<nblock_types;i_row++)
1815  {
1816  // Fix the column index (since we only want the first column)
1817  unsigned j_col=0;
1818 
1819  // Extract the required blocks
1820  this->get_block(i_row,j_col,*Mg_matrices_storage_pt[level][i_row]);
1821  }
1822  } // if (Alpha_shift!=0.0) else
1823 
1824  if (!Suppress_all_output)
1825  {
1826  // Document the time taken
1827  double t_jac_end=TimingHelpers::timer();
1828  double jacobian_setup_time=t_jac_end-t_jac_start;
1829  oomph_info << " - Time for setup of Jacobian block matrix [sec]: "
1830  << jacobian_setup_time << "\n" << std::endl;
1831  }
1832  }
1833  // If we're not dealing with the finest level we're dealing with a
1834  // coarse-grid problem
1835  else
1836  {
1837  // Initialise the timer start variable
1838  double t_gal_start=0.0;
1839 
1840  // If we're allowed
1841  if (!Suppress_all_output)
1842  {
1843  // Start timer for Galerkin matrix calculation
1844  t_gal_start=TimingHelpers::timer();
1845  }
1846 
1847  //---------------------------------------------------------------------
1848  // The system matrix on the coarser levels must be formed using the
1849  // Galerkin approximation which we do by calculating the product
1850  // A^2h = I^2h_h * A^h * I^h_2h, i.e. the coarser version of the
1851  // finer grid system matrix is formed by multiplying by the (fine grid)
1852  // restriction matrix from the left and the (fine grid) interpolation
1853  // matrix from the left. Fortunately, since the restriction and
1854  // interpolation acts on the real and imaginary parts separately we
1855  // can calculate the real and imaginary parts of the Galerkin
1856  // approximation separately.
1857  //---------------------------------------------------------------------
1858 
1859  //----------------------------------------------
1860  // Real component of the Galerkin approximation:
1861  //----------------------------------------------
1862  // First we need to calculate A^h * I^h_2h which we store as A^2h
1863  Mg_matrices_storage_pt[i-1][0]->
1864  multiply(*Interpolation_matrices_storage_pt[i-1],
1865  *Mg_matrices_storage_pt[i][0]);
1866 
1867  // Now calculate I^2h_h * (A^h * I^h_2h) where the quantity in brackets
1868  // was just calculated. This updates A^2h to give us the true
1869  // Galerkin approximation to the finer grid matrix
1870  Restriction_matrices_storage_pt[i-1]->
1871  multiply(*Mg_matrices_storage_pt[i][0],
1872  *Mg_matrices_storage_pt[i][0]);
1873 
1874  //---------------------------------------------------
1875  // Imaginary component of the Galerkin approximation:
1876  //---------------------------------------------------
1877  // First we need to calculate A^h * I^h_2h which we store as A^2h
1878  Mg_matrices_storage_pt[i-1][1]->
1879  multiply(*Interpolation_matrices_storage_pt[i-1],
1880  *Mg_matrices_storage_pt[i][1]);
1881 
1882  // Now calculate I^2h_h * (A^h * I^h_2h) where the quantity in brackets
1883  // was just calculated. This updates A^2h to give us the true
1884  // Galerkin approximation to the finer grid matrix
1885  Restriction_matrices_storage_pt[i-1]->
1886  multiply(*Mg_matrices_storage_pt[i][1],
1887  *Mg_matrices_storage_pt[i][1]);
1888 
1889  // If the user did not choose to suppress everything
1890  if (!Suppress_all_output)
1891  {
1892  // End timer for Galerkin matrix calculation
1893  double t_gal_end=TimingHelpers::timer();
1894 
1895  // Calculate setup time
1896  double galerkin_matrix_calculation_time=t_gal_end-t_gal_start;
1897 
1898  // Document the time taken
1899  oomph_info << " - Time for system block matrix formation using the "
1900  << "Galerkin approximation [sec]: "
1901  << galerkin_matrix_calculation_time << "\n" << std::endl;
1902  }
1903  } // if (i==0) else
1904 
1905  // We only
1906  if (i<Nlevel-1)
1907  {
1908  // Find the maximum edge width, h:
1909  //--------------------------------
1910  // Initialise the timer start variable
1911  double t_para_start=0.0;
1912 
1913  // If we're allowed to output things
1914  if (!Suppress_all_output)
1915  {
1916  // Start timer for parameter calculation on the i-th level
1917  t_para_start=TimingHelpers::timer();
1918  }
1919 
1920  // Calculate the i-th entry of Wavenumber and Max_edge_width
1921  maximum_edge_width(i,Max_edge_width[i]);
1922 
1923  // If the user did not choose to suppress everything
1924  if (!Suppress_all_output)
1925  {
1926  // End timer for Galerkin matrix calculation
1927  double t_para_end=TimingHelpers::timer();
1928 
1929  // Calculate setup time
1930  double parameter_calculation_time=t_para_end-t_para_start;
1931 
1932  // Document the time taken
1933  oomph_info << " - Time for maximum edge width calculation [sec]: "
1934  << parameter_calculation_time << "\n" << std::endl;
1935  }
1936  } // if (i<Nlevel-1)
1937  } // for (unsigned i=0;i<Nlevel;i++)
1938 
1939  // The last system matrix that needs to be setup is the fully expanded
1940  // version of the system matrix on the coarsest level. This is needed
1941  // for the direct solve on the coarsest level
1942  setup_coarsest_level_structures();
1943 
1944  // If we're allowed to output
1945  if (!Suppress_all_output)
1946  {
1947  // Stop clock
1948  double t_m_end=TimingHelpers::timer();
1949  double total_setup_time=double(t_m_end-t_m_start);
1950  oomph_info << "CPU time for setup of MG structures [sec]: "
1951  << total_setup_time << std::endl;
1952 
1953  // Notify user that the hierarchy of levels is complete
1954  oomph_info << "\n============"
1955  << "Multigrid Structures Setup Complete"
1956  << "===========\n" << std::endl;
1957  }
1958  } // End of setup_mg_structures
1959 
1960  //=========================================================================
1961  /// \short Function to set up structures on the coarsest level of the MG
1962  /// hierarchy. This includes setting up the CRDoubleMatrix version of the
1963  /// coarsest level system matrix. This would otherwise be stored as a
1964  /// vector of pointers to the constituent CRDoubleMatrix objects which
1965  /// has the form:
1966  /// |-----|
1967  /// | A_r |
1968  /// Matrix_mg_pt = |-----|
1969  /// | A_i |
1970  /// |-----|
1971  /// and we want to construct:
1972  /// |-----|------|
1973  /// | A_r | -A_c |
1974  /// Coarse_matrix_mg_pt = |-----|------|
1975  /// | A_c | A_r |
1976  /// |-----|------|
1977  /// Once this is done we have to set up the distributions of the vectors
1978  /// associated with Coarse_matrix_mg_pt
1979  //=========================================================================
1980  template<unsigned DIM>
1982  {
1983  // Start timer
1984  double t_cm_start=TimingHelpers::timer();
1985 
1986  //---------------------------------------------------
1987  // Extract information from the constituent matrices:
1988  //---------------------------------------------------
1989 
1990  // Grab the real and imaginary matrix parts from storage
1991  CRDoubleMatrix* real_matrix_pt=Mg_matrices_storage_pt[Nlevel-1][0];
1992  CRDoubleMatrix* imag_matrix_pt=Mg_matrices_storage_pt[Nlevel-1][1];
1993 
1994  // Number of nonzero entries in A_r
1995  unsigned nnz_r=real_matrix_pt->nnz();
1996  unsigned nnz_c=imag_matrix_pt->nnz();
1997 
1998  // Calculate the total number of rows (and thus columns) in the real matrix
1999  unsigned n_rows_r=real_matrix_pt->nrow();
2000 
2001  // Acquire access to the value, row_start and column_index arrays from
2002  // the real and imaginary portions of the full matrix.
2003  // Real part:
2004  const double* value_r_pt=real_matrix_pt->value();
2005  const int* row_start_r_pt=real_matrix_pt->row_start();
2006  const int* column_index_r_pt=real_matrix_pt->column_index();
2007 
2008  // Imaginary part:
2009  const double* value_c_pt=imag_matrix_pt->value();
2010  const int* row_start_c_pt=imag_matrix_pt->row_start();
2011  const int* column_index_c_pt=imag_matrix_pt->column_index();
2012 
2013 #ifdef PARANOID
2014  // PARANOID check - make sure the matrices have the same number of rows
2015  // to ensure they are compatible
2016  if (real_matrix_pt->nrow()!=imag_matrix_pt->nrow())
2017  {
2018  std::ostringstream error_message_stream;
2019  error_message_stream << "The real and imaginary matrices do not have "
2020  << "compatible sizes";
2021  throw OomphLibError(error_message_stream.str(),
2022  OOMPH_CURRENT_FUNCTION,
2023  OOMPH_EXCEPTION_LOCATION);
2024  }
2025  // PARANOID check - make sure the matrices have the same number of columns
2026  // to ensure they are compatible
2027  if (real_matrix_pt->ncol()!=imag_matrix_pt->ncol())
2028  {
2029  std::ostringstream error_message_stream;
2030  error_message_stream << "The real and imaginary matrices do not have "
2031  << "compatible sizes";
2032  throw OomphLibError(error_message_stream.str(),
2033  OOMPH_CURRENT_FUNCTION,
2034  OOMPH_EXCEPTION_LOCATION);
2035  }
2036 #endif
2037 
2038  // Calculate the total number of nonzeros in the full matrix
2039  unsigned nnz=2*(nnz_r+nnz_c);
2040 
2041  // Allocate space for the row_start and column_index vectors associated with
2042  // the complete matrix
2043  Vector<double> value(nnz,0.0);
2044  Vector<int> column_index(nnz,0);
2045  Vector<int> row_start(2*n_rows_r+1,0);
2046 
2047  //----------------------------
2048  // Build the row start vector:
2049  //----------------------------
2050 
2051  // Loop over the rows of the row_start vector. This is decomposed into
2052  // two parts. The first (n_rows_r+1) entries are found by computing
2053  // the entry-wise addition of row_start_r+row_start_c. The remaining
2054  // entries are almost the same as the first (n_rows_r+1). The only
2055  // distinction is that we need to shift the values of the entries by
2056  // the number of nonzeros in the top half of A. This is obvious from
2057  // observing the structure of the complete matrix.
2058 
2059  // Loop over the rows in the top half:
2060  for (unsigned i=0;i<n_rows_r;i++)
2061  {
2062  // Set the i-th entry in the row start vector
2063  row_start[i]=*(row_start_r_pt+i)+*(row_start_c_pt+i);
2064  }
2065 
2066  // Loop over the rows in the bottom half:
2067  for (unsigned i=n_rows_r;i<2*n_rows_r;i++)
2068  {
2069  // Set the i-th entry in the row start vector (bottom half)
2070  row_start[i]=row_start[i-n_rows_r]+(nnz_r+nnz_c);
2071  }
2072 
2073  // Set the last entry in the row start vector
2074  row_start[2*n_rows_r]=nnz;
2075 
2076  //-----------------------------------------
2077  // Build the column index and value vector:
2078  //-----------------------------------------
2079 
2080  // Variable to store the index of the nonzero
2081  unsigned i_nnz=0;
2082 
2083  // Loop over the top half of the complete matrix
2084  for (unsigned i=0;i<n_rows_r;i++)
2085  {
2086  // Calculate the number of nonzeros in the i-th row of A_r
2087  unsigned i_row_r_nnz=*(row_start_r_pt+i+1)-*(row_start_r_pt+i);
2088 
2089  // Calculate the number of nonzeros in the i-th row of A_c
2090  unsigned i_row_c_nnz=*(row_start_c_pt+i+1)-*(row_start_c_pt+i);
2091 
2092  // The index of the first entry in the i-th row of A_r
2093  unsigned i_first_entry_r=*(row_start_r_pt+i);
2094 
2095  // The index of the first entry in the i-th row of A_c
2096  unsigned i_first_entry_c=*(row_start_c_pt+i);
2097 
2098  // Loop over the number of nonzeros in the row associated with A_r
2099  for (unsigned j=0;j<i_row_r_nnz;j++)
2100  {
2101  // Assign the column index of the j-th entry in the i-th row of A_r
2102  // to the column_index vector
2103  column_index[i_nnz]=*(column_index_r_pt+i_first_entry_r+j);
2104 
2105  // Assign the corresponding entry in the value vector
2106  value[i_nnz]=*(value_r_pt+i_first_entry_r+j);
2107 
2108  // Increment the value of i_nnz
2109  i_nnz++;
2110  }
2111 
2112  // Loop over the number of nonzeros in the row associated with -A_c
2113  for (unsigned j=0;j<i_row_c_nnz;j++)
2114  {
2115  // Assign the column index of the j-th entry in the i-th row of -A_c
2116  // to the column_index vector
2117  column_index[i_nnz]=*(column_index_c_pt+i_first_entry_c+j)+n_rows_r;
2118 
2119  // Assign the corresponding entry in the value vector
2120  value[i_nnz]=-*(value_c_pt+i_first_entry_c+j);
2121 
2122  // Increment the value of i_nnz
2123  i_nnz++;
2124  }
2125  } // for (unsigned i=0;i<n_rows_r;i++)
2126 
2127  // Loop over the bottom half of the complete matrix
2128  for (unsigned i=n_rows_r;i<2*n_rows_r;i++)
2129  {
2130  // Calculate the number of nonzeros in row i of A_r
2131  unsigned i_row_r_nnz=
2132  *(row_start_r_pt+i-n_rows_r+1)-*(row_start_r_pt+i-n_rows_r);
2133 
2134  // Calculate the number of nonzeros in row i of A_c
2135  unsigned i_row_c_nnz=
2136  *(row_start_c_pt+i-n_rows_r+1)-*(row_start_c_pt+i-n_rows_r);
2137 
2138  // Get the index of the first entry in the i-th row of A_r
2139  unsigned i_first_entry_r=*(row_start_r_pt+i-n_rows_r);
2140 
2141  // Get the index of the first entry in the i-th row of A_c
2142  unsigned i_first_entry_c=*(row_start_c_pt+i-n_rows_r);
2143 
2144  // Loop over the number of nonzeros in the row associated with A_c
2145  for (unsigned j=0;j<i_row_c_nnz;j++)
2146  {
2147  // Assign the column index of the j-th entry in the i-th row of A_c
2148  // to the column_index vector
2149  column_index[i_nnz]=*(column_index_c_pt+i_first_entry_c+j);
2150 
2151  // Assign the corresponding entry in the value vector
2152  value[i_nnz]=*(value_c_pt+i_first_entry_c+j);
2153 
2154  // Increment the value of i_nnz
2155  i_nnz++;
2156  }
2157 
2158  // Loop over the number of nonzeros in the row associated with A_r
2159  for (unsigned j=0;j<i_row_r_nnz;j++)
2160  {
2161  // Assign the column index of the j-th entry in the i-th row of A_r
2162  // to the column_index vector
2163  column_index[i_nnz]=*(column_index_r_pt+i_first_entry_r+j)+n_rows_r;
2164 
2165  // Assign the corresponding entry in the value vector
2166  value[i_nnz]=*(value_r_pt+i_first_entry_r+j);
2167 
2168  // Increment the value of i_nnz
2169  i_nnz++;
2170  }
2171  } // for (unsigned i=n_rows_r;i<2*n_rows_r;i++)
2172 
2173  //-----------------------
2174  // Build the full matrix:
2175  //-----------------------
2176 
2177  // Allocate space for a CRDoubleMatrix
2178  Coarsest_matrix_mg_pt=new CRDoubleMatrix;
2179 
2180  // Make the distribution pointer
2181  LinearAlgebraDistribution* dist_pt=
2182  new LinearAlgebraDistribution(Mg_hierarchy_pt[Nlevel-1]->
2183  communicator_pt(),2*n_rows_r,false);
2184 
2185  // First, we need to build the matrix. Making use of its particular
2186  // structure we know that there are 2*n_rows_r columns in this matrix.
2187  // The remaining information has just been sorted out
2188  Coarsest_matrix_mg_pt->build(dist_pt,
2189  2*n_rows_r,
2190  value,
2191  column_index,
2192  row_start);
2193 
2194  // Build the distribution of associated solution vector
2195  Coarsest_x_mg.build(dist_pt);
2196 
2197  // Build the distribution of associated RHS vector
2198  Coarsest_rhs_mg.build(dist_pt);
2199 
2200  // Delete the associated distribution pointer
2201  delete dist_pt;
2202 
2203  // Summarise setup
2204  double t_cm_end=TimingHelpers::timer();
2205  double total_setup_time=double(t_cm_end-t_cm_start);
2206  oomph_info << " - Time for formation of the full matrix "
2207  << "on the coarsest level [sec]: "
2208  << total_setup_time << "\n" << std::endl;
2209  } // End of setup_coarsest_matrix_mg
2210 
2211 
2212 //==========================================================================
2213 /// \short Find the value of the parameters h on the level-th problem in
2214 /// the hierarchy. The value of h is determined by looping over each element
2215 /// in the mesh and calculating the length of each side and take the maximum
2216 /// value.Note, this is a heuristic calculation; if the mesh is nonuniform
2217 /// or adaptive refinement is used then the value of h, is not the same
2218 /// everywhere so we find the maximum edge width instead. If, however,
2219 /// uniform refinement is used on a uniform mesh (using quad elements) then
2220 /// this will return the correct value of h.
2221 ///
2222 /// This is the explicit template specialisation of the case DIM=2.
2223 //==========================================================================
2224  template<>
2226  maximum_edge_width(const unsigned& level,double& h)
2227  {
2228  // Create a pointer to the "bulk" mesh
2229  TreeBasedRefineableMeshBase* bulk_mesh_pt=
2230  Mg_hierarchy_pt[level]->mg_bulk_mesh_pt();
2231 
2232  // Reset the value of h
2233  h=0.0;
2234 
2235  // Find out how many nodes there are along one edge of the first element.
2236  // We assume here that all elements have the same number of nodes
2237  unsigned nnode_1d=dynamic_cast<FiniteElement*>
2238  (bulk_mesh_pt->element_pt(0))->nnode_1d();
2239 
2240  // Sort out corner node IDs:
2241  //--------------------------
2242  // Initialise a vector to store local node IDs of the corners
2243  Vector<unsigned> corner_node_id(4,0);
2244 
2245  // Identify the local node ID of the first corner
2246  corner_node_id[0]=0;
2247 
2248  // Identify the local node ID of the second corner
2249  corner_node_id[1]=nnode_1d-1;
2250 
2251  // Identify the local node ID of the third corner
2252  corner_node_id[2]=nnode_1d*nnode_1d-1;
2253 
2254  // Identify the local node ID of the fourth corner
2255  corner_node_id[3]=nnode_1d*(nnode_1d-1);
2256 
2257  // Create storage for the nodal information:
2258  //------------------------------------------
2259  // Pointer to the first corner node on the j-th edge
2260  Node* first_node_pt=0;
2261 
2262  // Pointer to the second corner node on the j-th edge
2263  Node* second_node_pt=0;
2264 
2265  // Vector to store the (Eulerian) position of the first corner node
2266  // along a given edge of the element
2267  Vector<double> first_node_x(2,0.0);
2268 
2269  // Vector to store the (Eulerian) position of the second corner node
2270  // along a given edge of the element
2271  Vector<double> second_node_x(2,0.0);
2272 
2273  // Calculate h:
2274  //-------------
2275  // Find out how many elements there are in the bulk mesh
2276  unsigned n_element=bulk_mesh_pt->nelement();
2277 
2278  // Store a pointer which will point to a given element in the bulk mesh
2279  FiniteElement* el_pt=0;
2280 
2281  // Initialise a dummy variable to compare with h
2282  double test_h=0.0;
2283 
2284  // Store the number of edges in a 2D quad element
2285  unsigned n_edge=4;
2286 
2287  // Loop over all of the elements in the bulk mesh
2288  for (unsigned i=0;i<n_element;i++)
2289  {
2290  // Upcast the pointer to the i-th element to a FiniteElement pointer
2291  el_pt=dynamic_cast<FiniteElement*>(bulk_mesh_pt->element_pt(i));
2292 
2293  // Loop over the edges of the element
2294  for (unsigned j=0;j<n_edge;j++)
2295  {
2296  // Get the local node ID of the first corner node on the j-th edge
2297  first_node_pt=el_pt->node_pt(corner_node_id[j]);
2298 
2299  // Get the local node ID of the second corner node on the j-th edge
2300  second_node_pt=el_pt->node_pt(corner_node_id[(j+1)%4]);
2301 
2302  // Obtain the (Eulerian) position of the first corner node
2303  for (unsigned n=0;n<2;n++)
2304  {
2305  // Grab the n-th coordinate of this node
2306  first_node_x[n]=first_node_pt->x(n);
2307  }
2308 
2309  // Obtain the (Eulerian) position of the second corner node
2310  for (unsigned n=0;n<2;n++)
2311  {
2312  // Grab the n-th coordinate of this node
2313  second_node_x[n]=second_node_pt->x(n);
2314  }
2315 
2316  // Reset the value of test_h
2317  test_h=0.0;
2318 
2319  // Calculate the norm of (second_node_x-first_node_x)
2320  for (unsigned n=0;n<2;n++)
2321  {
2322  // Update the value of test_h
2323  test_h+=pow(second_node_x[n]-first_node_x[n],2.0);
2324  }
2325 
2326  // Square root the value of h
2327  test_h=sqrt(test_h);
2328 
2329  // Check if the value of test_h is greater than h
2330  if (test_h>h)
2331  {
2332  // If the value of test_h is greater than h then store it
2333  h=test_h;
2334  }
2335  } // for (unsigned j=0;j<n_edge;j++)
2336  } // for (unsigned i=0;i<n_element;i++)
2337  } // End of maximum_edge_width
2338 
2339 //==========================================================================
2340 /// \short Find the value of the parameters h on the level-th problem in
2341 /// the hierarchy. The value of h is determined by looping over each element
2342 /// in the mesh and calculating the length of each side and take the maximum
2343 /// value. Note, this is a heuristic calculation; if the mesh is non-uniform
2344 /// or adaptive refinement is used then the value of h, is not the same
2345 /// everywhere so we find the maximum edge width instead. If, however,
2346 /// uniform refinement is used on a uniform mesh (using quad elements) then
2347 /// this will return the correct value of h.
2348 ///
2349 /// This is the explicit template specialisation of the case DIM=3. The
2350 /// calculation of h is different here. In 2D we were able to loop over
2351 /// each pair of nodes in an anti-clockwise manner since the only node
2352 /// pairs were {(C0,C1),(C1,C2),(C2,C3),(C3,C0)} where CN denotes the N-th
2353 /// corner in the element. In 3D this method cannot be used since we have
2354 /// 12 edges to consider.
2355 //==========================================================================
2356  template<>
2358  double& h)
2359  {
2360  // Create a pointer to the "bulk" mesh
2361  TreeBasedRefineableMeshBase* bulk_mesh_pt=
2362  Mg_hierarchy_pt[level]->mg_bulk_mesh_pt();
2363 
2364  //--------------------------------
2365  // Find the maximum edge width, h:
2366  //--------------------------------
2367  // Reset the value of h
2368  h=0.0;
2369 
2370  // Find out how many nodes there are along one edge of the first element.
2371  // We assume here that all elements have the same number of nodes
2372  unsigned nnode_1d=dynamic_cast<FiniteElement*>
2373  (bulk_mesh_pt->element_pt(0))->nnode_1d();
2374 
2375  // Sort out corner node IDs:
2376  //--------------------------
2377  // Grab the octree pointer from the first element in the bulk mesh
2378  OcTree* octree_pt=dynamic_cast<RefineableQElement<3>*>
2379  (bulk_mesh_pt->element_pt(0))->octree_pt();
2380 
2381  // Initialise a vector to store local node IDs of the corners
2382  Vector<unsigned> corner_node_id(8,0);
2383 
2384  // Identify the local node ID of the first corner
2385  corner_node_id[0]=octree_pt->
2386  vertex_to_node_number(OcTreeNames::LDB,nnode_1d);
2387 
2388  // Identify the local node ID of the second corner
2389  corner_node_id[1]=octree_pt->
2390  vertex_to_node_number(OcTreeNames::RDB,nnode_1d);
2391 
2392  // Identify the local node ID of the third corner
2393  corner_node_id[2]=octree_pt->
2394  vertex_to_node_number(OcTreeNames::LUB,nnode_1d);
2395 
2396  // Identify the local node ID of the fourth corner
2397  corner_node_id[3]=octree_pt->
2398  vertex_to_node_number(OcTreeNames::RUB,nnode_1d);
2399 
2400  // Identify the local node ID of the fifth corner
2401  corner_node_id[4]=octree_pt->
2402  vertex_to_node_number(OcTreeNames::LDF,nnode_1d);
2403 
2404  // Identify the local node ID of the sixth corner
2405  corner_node_id[5]=octree_pt->
2406  vertex_to_node_number(OcTreeNames::RDF,nnode_1d);
2407 
2408  // Identify the local node ID of the seventh corner
2409  corner_node_id[6]=octree_pt->
2410  vertex_to_node_number(OcTreeNames::LUF,nnode_1d);
2411 
2412  // Identify the local node ID of the eighth corner
2413  corner_node_id[7]=octree_pt->
2414  vertex_to_node_number(OcTreeNames::RUF,nnode_1d);
2415 
2416  // Sort out the local node ID pairs:
2417  //----------------------------------
2418  // Store the number of edges in a 3D cubic element
2419  unsigned n_edge=12;
2420 
2421  // Create a vector to store the pairs of adjacent nodes
2422  Vector<std::pair<unsigned,unsigned> > edge_node_pair(n_edge);
2423 
2424  // First edge
2425  edge_node_pair[0]=std::make_pair(corner_node_id[0],corner_node_id[1]);
2426 
2427  // Second edge
2428  edge_node_pair[1]=std::make_pair(corner_node_id[0],corner_node_id[2]);
2429 
2430  // Third edge
2431  edge_node_pair[2]=std::make_pair(corner_node_id[0],corner_node_id[4]);
2432 
2433  // Fourth edge
2434  edge_node_pair[3]=std::make_pair(corner_node_id[1],corner_node_id[3]);
2435 
2436  // Fifth edge
2437  edge_node_pair[4]=std::make_pair(corner_node_id[1],corner_node_id[5]);
2438 
2439  // Sixth edge
2440  edge_node_pair[5]=std::make_pair(corner_node_id[2],corner_node_id[3]);
2441 
2442  // Seventh edge
2443  edge_node_pair[6]=std::make_pair(corner_node_id[2],corner_node_id[6]);
2444 
2445  // Eighth edge
2446  edge_node_pair[7]=std::make_pair(corner_node_id[3],corner_node_id[7]);
2447 
2448  // Ninth edge
2449  edge_node_pair[8]=std::make_pair(corner_node_id[4],corner_node_id[5]);
2450 
2451  // Tenth edge
2452  edge_node_pair[9]=std::make_pair(corner_node_id[4],corner_node_id[6]);
2453 
2454  // Eleventh edge
2455  edge_node_pair[10]=std::make_pair(corner_node_id[5],corner_node_id[7]);
2456 
2457  // Twelfth edge
2458  edge_node_pair[11]=std::make_pair(corner_node_id[6],corner_node_id[7]);
2459 
2460  // Create storage for the nodal information:
2461  //------------------------------------------
2462  // Pointer to the first corner node on the j-th edge
2463  Node* first_node_pt=0;
2464 
2465  // Pointer to the second corner node on the j-th edge
2466  Node* second_node_pt=0;
2467 
2468  // Vector to store the (Eulerian) position of the first corner node
2469  // along a given edge of the element
2470  Vector<double> first_node_x(3,0.0);
2471 
2472  // Vector to store the (Eulerian) position of the second corner node
2473  // along a given edge of the element
2474  Vector<double> second_node_x(3,0.0);
2475 
2476  // Calculate h:
2477  //-------------
2478  // Find out how many elements there are in the bulk mesh
2479  unsigned n_element=bulk_mesh_pt->nelement();
2480 
2481  // Store a pointer which will point to a given element in the bulk mesh
2482  FiniteElement* el_pt=0;
2483 
2484  // Initialise a dummy variable to compare with h
2485  double test_h=0.0;
2486 
2487  // Loop over all of the elements in the bulk mesh
2488  for (unsigned i=0;i<n_element;i++)
2489  {
2490  // Upcast the pointer to the i-th element to a FiniteElement pointer
2491  el_pt=dynamic_cast<FiniteElement*>(bulk_mesh_pt->element_pt(i));
2492 
2493  // Loop over the edges of the element
2494  for (unsigned j=0;j<n_edge;j++)
2495  {
2496  // Get the local node ID of the first corner node on the j-th edge
2497  first_node_pt=el_pt->node_pt(edge_node_pair[j].first);
2498 
2499  // Get the local node ID of the second corner node on the j-th edge
2500  second_node_pt=el_pt->node_pt(edge_node_pair[j].second);
2501 
2502  // Obtain the (Eulerian) position of the first corner node
2503  for (unsigned n=0;n<3;n++)
2504  {
2505  // Grab the n-th coordinate of this node
2506  first_node_x[n]=first_node_pt->x(n);
2507  }
2508 
2509  // Obtain the (Eulerian) position of the second corner node
2510  for (unsigned n=0;n<3;n++)
2511  {
2512  // Grab the n-th coordinate of this node
2513  second_node_x[n]=second_node_pt->x(n);
2514  }
2515 
2516  // Reset the value of test_h
2517  test_h=0.0;
2518 
2519  // Calculate the norm of (second_node_x-first_node_x)
2520  for (unsigned n=0;n<3;n++)
2521  {
2522  // Update the value of test_h
2523  test_h+=pow(second_node_x[n]-first_node_x[n],2.0);
2524  }
2525 
2526  // Square root the value of h
2527  test_h=sqrt(test_h);
2528 
2529  // Check if the value of test_h is greater than h
2530  if (test_h>h)
2531  {
2532  // If the value of test_h is greater than h then store it
2533  h=test_h;
2534  }
2535  } // for (unsigned j=0;j<n_edge;j++)
2536  } // for (unsigned i=0;i<n_element;i++)
2537  } // End of maximum_edge_width
2538 
2539 //=========================================================================
2540 /// \short Set up the smoothers on all levels
2541 //=========================================================================
2542  template<unsigned DIM>
2544  {
2545  // Initialise the timer start variable
2546  double t_m_start=0.0;
2547 
2548  // Start the clock (if we're allowed to time things)
2549  if (!Suppress_all_output)
2550  {
2551  // Notify user
2552  oomph_info << "Starting the setup of all smoothers.\n" << std::endl;
2553 
2554  // Start the clock
2555  t_m_start=TimingHelpers::timer();
2556  }
2557 
2558  // Loop over the levels and assign the pre- and post-smoother pointers
2559  for (unsigned i=0;i<Nlevel-1;i++)
2560  {
2561  // If the pre-smoother factory function pointer hasn't been assigned
2562  // then we simply create a new instance of the ComplexDampedJacobi
2563  // smoother which is the default pre-smoother
2564  if (0==Pre_smoother_factory_function_pt)
2565  {
2566  // If the value of kh is strictly less than 0.5 then use damped Jacobi
2567  // as a smoother on this level otherwise use complex GMRES as a smoother
2568  // [see Elman et al."A multigrid method enhanced by Krylov subspace
2569  // iteration for discrete Helmholtz equations", p.1305]
2570  if (Wavenumber*Max_edge_width[i]<0.5)
2571  {
2572  // Make a new ComplexDampedJacobi object and assign its pointer
2573  ComplexDampedJacobi<CRDoubleMatrix>* damped_jacobi_pt=
2575 
2576  // Assign the pre-smoother pointer
2577  Pre_smoothers_storage_pt[i]=damped_jacobi_pt;
2578 
2579  // Make the smoother calculate the value of omega and store it
2580  damped_jacobi_pt->calculate_omega(Wavenumber,Max_edge_width[i]);
2581  }
2582  else
2583  {
2584  // Make a new ComplexGMRES object and assign its pointer
2586 
2587  // Assign the pre-smoother pointer
2588  Pre_smoothers_storage_pt[i]=gmres_pt;
2589  }
2590  }
2591  // Otherwise we use the pre-smoother factory function pointer to
2592  // generate a new pre-smoother
2593  else
2594  {
2595  // Get a pointer to an object of the same type as the pre-smoother
2596  Pre_smoothers_storage_pt[i]=
2597  (*Pre_smoother_factory_function_pt)();
2598  } // if (0==Pre_smoother_factory_function_pt)
2599 
2600  // If the post-smoother factory function pointer hasn't been assigned
2601  // then we simply create a new instance of the ComplexDampedJacobi smoother
2602  // which is the default post-smoother
2603  if (0==Post_smoother_factory_function_pt)
2604  {
2605  // If the value of kh is strictly less than 0.52 then use damped Jacobi
2606  // as a smoother on this level otherwise use complex GMRES as a smoother
2607  // [see Elman et al."A multigrid method enhanced by Krylov subspace
2608  // iteration for discrete Helmholtz equations", p.1295]
2609  if (Wavenumber*Max_edge_width[i]<0.5)
2610  {
2611  // Make a new ComplexDampedJacobi object and assign its pointer
2612  ComplexDampedJacobi<CRDoubleMatrix>* damped_jacobi_pt=
2614 
2615  // Assign the pre-smoother pointer
2616  Post_smoothers_storage_pt[i]=damped_jacobi_pt;
2617 
2618  // Make the smoother calculate the value of omega and store it
2619  damped_jacobi_pt->calculate_omega(Wavenumber,Max_edge_width[i]);
2620  }
2621  else
2622  {
2623  // Make a new ComplexGMRES object and assign its pointer
2625 
2626  // Assign the pre-smoother pointer
2627  Post_smoothers_storage_pt[i]=gmres_pt;
2628  }
2629  }
2630  // Otherwise we use the post-smoother factory function pointer to
2631  // generate a new post-smoother
2632  else
2633  {
2634  // Get a pointer to an object of the same type as the post-smoother
2635  Post_smoothers_storage_pt[i]=
2636  (*Post_smoother_factory_function_pt)();
2637  }
2638  } // if (0==Post_smoother_factory_function_pt)
2639 
2640  // Set the tolerance for the pre- and post-smoothers. The norm of the
2641  // solution which is compared against the tolerance is calculated
2642  // differently to the multigrid solver. To ensure that the smoother
2643  // continues to solve for the prescribed number of iterations we
2644  // lower the tolerance
2645  for (unsigned i=0;i<Nlevel-1;i++)
2646  {
2647  // Set the tolerance of the i-th level pre-smoother
2648  Pre_smoothers_storage_pt[i]->tolerance()=1.0e-16;
2649 
2650  // Set the tolerance of the i-th level post-smoother
2651  Post_smoothers_storage_pt[i]->tolerance()=1.0e-16;
2652  }
2653 
2654  // Set the number of pre- and post-smoothing iterations in each
2655  // pre- and post-smoother
2656  for (unsigned i=0;i<Nlevel-1;i++)
2657  {
2658  // Set the number of pre-smoothing iterations as the value of Npre_smooth
2659  Pre_smoothers_storage_pt[i]->max_iter()=Npre_smooth;
2660 
2661  // Set the number of pre-smoothing iterations as the value of Npost_smooth
2662  Post_smoothers_storage_pt[i]->max_iter()=Npost_smooth;
2663  }
2664 
2665  // Complete the setup of all of the smoothers
2666  for (unsigned i=0;i<Nlevel-1;i++)
2667  {
2668  // Pass a pointer to the system matrix on the i-th level to the i-th
2669  // level pre-smoother
2670  Pre_smoothers_storage_pt[i]->
2671  complex_smoother_setup(Mg_matrices_storage_pt[i]);
2672 
2673  // Pass a pointer to the system matrix on the i-th level to the i-th
2674  // level post-smoother
2675  Post_smoothers_storage_pt[i]->
2676  complex_smoother_setup(Mg_matrices_storage_pt[i]);
2677  }
2678 
2679  // Set up the distributions of each smoother
2680  for (unsigned i=0;i<Nlevel-1;i++)
2681  {
2682  // Get the number of dofs on the i-th level of the hierarchy.
2683  // This will be the same as the size of the solution vector
2684  // associated with the i-th level
2685  unsigned n_dof=X_mg_vectors_storage[i][0].nrow();
2686 
2687  // Create a LinearAlgebraDistribution which will be passed to the
2688  // linear solver
2689  LinearAlgebraDistribution dist(Mg_hierarchy_pt[i]->communicator_pt(),
2690  n_dof,false);
2691 
2692 #ifdef PARANOID
2693 #ifdef OOMPH_HAS_MPI
2694  // Set up the warning messages
2695  std::string warning_message="Setup of pre- and post-smoother distribution ";
2696  warning_message+="has not been tested with MPI.";
2697 
2698  // If we're not running the code in serial
2699  if (dist.communicator_pt()->nproc()>1)
2700  {
2701  // Throw a warning
2702  OomphLibWarning(warning_message,
2703  OOMPH_CURRENT_FUNCTION,
2704  OOMPH_EXCEPTION_LOCATION);
2705  }
2706 #endif
2707 #endif
2708 
2709  // Build the distribution of the pre-smoother
2710  Pre_smoothers_storage_pt[i]->build_distribution(dist);
2711 
2712  // Build the distribution of the post-smoother
2713  Post_smoothers_storage_pt[i]->build_distribution(dist);
2714  }
2715 
2716  // Disable the smoother output
2717  if (!Doc_time)
2718  {
2719  // Disable time documentation from the smoothers and the SuperLU linear
2720  // solver (this latter is only done on the coarsest level where it is
2721  // required)
2722  disable_smoother_and_superlu_doc_time();
2723  }
2724 
2725  // If we're allowed to output
2726  if (!Suppress_all_output)
2727  {
2728  // Stop clock
2729  double t_m_end=TimingHelpers::timer();
2730  double total_setup_time=double(t_m_end-t_m_start);
2731  oomph_info << "CPU time for setup of smoothers on all levels [sec]: "
2732  << total_setup_time << std::endl;
2733 
2734  // Notify user that the extraction is complete
2735  oomph_info << "\n=================="
2736  << "Smoother Setup Complete"
2737  << "=================" << std::endl;
2738  }
2739  } // End of setup_smoothers
2740 
2741 
2742 //===================================================================
2743 /// \short Set up the interpolation matrices
2744 //===================================================================
2745  template<unsigned DIM>
2747  {
2748  // Variable to hold the number of sons in an element
2749  unsigned n_sons;
2750 
2751  // Number of son elements
2752  if (DIM==2)
2753  {
2754  n_sons=4;
2755  }
2756  else if (DIM==3)
2757  {
2758  n_sons=8;
2759  }
2760  else
2761  {
2762  std::ostringstream error_message_stream;
2763  error_message_stream << "DIM should be 2 or 3 not "
2764  << DIM << std::endl;
2765  throw OomphLibError(error_message_stream.str(),
2766  OOMPH_CURRENT_FUNCTION,
2767  OOMPH_EXCEPTION_LOCATION);
2768  }
2769 
2770 #ifdef PARANOID
2771  // PARANOID check - for our implementation we demand that the first
2772  // submesh is the refineable bulk mesh that is provided by the function
2773  // mg_bulk_mesh_pt. Since each mesh in the hierarchy will be set up
2774  // in the same manner through the problem constructor we only needed
2775  // to check the finest level mesh
2776  if (Mg_hierarchy_pt[0]->mesh_pt(0)!=Mg_hierarchy_pt[0]->mg_bulk_mesh_pt())
2777  {
2778  // Create an output stream
2779  std::ostringstream error_message_stream;
2780 
2781  // Create the error message
2782  error_message_stream << "MG solver requires the first submesh be the "
2783  << "refineable bulk mesh provided by the user."
2784  << std::endl;
2785 
2786  // Throw the error message
2787  throw OomphLibError(error_message_stream.str(),
2788  OOMPH_CURRENT_FUNCTION,
2789  OOMPH_EXCEPTION_LOCATION);
2790  }
2791 #endif
2792 
2793  // Vector of local coordinates in the element
2794  Vector<double> s(DIM,0.0);
2795 
2796  // Vector to contain the (Eulerian) spatial location of the fine node.
2797  // This will only be used if we have a PML layer attached to the mesh
2798  // which will require the use of locate_zeta functionality
2799  Vector<double> fine_node_position(DIM,0.0);
2800 
2801  // Find the number of nodes in an element in the mesh. Since this
2802  // quantity will be the same in all meshes we can precompute it here
2803  // using the original problem pointer
2804  unsigned nnod_element=dynamic_cast<FiniteElement*>
2805  (Mg_problem_pt->mesh_pt()->element_pt(0))->nnode();
2806 
2807  // Initialise a null pointer which will be used to point to an object
2808  // of the class MeshAsGeomObject
2809  MeshAsGeomObject* coarse_mesh_from_obj_pt=0;
2810 
2811  // Loop over each level (apart from the coarsest level; an interpolation
2812  // matrix and thus a restriction matrix is not needed here), with 0 being
2813  // the finest level and Nlevel-1 being the coarsest level
2814  for (unsigned level=0;level<Nlevel-1;level++)
2815  {
2816  // Store the ID of the fine level
2817  unsigned fine_level=level;
2818 
2819  // Store the ID of the coarse level
2820  unsigned coarse_level=level+1;
2821 
2822  // Make a pointer to the mesh on the finer level and dynamic_cast
2823  // it as an object of the refineable mesh class.
2824  Mesh* ref_fine_mesh_pt=Mg_hierarchy_pt[fine_level]->mesh_pt();
2825 
2826  // Make a pointer to the mesh on the coarse level and dynamic_cast
2827  // it as an object of the refineable mesh class
2828  Mesh* ref_coarse_mesh_pt=Mg_hierarchy_pt[coarse_level]->mesh_pt();
2829 
2830  // Access information about the number of elements in the fine mesh
2831  // from the pointer to the fine mesh (to loop over the rows of the
2832  // interpolation matrix)
2833  unsigned fine_n_element=ref_fine_mesh_pt->nelement();
2834 
2835  // Find the number of elements in the bulk mesh
2836  unsigned n_bulk_mesh_element=
2837  Mg_hierarchy_pt[fine_level]->mg_bulk_mesh_pt()->nelement();
2838 
2839  // If there are elements apart from those in the bulk mesh
2840  // then we require locate_zeta functionality. For this reason
2841  // we have to assign a value to the MeshAsGeomObject pointer
2842  // which we do by creating a MeshAsGeomObject from the coarse
2843  // mesh pointer
2844  if (fine_n_element>n_bulk_mesh_element)
2845  {
2846  // Assign the pointer to coarse_mesh_from_obj_pt
2847  coarse_mesh_from_obj_pt=
2848  new MeshAsGeomObject(Mg_hierarchy_pt[coarse_level]->mesh_pt());
2849  }
2850 
2851  // Find the number of dofs in the fine mesh
2852  unsigned n_fine_dof=Mg_hierarchy_pt[fine_level]->ndof();
2853 
2854  // Find the number of dofs in the coarse mesh
2855  unsigned n_coarse_dof=Mg_hierarchy_pt[coarse_level]->ndof();
2856 
2857  // To get the number of rows in the interpolation matrix we divide
2858  // the number of dofs on each level by 2 since there are 2 dofs at
2859  // each node in the mesh corresponding to the real and imaginary
2860  // part of the solution:
2861 
2862  // Get the numbers of rows in the interpolation matrix.
2863  unsigned n_row=n_fine_dof/2.0;
2864 
2865  // Get the numbers of columns in the interpolation matrix
2866  unsigned n_col=n_coarse_dof/2.0;
2867 
2868  // Mapping relating the pointers to related elements in the coarse
2869  // and fine meshes: coarse_mesh_element_pt[fine_mesh_element_pt]. If
2870  // the element in the fine mesh has been unrefined between these two
2871  // levels, this map returns the father element in the coarsened mesh.
2872  // If this element in the fine mesh has not been unrefined, the map
2873  // returns the pointer to the same-sized equivalent element in the
2874  // coarsened mesh.
2875  std::map<RefineableQElement<DIM>*,
2876  RefineableQElement<DIM>*> coarse_mesh_reference_element_pt;
2877 
2878  // Variable to count the number of elements in the fine mesh
2879  unsigned e_fine=0;
2880 
2881  // Variable to count the number of elements in the coarse mesh
2882  unsigned e_coarse=0;
2883 
2884  // While loop over fine elements (while because we're incrementing the
2885  // counter internally if the element was unrefined...). We only bother
2886  // going until we've covered all of the elements in the bulk mesh
2887  // because once we reach the PML layer (if there is one) there will be
2888  // no tree structure to match in between levels as PML elements are
2889  // simply regenerated once the bulk mesh has been refined.
2890  while (e_fine<n_bulk_mesh_element)
2891  {
2892  // Pointer to element in fine mesh
2893  RefineableQElement<DIM>* el_fine_pt=
2894  dynamic_cast<RefineableQElement<DIM>*>
2895  (ref_fine_mesh_pt->finite_element_pt(e_fine));
2896 
2897 #ifdef PARANOID
2898  // Make sure the coarse level element pointer is not a null pointer. If
2899  // it is something has gone wrong
2900  if (e_coarse>ref_coarse_mesh_pt->nelement()-1)
2901  {
2902  // Create an output stream
2903  std::ostringstream error_message_stream;
2904 
2905  // Create an error message
2906  error_message_stream << "The coarse level mesh has "
2907  << ref_coarse_mesh_pt->nelement()
2908  << " elements but the coarse\nelement loop "
2909  << "is looking at the "
2910  << e_coarse << "-th element!" << std::endl;
2911 
2912  // Throw the error message
2913  throw OomphLibError(error_message_stream.str(),
2914  OOMPH_CURRENT_FUNCTION,
2915  OOMPH_EXCEPTION_LOCATION);
2916  }
2917 #endif
2918 
2919  // Pointer to element in coarse mesh
2920  RefineableQElement<DIM>* el_coarse_pt=
2921  dynamic_cast<RefineableQElement<DIM>*>
2922  (ref_coarse_mesh_pt->finite_element_pt(e_coarse));
2923 
2924  // If the levels are different then the element in the fine
2925  // mesh has been unrefined between these two levels
2926  if (el_fine_pt->tree_pt()->level()>el_coarse_pt->tree_pt()->level())
2927  {
2928  // The element in the fine mesh has been unrefined between these two
2929  // levels. Hence it and its three brothers (ASSUMED to be stored
2930  // consecutively in the Mesh's vector of pointers to its constituent
2931  // elements -- we'll check this!) share the same father element in
2932  // the coarse mesh, currently pointed to by el_coarse_pt.
2933  for (unsigned i=0;i<n_sons;i++)
2934  {
2935  // Set mapping to father element in coarse mesh
2936  coarse_mesh_reference_element_pt[
2937  dynamic_cast<RefineableQElement<DIM>*>
2938  (ref_fine_mesh_pt->finite_element_pt(e_fine))]=el_coarse_pt;
2939 
2940  // Increment counter for elements in fine mesh
2941  e_fine++;
2942  }
2943  }
2944  // The element in the fine mesh has not been unrefined between
2945  // these two levels, so the reference element is the same-sized
2946  // equivalent element in the coarse mesh
2947  else if (el_fine_pt->tree_pt()->level()==el_coarse_pt->tree_pt()->level())
2948  {
2949  // The element in the fine mesh has not been unrefined between these two
2950  // levels
2951  coarse_mesh_reference_element_pt[
2952  dynamic_cast<RefineableQElement<DIM>*>
2953  (ref_fine_mesh_pt->finite_element_pt(e_fine))]=el_coarse_pt;
2954 
2955  // Increment counter for elements in fine mesh
2956  e_fine++;
2957  }
2958  // If the element has been unrefined between levels. Not something
2959  // we can deal with at the moment (although it would be relatively
2960  // simply to implement...).
2961  // Option 1: Find the son elements (from the coarse mesh) associated
2962  // with the father element (from the fine mesh) and extract the
2963  // appropriate nodal values to find the nodal values in the father
2964  // element.
2965  // Option 2: Use the function
2966  // unrefine_uniformly();
2967  // to ensure that the coarser meshes really only have father elements
2968  // or the element itself.
2969  else
2970  {
2971  // Create an output stream
2972  std::ostringstream error_message_stream;
2973 
2974  // Create an error message
2975  error_message_stream << "Element unrefined between levels! Can't "
2976  << "handle this case yet..." << std::endl;
2977 
2978  // Throw the error message
2979  throw OomphLibError(error_message_stream.str(),
2980  OOMPH_CURRENT_FUNCTION,
2981  OOMPH_EXCEPTION_LOCATION);
2982  } // if (el_fine_pt->tree_pt()->level()!=...)
2983 
2984  // Increment counter for elements in coarse mesh
2985  e_coarse++;
2986  } // while(e_fine<n_bulk_mesh_element)
2987 
2988  // To allow an update of a row only once we use STL vectors for bools
2989  std::vector<bool> contribution_made(n_row,false);
2990 
2991  // Create the row start vector whose i-th entry will contain the index
2992  // in column_start where the entries in the i-th row of the matrix start
2993  Vector<int> row_start(n_row+1);
2994 
2995  // Create the column index vector whose entries will store the column
2996  // index of each contribution, i.e. the global equation of the coarse
2997  // mesh node which supplies a contribution. We don't know how many
2998  // entries this will have so we dynamically allocate entries at run time
2999  Vector<int> column_index;
3000 
3001  // Create the value vector which will be used to store the nonzero
3002  // entries in the CRDoubleMatrix. We don't know how many entries this
3003  // will have either so we dynamically allocate its entries at run time
3004  Vector<double> value;
3005 
3006  // The value of index will tell us which row of the interpolation matrix
3007  // we're working on in the following for loop
3008  // DRAIG: Should this worry us? We assume that every node we cover is
3009  // the next node in the mesh (we loop over the elements and the nodes
3010  // inside that). This does work but it may not work for some meshes
3011  unsigned index=0;
3012 
3013  // New loop to go over each element in the fine mesh
3014  for (unsigned k=0;k<fine_n_element;k++)
3015  {
3016  // Set a pointer to the element in the fine mesh
3017  RefineableQElement<DIM>* el_fine_pt=
3018  dynamic_cast<RefineableQElement<DIM>*>
3019  (ref_fine_mesh_pt->finite_element_pt(k));
3020 
3021  // Initialise a null pointer which will point to the corresponding
3022  // coarse mesh element. If we're in the bulk mesh, this will point
3023  // to the parent element of the fine mesh element or itself (if
3024  // there was no refinement between the levels). If, however, we're
3025  // in the PML layer then this will point to element that encloses
3026  // the fine mesh PML element
3027  RefineableQElement<DIM>* el_coarse_pt=0;
3028 
3029  // Variable to store the son type of the fine mesh element with
3030  // respect to the corresponding coarse mesh element (set to OMEGA
3031  // if no unrefinement took place)
3032  int son_type=0;
3033 
3034  // If we're in the bulk mesh we can assign the coarse mesh element
3035  // pointer right now using the map coarse_mesh_reference_element_pt
3036  if (k<n_bulk_mesh_element)
3037  {
3038  // Get the reference element (either the father element or the
3039  // same-sized element) in the coarse mesh
3040  el_coarse_pt=coarse_mesh_reference_element_pt[el_fine_pt];
3041 
3042  // Check if the elements are different on both levels (i.e. to check
3043  // if any unrefinement took place)
3044  if (el_fine_pt->tree_pt()->level()!=el_coarse_pt->tree_pt()->level())
3045  {
3046  // If the element was refined then we need the son type
3047  son_type=el_fine_pt->tree_pt()->son_type();
3048  }
3049  else
3050  {
3051  // If the element is the same in both meshes
3052  son_type=Tree::OMEGA;
3053  }
3054  } // if (k<n_bulk_mesh_element)
3055 
3056  // Loop through all the nodes in an element in the fine mesh
3057  for(unsigned i=0;i<nnod_element;i++)
3058  {
3059  // Row number in interpolation matrix: Global equation number
3060  // of the d.o.f. stored at this node in the fine element
3061  int ii=el_fine_pt->node_pt(i)->eqn_number(0);
3062 
3063  // Check whether or not the node is a proper d.o.f.
3064  if (ii>=0)
3065  {
3066  // Only assign values to the given row of the interpolation matrix
3067  // if they haven't already been assigned. Notice, we check if the
3068  // (ii/2)-th entry has been set. This is because there are two dofs
3069  // at each node so dividing by two will give us the row number
3070  // associated with this node
3071  if(contribution_made[ii/2]==false)
3072  {
3073  // The value of index was initialised when we allocated space
3074  // for the three vectors to store the CRDoubleMatrix. We use
3075  // index to go through the entries of the row_start vector.
3076  // The next row starts at the value.size() (draw out the entries
3077  // in value if this doesn't make sense noting that the storage
3078  // for the vector 'value' is dynamically allocated)
3079  row_start[index]=value.size();
3080 
3081  // If we're in the PML layer then we need to find the parent element
3082  // associated with a given node using locate_zeta. Unfortunately,
3083  // if this is the case and a given local node in the fine mesh
3084  // element lies on the interface between two elements (E1) and (E2)
3085  // in the coarse mesh then either element will be returned. Suppose,
3086  // for instance, a pointer to (E1) is returned. If the next local
3087  // node lies in the (E2) then the contributions which we pick up
3088  // from the local nodes in (E1) will most likely be incorrect while
3089  // the column index (the global equation number of the coarse mesh
3090  // node) corresponding to the given contribution will definitely be
3091  // incorrect. If we're not using linear quad elements we can get
3092  // around this by using locate_zeta carefully by identifying a node
3093  // which is internal to the fine mesh element. This will allow us to
3094  // correctly obtain the corresponding element in the coarse mesh
3095  if (k>=n_bulk_mesh_element)
3096  {
3097  // Get the (Eulerian) spatial location of the i-th local node
3098  // in the fine mesh element
3099  el_fine_pt->node_pt(i)->position(fine_node_position);
3100 
3101  // Initalise a null pointer to point to an object of the class
3102  // GeomObject
3103  GeomObject* el_pt=0;
3104 
3105  // Get the reference element (either the father element or the
3106  // same-sized element) in the coarse-mesh PML layer using
3107  // the locate_zeta functionality
3108  coarse_mesh_from_obj_pt->locate_zeta(fine_node_position,el_pt,s);
3109 
3110  // Upcast the GeomElement object to a RefineableQElement object
3111  el_coarse_pt=dynamic_cast<RefineableQElement<DIM>*>(el_pt);
3112  }
3113  else
3114  {
3115  // Calculate the local coordinates of the given node
3116  el_fine_pt->local_coordinate_of_node(i,s);
3117 
3118  // Find the local coordinates s, of the present node, in the
3119  // reference element in the coarse mesh, given the element's
3120  // son_type (e.g. SW,SE... )
3121  level_up_local_coord_of_node(son_type,s);
3122  }
3123 
3124  // Allocate space for shape functions in the coarse mesh
3125  Shape psi(el_coarse_pt->nnode());
3126 
3127  // Set the shape function in the reference element
3128  el_coarse_pt->shape(s,psi);
3129 
3130  // Auxiliary storage
3131  std::map<unsigned,double> contribution;
3132  Vector<unsigned> keys;
3133 
3134  // Find the number of nodes in an element in the coarse mesh
3135  unsigned nnod_coarse=el_coarse_pt->nnode();
3136 
3137  // Loop through all the nodes in the reference element
3138  for (unsigned j=0;j<nnod_coarse;j++)
3139  {
3140  // Column number in interpolation matrix: Global equation
3141  // number of the d.o.f. stored at this node in the coarse
3142  // element
3143  int jj=el_coarse_pt->node_pt(j)->eqn_number(0);
3144 
3145  // If the value stored at this node is pinned or hanging
3146  if (jj<0)
3147  {
3148  // Hanging node: In this case we need to accumulate the
3149  // contributions from the master nodes
3150  if (el_coarse_pt->node_pt(j)->is_hanging())
3151  {
3152  // Find the number of master nodes of the hanging
3153  // the node in the reference element
3154  HangInfo* hang_info_pt=el_coarse_pt->node_pt(j)->hanging_pt();
3155  unsigned nmaster=hang_info_pt->nmaster();
3156 
3157  // Loop over the master nodes
3158  for (unsigned i_master=0;i_master<nmaster;i_master++)
3159  {
3160  // Set up a pointer to the master node
3161  Node* master_node_pt=hang_info_pt->master_node_pt(i_master);
3162 
3163  // The column number in the interpolation matrix: the
3164  // global equation number of the d.o.f. stored at this master
3165  // node for the coarse element
3166  int master_jj=master_node_pt->eqn_number(0);
3167 
3168  // Is the master node a proper d.o.f.?
3169  if (master_jj>=0)
3170  {
3171  // If the weight of the master node is non-zero
3172  if (psi(j)*hang_info_pt->master_weight(i_master)!=0.0)
3173  {
3174  contribution[master_jj/2]+=
3175  psi(j)*hang_info_pt->master_weight(i_master);
3176  }
3177  } // if (master_jj>=0)
3178  } // for (unsigned i_master=0;i_master<nmaster;i_master++)
3179  } // if (el_coarse_pt->node_pt(j)->is_hanging())
3180  }
3181  // In the case that the node is not pinned or hanging
3182  else
3183  {
3184  // If we can get a nonzero contribution from the shape function
3185  if (psi(j)!=0.0)
3186  {
3187  contribution[jj/2]+=psi(j);
3188  }
3189  } // if (jj<0) else
3190  } // for (unsigned j=0;j<nnod_coarse;j++)
3191 
3192  // Put the contributions into the value vector
3193  for (std::map<unsigned,double>::iterator it=contribution.begin();
3194  it!=contribution.end(); ++it)
3195  {
3196  if (it->second!=0)
3197  {
3198  // The first entry in contribution is the column index
3199  column_index.push_back(it->first);
3200 
3201  // The second entry in contribution is the value of the
3202  // contribution
3203  value.push_back(it->second);
3204  }
3205  } // for (std::map<unsigned,double>::iterator it=...)
3206 
3207  // Increment the value of index by 1 to indicate that we have
3208  // finished the row, or equivalently, covered another global
3209  // node in the fine mesh
3210  index++;
3211 
3212  // Change the entry in contribution_made to true now to indicate
3213  // that the row has been filled
3214  contribution_made[ii/2]=true;
3215  } // if(contribution_made[ii/2]==false)
3216  } // if (ii>=0)
3217  } // for(unsigned i=0;i<nnod_element;i++)
3218  } // for (unsigned k=0;k<fine_n_element;k++)
3219 
3220  // Set the last entry in the row_start vector
3221  row_start[n_row]=value.size();
3222 
3223  // Set the interpolation matrix to be that formed as the CRDoubleMatrix
3224  // using the vectors value, row_start, column_index and the value
3225  // of fine_n_unknowns and coarse_n_unknowns
3226  interpolation_matrix_set(level,
3227  value,
3228  column_index,
3229  row_start,
3230  n_col,
3231  n_row);
3232  } // for (unsigned level=0;level<Nlevel-1;level++)
3233  } // End of setup_interpolation_matrices
3234 
3235 //===================================================================
3236 /// \short Setup the interpolation matrices
3237 //===================================================================
3238  template<unsigned DIM>
3241  {
3242 
3243 #ifdef PARANOID
3244  if ((DIM!=2)&&(DIM!=3))
3245  {
3246  std::ostringstream error_message_stream;
3247  error_message_stream << "DIM should be 2 or 3 not "
3248  << DIM << std::endl;
3249  throw OomphLibError(error_message_stream.str(),
3250  OOMPH_CURRENT_FUNCTION,
3251  OOMPH_EXCEPTION_LOCATION);
3252  }
3253 #endif
3254 
3255  // Vector of local coordinates in the element
3256  Vector<double> s(DIM,0.0);
3257 
3258  // Loop over each level (apart from the coarsest level; an interpolation
3259  // matrix and thus a restriction matrix is not needed here), with 0 being
3260  // the finest level and Nlevel-1 being the coarsest level
3261  for (unsigned level=0;level<Nlevel-1;level++)
3262  {
3263  // Assign values to a couple of variables to help out
3264  unsigned coarse_level=level+1;
3265  unsigned fine_level=level;
3266 
3267  // Make a pointer to the mesh on the finer level and dynamic_cast
3268  // it as an object of the refineable mesh class
3269  Mesh* ref_fine_mesh_pt=Mg_hierarchy_pt[fine_level]->mg_bulk_mesh_pt();
3270 
3271  // To use the locate zeta functionality the coarse mesh must be of the
3272  // type MeshAsGeomObject
3273  MeshAsGeomObject* coarse_mesh_from_obj_pt=
3274  new MeshAsGeomObject(Mg_hierarchy_pt[coarse_level]->mg_bulk_mesh_pt());
3275 
3276  // Access information about the number of degrees of freedom
3277  // from the pointers to the problem on each level
3278  unsigned coarse_n_unknowns=Mg_hierarchy_pt[coarse_level]->ndof();
3279  unsigned fine_n_unknowns=Mg_hierarchy_pt[fine_level]->ndof();
3280 
3281  // Make storage vectors to form the interpolation matrix using a
3282  // condensed row matrix (CRDoubleMatrix). The i-th value of the vector
3283  // row_start contains entries which tells us at which entry of the
3284  // vector column_start we start on the i-th row of the actual matrix.
3285  // The entries of column_start indicate the column position of each
3286  // non-zero entry in every row. This runs through the first row
3287  // from left to right then the second row (again, left to right)
3288  // and so on until the end. The entries in value are the entries in
3289  // the interpolation matrix. The vector column_start has the same length
3290  // as the vector value.
3291  Vector<double> value;
3292  Vector<int> column_index;
3293  Vector<int> row_start(fine_n_unknowns+1);
3294 
3295  // Vector to contain the (Eulerian) spatial location of the fine node
3296  Vector<double> fine_node_position(DIM);
3297 
3298  // Find the number of nodes in the mesh
3299  unsigned n_node_fine_mesh=ref_fine_mesh_pt->nnode();
3300 
3301  // Loop over the unknowns in the mesh
3302  for (unsigned i_fine_node=0;i_fine_node<n_node_fine_mesh;i_fine_node++)
3303  {
3304  // Set a pointer to the i_fine_unknown-th node in the fine mesh
3305  Node* fine_node_pt=ref_fine_mesh_pt->node_pt(i_fine_node);
3306 
3307  // Get the global equation number
3308  int i_fine=fine_node_pt->eqn_number(0);
3309 
3310  // If the node is a proper d.o.f.
3311  if (i_fine>=0)
3312  {
3313  // Row number in interpolation matrix: Global equation number
3314  // of the d.o.f. stored at this node in the fine element
3315  row_start[i_fine]=value.size();
3316 
3317  // Get the (Eulerian) spatial location of the fine node
3318  fine_node_pt->position(fine_node_position);
3319 
3320  // Create a null pointer to the GeomObject class
3321  GeomObject* el_pt=0;
3322 
3323  // Get the reference element (either the father element or the
3324  // same-sized element) in the coarse mesh using locate_zeta
3325  coarse_mesh_from_obj_pt->locate_zeta(fine_node_position,el_pt,s);
3326 
3327  // Upcast GeomElement as a FiniteElement
3328  FiniteElement* el_coarse_pt=dynamic_cast<FiniteElement*>(el_pt);
3329 
3330  // Find the number of nodes in the element
3331  unsigned n_node=el_coarse_pt->nnode();
3332 
3333  // Allocate space for shape functions in the coarse mesh
3334  Shape psi(n_node);
3335 
3336  // Calculate the geometric shape functions at local coordinate s
3337  el_coarse_pt->shape(s,psi);
3338 
3339  // Auxiliary storage
3340  std::map<unsigned,double> contribution;
3341  Vector<unsigned> keys;
3342 
3343  // Loop through all the nodes in the (coarse mesh) element containing the
3344  // node pointed to by fine_node_pt (fine mesh)
3345  for(unsigned j_node=0;j_node<n_node;j_node++)
3346  {
3347  // Get the j_coarse_unknown-th node in the coarse element
3348  Node* coarse_node_pt=el_coarse_pt->node_pt(j_node);
3349 
3350  // Column number in interpolation matrix: Global equation number of
3351  // the d.o.f. stored at this node in the coarse element
3352  int j_coarse=coarse_node_pt->eqn_number(0);
3353 
3354  // If the value stored at this node is pinned or hanging
3355  if (j_coarse<0)
3356  {
3357  // Hanging node: In this case we need to accumulate the
3358  // contributions from the master nodes
3359  if (el_coarse_pt->node_pt(j_node)->is_hanging())
3360  {
3361  // Find the number of master nodes of the hanging
3362  // the node in the reference element
3363  HangInfo* hang_info_pt=coarse_node_pt->hanging_pt();
3364  unsigned nmaster=hang_info_pt->nmaster();
3365 
3366  // Loop over the master nodes
3367  for (unsigned i_master=0;i_master<nmaster;i_master++)
3368  {
3369  // Set up a pointer to the master node
3370  Node* master_node_pt=hang_info_pt->master_node_pt(i_master);
3371 
3372  // The column number in the interpolation matrix: the
3373  // global equation number of the d.o.f. stored at this master
3374  // node for the coarse element
3375  int master_jj=master_node_pt->eqn_number(0);
3376 
3377  // Is the master node a proper d.o.f.?
3378  if (master_jj>=0)
3379  {
3380  // If the weight of the master node is non-zero
3381  if (psi(j_node)*hang_info_pt->master_weight(i_master)!=0.0)
3382  {
3383  contribution[master_jj]+=
3384  psi(j_node)*hang_info_pt->master_weight(i_master);
3385  }
3386  } // End of if statement (check it's not a boundary node)
3387  } // End of the loop over the master nodes
3388  } // End of the if statement for only hanging nodes
3389  } // End of the if statement for pinned or hanging nodes
3390  // In the case that the node is not pinned or hanging
3391  else
3392  {
3393  // If we can get a nonzero contribution from the shape function
3394  // at the j_node-th node in the element
3395  if (psi(j_node)!=0.0)
3396  {
3397  contribution[j_coarse]+=psi(j_node);
3398  }
3399  } // End of the if-else statement (check if the node was pinned/hanging)
3400  } // Finished loop over the nodes j in the reference element (coarse)
3401 
3402  // Put the contributions into the value vector
3403  for (std::map<unsigned,double>::iterator it=contribution.begin();
3404  it!=contribution.end(); ++it)
3405  {
3406  if (it->second!=0)
3407  {
3408  value.push_back(it->second);
3409  column_index.push_back(it->first);
3410  }
3411  } // End of putting contributions into the value vector
3412  } // End check (whether or not the fine node was a d.o.f.)
3413  } // End of the for-loop over nodes in the fine mesh
3414 
3415  // Set the last entry of row_start
3416  row_start[fine_n_unknowns]=value.size();
3417 
3418  // Set the interpolation matrix to be that formed as the CRDoubleMatrix
3419  // using the vectors value, row_start, column_index and the value
3420  // of fine_n_unknowns and coarse_n_unknowns
3421  interpolation_matrix_set(level,
3422  value,
3423  column_index,
3424  row_start,
3425  coarse_n_unknowns,
3426  fine_n_unknowns);
3427  } // End of loop over each level
3428  } // End of setup_interpolation_matrices_unstructured
3429 
3430 //=========================================================================
3431 /// \short Given the son type of the element and the local coordinate s of
3432 /// a given node in the son element, return the local coordinate s in its
3433 /// father element. 3D case.
3434 //=========================================================================
3435  template<>
3438  {
3439  // If the element is unrefined between the levels the local coordinate
3440  // of the node in one element is the same as that in the other element
3441  // therefore we only need to perform calculations if the levels are
3442  // different (i.e. son_type is not OMEGA)
3443  if (son_type!=Tree::OMEGA)
3444  {
3445  // Scale the local coordinate from the range [-1,1]x[-1,1]x[-1,1]
3446  // to the range [0,1]x[0,1]x[0,1] to match the width of the local
3447  // coordinate range of the fine element from the perspective of
3448  // the father element. This then simply requires a shift of the
3449  // coordinates to match which type of son element we're dealing with
3450  s[0]=(s[0]+1.0)/2.0;
3451  s[1]=(s[1]+1.0)/2.0;
3452  s[2]=(s[2]+1.0)/2.0;
3453 
3454  // Cases: The son_type determines how the local coordinates should be
3455  // shifted to give the local coordinates in the coarse mesh element
3456  switch(son_type)
3457  {
3458  case OcTreeNames::LDF:
3459  s[0]-=1;
3460  s[1]-=1;
3461  break;
3462 
3463  case OcTreeNames::LDB:
3464  s[0]-=1;
3465  s[1]-=1;
3466  s[2]-=1;
3467  break;
3468 
3469  case OcTreeNames::LUF:
3470  s[0]-=1;
3471  break;
3472 
3473  case OcTreeNames::LUB:
3474  s[0]-=1;
3475  s[2]-=1;
3476  break;
3477 
3478  case OcTreeNames::RDF:
3479  s[1]-=1;
3480  break;
3481 
3482  case OcTreeNames::RDB:
3483  s[1]-=1;
3484  s[2]-=1;
3485  break;
3486 
3487  case OcTreeNames::RUF:
3488  break;
3489 
3490  case OcTreeNames::RUB:
3491  s[2]-=1;
3492  break;
3493  }
3494  } // if son_type!=Tree::OMEGA
3495  } // End of level_up_local_coord_of_node
3496 
3497 //=========================================================================
3498 /// \short Given the son type of the element and the local coordinate s of
3499 /// a given node in the son element, return the local coordinate s in its
3500 /// father element. 2D case.
3501 //=========================================================================
3502  template<>
3505  {
3506  // If the element is unrefined between the levels the local coordinate
3507  // of the node in one element is the same as that in the other element
3508  // therefore we only need to perform calculations if the levels are
3509  // different (i.e. son_type is not OMEGA)
3510  if (son_type!=Tree::OMEGA)
3511  {
3512  // Scale the local coordinate from the range [-1,1]x[-1,1] to the range
3513  // [0,1]x[0,1] to match the width of the local coordinate range of the
3514  // fine element from the perspective of the father element. This
3515  // then simply requires a shift of the coordinates to match which type
3516  // of son element we're dealing with
3517  s[0]=(s[0]+1.0)/2.0;
3518  s[1]=(s[1]+1.0)/2.0;
3519 
3520  // Cases: The son_type determines how the local coordinates should be
3521  // shifted to give the local coordinates in the coarse mesh element
3522  switch(son_type)
3523  {
3524  // If we're dealing with the bottom-left element we need to shift
3525  // the range [0,1]x[0,1] to [-1,0]x[-1,0]
3526  case QuadTreeNames::SW:
3527  s[0]-=1;
3528  s[1]-=1;
3529  break;
3530 
3531  // If we're dealing with the bottom-right element we need to shift
3532  // the range [0,1]x[0,1] to [0,1]x[-1,0]
3533  case QuadTreeNames::SE:
3534  s[1]-=1;
3535  break;
3536 
3537  // If we're dealing with the top-right element we need to shift the
3538  // range [0,1]x[0,1] to [0,1]x[0,1], i.e. nothing needs to be done
3539  case QuadTreeNames::NE:
3540  break;
3541 
3542  // If we're dealing with the top-left element we need to shift
3543  // the range [0,1]x[0,1] to [-1,0]x[0,1]
3544  case QuadTreeNames::NW:
3545  s[0]-=1;
3546  break;
3547  }
3548  } // if son_type!=Tree::OMEGA
3549  } // End of level_up_local_coord_of_node
3550 
3551 //===================================================================
3552 /// \short Restrict residual (computed on current MG level) to
3553 /// next coarser mesh and stick it into the coarse mesh RHS vector
3554 /// using the restriction matrix (if restrict_flag=1) or the transpose
3555 /// of the interpolation matrix (if restrict_flag=2)
3556 //===================================================================
3557  template<unsigned DIM>
3559  {
3560 #ifdef PARANOID
3561  // Check to make sure we can actually restrict the vector
3562  if (!(level<Nlevel-1))
3563  {
3564  // Throw an error
3565  throw OomphLibError("Input level exceeds the possible parameter choice.",
3566  OOMPH_CURRENT_FUNCTION,
3567  OOMPH_EXCEPTION_LOCATION);
3568  }
3569 #endif
3570 
3571  // Multiply the real part of the residual vector by the restriction
3572  // matrix on the level-th level
3573  Restriction_matrices_storage_pt[level]->
3574  multiply(Residual_mg_vectors_storage[level][0],
3575  Rhs_mg_vectors_storage[level+1][0]);
3576 
3577  // Multiply the imaginary part of the residual vector by the restriction
3578  // matrix on the level-th level
3579  Restriction_matrices_storage_pt[level]->
3580  multiply(Residual_mg_vectors_storage[level][1],
3581  Rhs_mg_vectors_storage[level+1][1]);
3582 
3583  } // End of restrict_residual
3584 
3585 //===================================================================
3586 /// \short Interpolate solution at current level onto
3587 /// next finer mesh and correct the solution x at that level
3588 //===================================================================
3589  template<unsigned DIM>
3591  interpolate_and_correct(const unsigned& level)
3592  {
3593 #ifdef PARANOID
3594  // Check to make sure we can actually restrict the vector
3595  if (!(level>0))
3596  {
3597  // Throw an error
3598  throw OomphLibError("Input level exceeds the possible parameter choice.",
3599  OOMPH_CURRENT_FUNCTION,
3600  OOMPH_EXCEPTION_LOCATION);
3601  }
3602 #endif
3603 
3604  // Build distribution of a temporary vector (real part)
3605  DoubleVector temp_soln_r(X_mg_vectors_storage[level-1][0].distribution_pt());
3606 
3607  // Build distribution of a temporary vector (imaginary part)
3608  DoubleVector temp_soln_c(X_mg_vectors_storage[level-1][1].distribution_pt());
3609 
3610  // Interpolate the solution vector
3611  Interpolation_matrices_storage_pt[level-1]->
3612  multiply(X_mg_vectors_storage[level][0],temp_soln_r);
3613 
3614  // Interpolate the solution vector
3615  Interpolation_matrices_storage_pt[level-1]->
3616  multiply(X_mg_vectors_storage[level][1],temp_soln_c);
3617 
3618  // Update the real part of the solution
3619  X_mg_vectors_storage[level-1][0]+=temp_soln_r;
3620 
3621  // Update the imaginary part of the solution
3622  X_mg_vectors_storage[level-1][1]+=temp_soln_c;
3623 
3624  } // End of interpolate_and_correct
3625 
3626 //===================================================================
3627 /// \short Linear solver. This is where the general V-cycle algorithm
3628 /// is implemented
3629 //===================================================================
3630  template<unsigned DIM>
3632  {
3633  // If we're allowed to time things
3634  double t_mg_start=0.0;
3635  if (!Suppress_v_cycle_output)
3636  {
3637  // Start the clock!
3638  t_mg_start=TimingHelpers::timer();
3639  }
3640 
3641  // Current level
3642  unsigned finest_level=0;
3643 
3644  // Initialise the V-cycle counter
3645  V_cycle_counter=0;
3646 
3647  // Calculate the norm of the residual then output
3648  double normalised_residual_norm=residual_norm(finest_level);
3649  if (!Suppress_v_cycle_output)
3650  {
3651  oomph_info << "\nResidual on finest level for V-cycle: "
3652  << normalised_residual_norm << std::endl;
3653  }
3654 
3655  // Outer loop over V-cycles
3656  //-------------------------
3657  // While the tolerance is not met and the maximum number of
3658  // V-cycles has not been completed
3659  while((normalised_residual_norm>Tolerance) &&
3660  (V_cycle_counter!=Nvcycle))
3661  {
3662  // If the user did not wish to suppress the V-cycle output
3663  if (!Suppress_v_cycle_output)
3664  {
3665  // Output the V-cycle counter
3666  oomph_info << "\nStarting V-cycle: " << V_cycle_counter << std::endl;
3667  }
3668 
3669  //---------------------------------------------------------------------
3670  // Loop downwards over all levels that have coarser levels beneath them
3671  //---------------------------------------------------------------------
3672  for (unsigned i=0;i<Nlevel-1;i++)
3673  {
3674  // Initialise X_mg and Residual_mg to 0.0 except for the finest level
3675  // since X_mg contains the current approximation to the solution and
3676  // Residual_mg contains the RHS vector on the finest level
3677  if(i!=0)
3678  {
3679  // Initialise the real part of the solution vector
3680  X_mg_vectors_storage[i][0].initialise(0.0);
3681 
3682  // Initialise the imaginary part of the solution vector
3683  X_mg_vectors_storage[i][1].initialise(0.0);
3684 
3685  // Initialise the real part of the residual vector
3686  Residual_mg_vectors_storage[i][0].initialise(0.0);
3687 
3688  // Initialise the imaginary part of the residual vector
3689  Residual_mg_vectors_storage[i][1].initialise(0.0);
3690  }
3691 
3692  // Perform a few pre-smoothing steps and return vector that contains
3693  // the residuals of the linear system at this level.
3694  pre_smooth(i);
3695 
3696  // Restrict the residual to the next coarser mesh and
3697  // assign it to the RHS vector at that level
3698  restrict_residual(i);
3699 
3700  } // Moving down the V-cycle
3701 
3702  //-----------------------------------------------------------
3703  // Reached the lowest level: Do a direct solve, using the RHS
3704  // vector obtained by restriction from above.
3705  //-----------------------------------------------------------
3706  direct_solve();
3707 
3708  //---------------------------------------------------------------
3709  // Loop upwards over all levels that have finer levels above them
3710  //---------------------------------------------------------------
3711  for (unsigned i=Nlevel-1;i>0;i--)
3712  {
3713  // Interpolate solution at current level onto
3714  // next finer mesh and correct the solution x at that level
3715  interpolate_and_correct(i);
3716 
3717  // Perform a few post-smoothing steps (ignore
3718  // vector that contains the residuals of the linear system
3719  // at this level)
3720  post_smooth(i-1);
3721  }
3722 
3723  // Set counter for number of cycles (for doc)
3724  V_cycle_counter++;
3725 
3726  // Calculate the new residual norm then output (if allowed)
3727  normalised_residual_norm=residual_norm(finest_level);
3728 
3729  // Print the residual on the finest level
3730  if (!Suppress_v_cycle_output)
3731  {
3732  oomph_info << "Residual on finest level of V-cycle: "
3733  << normalised_residual_norm << std::endl;
3734  }
3735  } // End of the V-cycles
3736 
3737  // Copy the solution into the result vector
3738  result=X_mg_vectors_storage[finest_level];
3739 
3740  // Need an extra line space if V-cycle output is suppressed
3741  if (!Suppress_v_cycle_output)
3742  {
3743  oomph_info << std::endl;
3744  }
3745 
3746  // If all output is to be suppressed
3747  if (!Suppress_all_output)
3748  {
3749  // Output number of V-cycles taken to solve
3750  if (normalised_residual_norm<Tolerance)
3751  {
3752  Has_been_solved=true;
3753  }
3754  } // if (!Suppress_all_output)
3755 
3756  // If the V-cycle output isn't suppressed
3757  if (!Suppress_v_cycle_output)
3758  {
3759  // Stop the clock
3760  double t_mg_end=TimingHelpers::timer();
3761  double total_G_setup_time=double(t_mg_end-t_mg_start);
3762  oomph_info << "CPU time for MG solver [sec]: "
3763  << total_G_setup_time << std::endl;
3764  }
3765  } // end of mg_solve
3766 
3767 } // End of namespace oomph
3768 
3769 #endif
double Alpha_shift
Temporary version of the shift – to run bash scripts.
void setup()
Function to set up the hierachy of levels. Creates a vector of pointers to each MG level...
int * column_index()
Access to C-style column index array.
Definition: matrices.h:1056
unsigned long nnz() const
Return the number of nonzero entries (the local nnz)
Definition: matrices.h:1068
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 level_up_local_coord_of_node(const int &son_type, Vector< double > &s)
Given the son_type of an element and a local node number j in that element with nnode_1d nodes per co...
void setup_mg_hierarchy()
Function to set up the hierachy of levels. Creates a vector of pointers to each MG level...
bool distributed() const
Definition: problem.h:798
double & tolerance()
Access to convergence tolerance.
Nullstream oomph_nullstream
Single (global) instantiation of the Nullstream.
void enable_doc_time()
Enable time documentation.
The GMRES method rewritten for complex matrices.
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
void pre_smooth(const unsigned &level)
Pre-smoother: Perform &#39;max_iter&#39; smoothing steps on the linear system Ax=b with current RHS vector...
void setup_smoothers()
Function to set up all of the smoothers once the system matrices have been set up.
unsigned Nvcycle
Maximum number of V-cycles.
void interpolation_matrix_set(const unsigned &level, Vector< double > &value, Vector< int > &col_index, Vector< int > &row_st, unsigned &ncol, unsigned &nrow)
Builds a CRDoubleMatrix that is used to interpolate the residual between levels. The transpose can be...
cstr elem_len * i
Definition: cfortran.h:607
HelmholtzMGPreconditioner(HelmholtzMGProblem *mg_problem_pt)
Constructor: Set up default values for number of V-cycles and pre- and post-smoothing steps...
Vector< Vector< DoubleVector > > X_mg_vectors_storage
Vector of vectors to store the solution vectors (X_mg) in two parts; the real and imaginary...
void setup_transfer_matrices()
Setup the transfer matrices on each level.
virtual TreeBasedRefineableMeshBase * mg_bulk_mesh_pt()=0
Function to get a pointer to the mesh we will be working with. If there are flux elements present in ...
PreSmootherFactoryFctPt Pre_smoother_factory_function_pt
Function to create pre-smoothers.
virtual void actions_after_adapt()
Actions that are to be performed after a mesh adaptation.
Definition: problem.h:1007
bool Has_been_solved
Boolean variable to indicate whether or not the problem was successfully solved.
The Problem class.
Definition: problem.h:152
OomphCommunicator * communicator_pt() const
const access to the communicator pointer
double Wavenumber
The value of the wavenumber, k.
unsigned Npost_smooth
Number of post-smoothing steps.
bool Doc_time
Indicates whether or not time documentation should be used.
A general Finite Element class.
Definition: elements.h:1274
double & tolerance()
Access function for the variable Tolerance (lvalue)
Vector< Vector< CRDoubleMatrix * > > Mg_matrices_storage_pt
Vector of vectors to store the system matrices. The i-th entry in this vector contains a vector of le...
double const & master_weight(const unsigned &i) const
Return weight for dofs on i-th master node.
Definition: nodes.h:753
void multiply(const CRDoubleMatrix *matrix, const DoubleVector &x, DoubleVector &soln)
Function to perform a matrix-vector multiplication on a oomph-lib matrix and vector using Trilinos fu...
void locate_zeta(const Vector< double > &zeta, GeomObject *&sub_geom_object_pt, Vector< double > &s, const bool &use_coordinate_as_initial_guess=false)
Find the sub geometric object and local coordinate therein that corresponds to the intrinsic coordina...
Nodes are derived from Data, but, in addition, have a definite (Eulerian) position in a space of a gi...
Definition: nodes.h:852
unsigned nmaster() const
Return the number of master nodes.
Definition: nodes.h:733
bool Suppress_all_output
If this is set to true then all output from the solver is suppressed.
static OomphCommunicator * communicator_pt()
access to the global oomph-lib communicator
Vector< CRDoubleMatrix * > Restriction_matrices_storage_pt
Vector to store the restriction matrices.
Mesh *& mesh_pt()
Return a pointer to the global mesh.
Definition: problem.h:1264
OomphInfo oomph_info
bool is_hanging() const
Test whether the node is geometrically hanging.
Definition: nodes.h:1207
double *& alpha_pt()
Pointer to Alpha, wavenumber complex shift.
long & eqn_number(const unsigned &i)
Return the equation number of the i-th stored variable.
Definition: nodes.h:365
void interpolate_and_correct(const unsigned &level)
Interpolate solution at current level onto next finer mesh and correct the solution x at that level...
e
Definition: cfortran.h:575
OomphCommunicator * communicator_pt()
access function to the oomph-lib communicator
Definition: problem.h:1221
double * value()
Access to C-style value array.
Definition: matrices.h:1062
void setup_mg_structures()
Function to set up the hierachy of levels. Creates a vector of pointers to each MG level...
void block_preconditioner_self_test()
Function to ensure the block form of the Jacobian matches the form described, i.e. we should have: |--—|---—| | A_r | -A_c | A = |--—|---—|. | A_c | A_r | |--—|---—|.
Vector< HelmholtzSmoother * > Post_smoothers_storage_pt
Vector to store the post-smoothers.
double residual_norm(const unsigned &level)
Return norm of residual r=b-Ax and the residual vector itself on the level-th level.
unsigned long nelement() const
Return number of elements in the mesh.
Definition: mesh.h:587
void mg_solve(Vector< DoubleVector > &result)
Do the actual solve – this is called through the pure virtual solve function in the LinearSolver bas...
void set_restriction_matrices_as_interpolation_transposes()
Builds a CRDoubleMatrix on each level that is used to restrict the residual between levels...
void disable_smoother_and_superlu_doc_time()
Suppress the output of both smoothers and SuperLU.
unsigned iterations() const
Number of iterations.
Vector< HelmholtzMGProblem * > Mg_hierarchy_pt
Vector containing pointers to problems in hierarchy.
DoubleVector Coarsest_rhs_mg
Assuming we&#39;re solving the system Ax=b, this vector will contain the expanded solution vector on the ...
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...
unsigned long ncol() const
Return the number of columns of the matrix.
Definition: matrices.h:998
void setup_coarsest_level_structures()
Function to create the fully expanded system matrix on the coarsest level.
void disable_v_cycle_output()
Disable all output from mg_solve apart from the number of V-cycles used to solve the problem...
virtual HelmholtzMGProblem * make_new_problem()=0
This function needs to be implemented in the derived problem: Returns a pointer to a new object of th...
void enable_v_cycle_output()
Enable the output of the V-cycle timings and other output.
void disable_doc_time()
Disable time documentation.
void maximum_edge_width(const unsigned &level, double &h)
Estimate the value of the parameter h on the level-th problem in the hierarchy.
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
Vector< Vector< DoubleVector > > Rhs_mg_vectors_storage
Vector of vectors to store the RHS vectors. This uses the same format as the X_mg_vectors_storage vec...
Base class for tree-based refineable meshes.
void setup_interpolation_matrices()
Setup the interpolation matrix on each level.
HelmholtzMGProblem class; subclass of Problem.
static char t char * s
Definition: cfortran.h:572
void post_smooth(const unsigned &level)
Post-smoother: Perform max_iter smoothing steps on the linear system Ax=b with current RHS vector...
void interpolation_matrix_set(const unsigned &level, double *value, int *col_index, int *row_st, unsigned &ncol, unsigned &nnz)
Builds a CRDoubleMatrix that is used to interpolate the residual between levels. The transpose can be...
unsigned & npre_smooth()
Return the number of pre-smoothing iterations (lvalue)
void position(Vector< double > &pos) const
Compute Vector of nodal positions either directly or via hanging node representation.
Definition: nodes.cc:2413
DoubleVector Coarsest_x_mg
Assuming we&#39;re solving the system Ax=b, this vector will contain the expanded solution vector on the ...
Vector< HelmholtzSmoother * > Pre_smoothers_storage_pt
Vector to store the pre-smoothers.
PostSmootherFactoryFctPt Post_smoother_factory_function_pt
Function to create post-smoothers.
Node *const & master_node_pt(const unsigned &i) const
Return a pointer to the i-th master node.
Definition: nodes.h:736
Node *& node_pt(const unsigned &n)
Return a pointer to the local node n.
Definition: elements.h:2109
void setup_interpolation_matrices_unstructured()
Setup the interpolation matrix on each level (used for unstructured meshes)
double timer()
returns the time in seconds after some point in past
void initialise(const _Tp &__value)
Iterate over all values and set to the desired value.
Definition: Vector.h:171
double k_squared() const
Get the square of wavenumber.
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
HelmholtzMGProblem()
Constructor. Initialise pointers to coarser and finer levels.
Vector< Vector< DoubleVector > > Residual_mg_vectors_storage
Vector to vectors to store the residual vectors. This uses the same format as the X_mg_vectors_storag...
void disable_output()
Suppress anything that can be suppressed, i.e. any timings. Things like mesh adaptation can not howev...
unsigned long nnode() const
Return number of nodes in the mesh.
Definition: mesh.h:590
void restrict_residual(const unsigned &level)
Restrict residual (computed on level-th MG level) to the next coarser mesh and stick it into the coar...
void full_setup()
Do a full setup (assumes everything will be setup around the HelmholtzMGProblem pointer given in the ...
HelmholtzMGProblem * Mg_problem_pt
Pointer to the MG problem (deep copy)
FiniteElement * finite_element_pt(const unsigned &e) const
Upcast (downcast?) to FiniteElement (needed to access FiniteElement member functions).
Definition: mesh.h:477
void calculate_omega(const double &k, const double &h)
Function to calculate the value of Omega by passing in the value of k and h [see Elman et al...
static const int OMEGA
Default value for an unassigned neighbour.
Definition: tree.h:241
void enable_output()
Enable the output from anything that could have been suppressed.
double Tolerance
The tolerance of the multigrid preconditioner.
void set_pre_smoother_factory_function(PreSmootherFactoryFctPt pre_smoother_fn)
Access function to set the pre-smoother creation function.
double & alpha_shift()
Function to change the value of the shift.
std::string string(const unsigned &i)
Return the i-th string or "" if the relevant string hasn&#39;t been defined.
unsigned V_cycle_counter
Pointer to counter for V-cycles.
bool Has_been_setup
Boolean variable to indicate whether or not the solver has been setup.
virtual void setup()=0
Setup the preconditioner. Pure virtual generic interface function.
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
Class for dense matrices, storing all the values of the matrix as a pointer to a pointer with assorte...
Definition: communicator.h:50
void concatenate(const Vector< DoubleVector *> &in_vector_pt, DoubleVector &out_vector)
Concatenate DoubleVectors. Takes a Vector of DoubleVectors. If the out vector is built, we will not build a new distribution. Otherwise we build a uniform distribution.
bool Suppress_v_cycle_output
Indicates whether or not the V-cycle output should be suppressed.
unsigned Nlevel
The number of levels in the multigrid heirachy.
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
unsigned long nrow() const
Return the number of rows of the matrix.
Definition: matrices.h:992
std::ostream * Stream_pt
Pointer to the output stream – defaults to std::cout.
Vector< CRDoubleMatrix * > Interpolation_matrices_storage_pt
Vector to store the interpolation matrices.
unsigned & npost_smooth()
Return the number of post-smoothing iterations (lvalue)
CRDoubleMatrix * Coarsest_matrix_mg_pt
Stores the system matrix on the coarest level in the fully expanded format: |--—|---—| | A_r | -A_c...
int * row_start()
Access to C-style row_start array.
Definition: matrices.h:1050
void preconditioner_solve(const DoubleVector &r, DoubleVector &z)
Function applies MG to the vector r for a full solve.
Vector< double > Max_edge_width
Vector to storage the maximum edge width of each mesh. We only need the maximum edge width on levels ...
~HelmholtzMGPreconditioner()
Delete any dynamically allocated data.
unsigned nnode() const
Return the number of nodes.
Definition: elements.h:2146
A class for compressed row matrices. This is a distributable object.
Definition: matrices.h:872
void split(const DoubleVector &in_vector, Vector< DoubleVector *> &out_vector_pt)
Split a DoubleVector into the out DoubleVectors. Let vec_A be the in Vector, and let vec_B and vec_C ...
void set_post_smoother_factory_function(PostSmootherFactoryFctPt post_smoother_fn)
Access function to set the post-smoother creation function.
virtual void shape(const Vector< double > &s, Shape &psi) const =0
Calculate the geometric shape functions at local coordinate s. This function must be overloaded for e...
A general mesh class.
Definition: mesh.h:74
virtual ~HelmholtzMGProblem()
Destructor (empty)
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
void clean_up_memory()
Clean up anything that needs to be cleaned up.
unsigned Npre_smooth
Number of pre-smoothing steps.
void direct_solve()
Call the direct solver (SuperLU) to solve the problem exactly.