geometric_multigrid.h
Go to the documentation of this file.
1 // Include guards
2 #ifndef OOMPH_GEOMETRIC_MULTIGRID_HEADER
3 #define OOMPH_GEOMETRIC_MULTIGRID_HEADER
4 
5 // Config header generated by autoconfig
6 #ifdef HAVE_CONFIG_H
7 #include <config.h>
8 #endif
9 
10 // For the Problem class
11 #include "problem.h"
12 
13 // For the TreeBasedRefineableMeshBase and MeshAsGeomObject class
14 #include "refineable_mesh.h"
16 
17 // For the RefineableQElement class
18 #include "Qelements.h"
19 
20 // Other obvious stuff
21 #include "matrices.h"
23 #include "preconditioner.h"
24 
25 // Namespace extension
26 namespace oomph
27 {
28 
29 //======================================================================
30 /// MGProblem class; subclass of Problem
31 //======================================================================
32  class MGProblem : public virtual Problem
33  {
34 
35  public:
36 
37  /// Constructor. Initialise pointers to coarser and finer levels
39  {}
40 
41  /// Destructor (empty)
42  virtual ~MGProblem()
43  {}
44 
45  /// \short This function needs to be implemented in the derived problem:
46  /// Returns a pointer to a new object of the same type as the derived
47  /// problem
48  virtual MGProblem* make_new_problem()=0;
49 
50  /// \short Function to get a pointer to the mesh we will be working
51  /// with. If there are flux elements present in the mesh this will
52  /// be overloaded to return a pointer to the bulk mesh otherwise
53  /// it can be overloaded to point to the global mesh but it must
54  /// be of type RefineableMeshBase
56 
57  }; // End of MGProblem class
58 
59 
60 //////////////////////////////////////////////////////////
61 //////////////////////////////////////////////////////////
62 //////////////////////////////////////////////////////////
63 
64 
65 //======================================================================
66 // MG solver class
67 //======================================================================
68  template<unsigned DIM>
70  {
71  public:
72 
73  /// \short typedef for a function that returns a pointer to an object
74  /// of the class Smoother to be used as the pre-smoother
75  typedef Smoother* (*PreSmootherFactoryFctPt)();
76 
77  /// \short typedef for a function that returns a pointer to an object
78  /// of the class Smoother to be used as the post-smoother
79  typedef Smoother* (*PostSmootherFactoryFctPt)();
80 
81  /// Access function to set the pre-smoother creation function.
82  void set_pre_smoother_factory_function(PreSmootherFactoryFctPt pre_smoother_fn)
83  {
84  // Assign the function pointer
85  Pre_smoother_factory_function_pt=pre_smoother_fn;
86  }
87 
88  /// Access function to set the post-smoother creation function.
90  PostSmootherFactoryFctPt post_smoother_fn)
91  {
92  // Assign the function pointer
93  Post_smoother_factory_function_pt=post_smoother_fn;
94  }
95 
96  /// \short Constructor: Set up default values for number of V-cycles
97  /// and pre- and post-smoothing steps.
98  MGSolver(MGProblem* mg_problem_pt) :
99  Nvcycle(200),
100  Mg_problem_pt(mg_problem_pt),
101  Suppress_v_cycle_output(false),
102  Suppress_all_output(false),
103  Stream_pt(0),
104  Pre_smoother_factory_function_pt(0),
105  Post_smoother_factory_function_pt(0),
106  Npre_smooth(2),
107  Npost_smooth(2),
108  Doc_everything(false),
109  Has_been_setup(false),
110  Has_been_solved(false)
111  {
112  // Set the tolerance in the base class
113  this->Tolerance=1.0e-09;
114 
115  // Set the pointer to the finest level as the first
116  // entry in Mg_hierarchy
117  Mg_hierarchy.push_back(Mg_problem_pt);
118  } // End of MGSolver
119 
120  /// Delete any dynamically allocated data
122  {
123  // Run the function written to clean up the memory
124  clean_up_memory();
125  } // End of ~MGSolver
126 
127  /// Clean up anything that needs to be cleaned up
129  {
130  // We only need to destroy data if the solver has been set up and
131  // the data hasn't already been cleared
132  if (Has_been_setup)
133  {
134  // Loop over all of the levels in the hierarchy
135  for (unsigned i=0;i<Nlevel-1;i++)
136  {
137  // Delete the pre-smoother associated with this level
138  delete Pre_smoothers_storage_pt[i];
139 
140  // Make it a null pointer
141  Pre_smoothers_storage_pt[i]=0;
142 
143  // Delete the post-smoother associated with this level
144  delete Post_smoothers_storage_pt[i];
145 
146  // Make it a null pointer
147  Post_smoothers_storage_pt[i]=0;
148 
149  // Delete the system matrix associated with the i-th level
150  delete Mg_matrices_storage_pt[i];
151 
152  // Make it a null pointer
153  Mg_matrices_storage_pt[i]=0;
154  }
155 
156  // Loop over all but the coarsest of the levels in the hierarchy
157  for (unsigned i=0;i<Nlevel-1;i++)
158  {
159  // Delete the interpolation matrix associated with the i-th level
160  delete Interpolation_matrices_storage_pt[i];
161 
162  // Make it a null pointer
163  Interpolation_matrices_storage_pt[i]=0;
164 
165  // Delete the restriction matrix associated with the i-th level
166  delete Restriction_matrices_storage_pt[i];
167 
168  // Make it a null pointer
169  Restriction_matrices_storage_pt[i]=0;
170  }
171 
172  // If this solver has been set up then a hierarchy of problems
173  // will have been set up. If the user chose to document everything
174  // then the coarse-grid multigrid problems will have been kept alive
175  // which means we now have to loop over the coarse-grid levels and
176  // destroy them
177  if (Doc_everything)
178  {
179  // Loop over the levels
180  for (unsigned i=1;i<Nlevel;i++)
181  {
182  // Delete the i-th level problem
183  delete Mg_hierarchy[i];
184 
185  // Make the associated pointer a null pointer
186  Mg_hierarchy[i]=0;
187  }
188  } // if (Doc_everything)
189 
190  // Everything has been deleted now so we need to indicate that the
191  // solver is not set up
192  Has_been_setup=false;
193  } // if (Has_been_setup)
194  } // End of clean_up_memory
195 
196  /// \short Makes a vector which will be used in the self-test. Is currently
197  /// set to make the entries of the vector represent a plane wave propagating
198  /// at an angle of 45 degrees
199  void set_self_test_vector();
200 
201  /// \short Makes a vector, restricts it down the levels of the hierarchy
202  /// and documents it at each level. After this is done the vector is
203  /// interpolated up the levels of the hierarchy with the output
204  /// being documented at each level
205  void self_test();
206 
207  /// \short Make a self-test to make sure that the interpolation matrices
208  /// are doing the same thing to restrict the vectors down through the
209  /// heirachy.
210  void restriction_self_test();
211 
212  /// \short Make a self-test to make sure that the interpolation matrices
213  /// are doing the same thing to interpolate the vectors up.
214  void interpolation_self_test();
215 
216  /// \short Given a level in the hierarchy, an input vector and a filename
217  /// this function will document the given vector according to the structure
218  /// of the mesh on the given level
219  void plot(const unsigned& hierarchy_level,
220  const DoubleVector& input_vector,
221  const std::string& filename);
222 
223  /// \short Disable all output from mg_solve apart from the number of
224  /// V-cycles used to solve the problem
226  {
227  // Set the value of Doc_time (inherited from LinearSolver) to false
228  Doc_time=false;
229 
230  // Enable the suppression of output from the V-cycle
231  Suppress_v_cycle_output=true;
232  } // End of disable_v_cycle_output
233 
234  /// \short Suppress anything that can be suppressed, i.e. any timings.
235  /// Things like mesh adaptation can not however be silenced using this
237  {
238  // Set the value of Doc_time (inherited from LinearSolver) to false
239  Doc_time=false;
240 
241  // Enable the suppression of output from the V-cycle
242  Suppress_v_cycle_output=true;
243 
244  // Enable the suppression of everything
245  Suppress_all_output=true;
246 
247  // Store the output stream pointer
248  Stream_pt=oomph_info.stream_pt();
249 
250  // Now set the oomph_info stream pointer to the null stream to
251  // disable all possible output
253  } // End of disable_output
254 
255  /// \short Enable the output of the V-cycle timings and other output
257  {
258  // Enable time documentation
259  Doc_time=true;
260 
261  // Enable output from the MG solver
262  Suppress_v_cycle_output=false;
263  } // End of enable_v_cycle_output
264 
265  /// \short Enable the output from anything that could have been suppressed
267  {
268  // Enable the documentation of everything (if this is set to TRUE then
269  // the function self_test() will be run which outputs a solution
270  // represented on each level of the hierarchy
271  Doc_everything=true;
272  } // End of enable_doc_everything
273 
274  /// \short Enable the output from anything that could have been suppressed
276  {
277  // Enable time documentation
278  Doc_time=true;
279 
280  // Enable output from everything during the full setup of the solver
281  Suppress_all_output=false;
282 
283  // Enable output from the MG solver
284  Suppress_v_cycle_output=false;
285  } // End of enable_output
286 
287  /// \short Suppress the output of both smoothers and SuperLU
289  {
290  // Loop over all levels of the hierarchy
291  for (unsigned i=0;i<Nlevel-1;i++)
292  {
293  // Disable time documentation on each level (for each pre-smoother)
294  Pre_smoothers_storage_pt[i]->disable_doc_time();
295 
296  // Disable time documentation on each level (for each post-smoother)
297  Post_smoothers_storage_pt[i]->disable_doc_time();
298  }
299 
300  // We only do a direct solve on the coarsest level so this is the only
301  // place we need to silence SuperLU
302  Mg_matrices_storage_pt[Nlevel-1]->linear_solver_pt()->disable_doc_time();
303  } // End of disable_smoother_and_superlu_doc_time
304 
305  /// Return the number of post-smoothing iterations (lvalue)
306  unsigned& npost_smooth()
307  {
308  // Return the number of post-smoothing iterations to be done on each
309  // level of the hierarchy
310  return Npost_smooth;
311  } // End of npost_smooth
312 
313  /// Return the number of pre-smoothing iterations (lvalue)
314  unsigned& npre_smooth()
315  {
316  // Return the number of pre-smoothing iterations to be done on each
317  // level of the hierarchy
318  return Npre_smooth;
319  } // End of npre_smooth
320 
321  /// \short Pre-smoother: Perform 'max_iter' smoothing steps on the
322  /// linear system Ax=b with current RHS vector, b, starting with
323  /// current solution vector, x. Return the residual vector r=b-Ax.
324  /// Uses the default smoother (set in the MGProblem constructor)
325  /// which can be overloaded for a specific problem.
326  void pre_smooth(const unsigned& level)
327  {
328  // Run pre-smoother 'max_iter' times
329  Pre_smoothers_storage_pt[level]->
330  smoother_solve(Rhs_mg_vectors_storage[level],
331  X_mg_vectors_storage[level]);
332 
333  // Calculate the residual r=b-Ax and assign it
334  Mg_matrices_storage_pt[level]->
335  residual(X_mg_vectors_storage[level],
336  Rhs_mg_vectors_storage[level],
337  Residual_mg_vectors_storage[level]);
338  } // End of pre_smooth
339 
340  /// \short Post-smoother: Perform max_iter smoothing steps on the
341  /// linear system Ax=b with current RHS vector, b, starting with
342  /// current solution vector, x. Uses the default smoother (set in
343  /// the MGProblem constructor) which can be overloaded for specific
344  /// problem.
345  void post_smooth(const unsigned& level)
346  {
347  // Run post-smoother 'max_iter' times
348  Post_smoothers_storage_pt[level]->
349  smoother_solve(Rhs_mg_vectors_storage[level],
350  X_mg_vectors_storage[level]);
351  } // End of post_smooth
352 
353  /// \short Return norm of residual r=b-Ax and the residual vector itself
354  /// on the level-th level
355  double residual_norm(const unsigned& level)
356  {
357  // And zero the entries of residual
358  Residual_mg_vectors_storage[level].initialise(0.0);
359 
360  // Get the residual
361  Mg_matrices_storage_pt[level]->residual(X_mg_vectors_storage[level],
362  Rhs_mg_vectors_storage[level],
363  Residual_mg_vectors_storage[level]);
364 
365  // Return the norm of the residual
366  return Residual_mg_vectors_storage[level].norm();
367  } // End of residual_norm
368 
369  /// \short Call the direct solver (SuperLU) to solve the problem exactly.
370  // The result is placed in X_mg
372  {
373  // Get solution by direct solve:
374  Mg_matrices_storage_pt[Nlevel-1]->
375  solve(Rhs_mg_vectors_storage[Nlevel-1],
376  X_mg_vectors_storage[Nlevel-1]);
377  } // End of direct_solve
378 
379  /// \short Builds a CRDoubleMatrix that is used to interpolate the
380  /// residual between levels. The transpose can be used as the full
381  /// weighting restriction.
382  void interpolation_matrix_set(const unsigned& level,
383  double* value,
384  int* col_index,
385  int* row_st,
386  unsigned& ncol,
387  unsigned& nnz)
388  {
389  // Dynamically allocate the interpolation matrix
390  Interpolation_matrices_storage_pt[level]=new CRDoubleMatrix;
391 
392  // Build the matrix
393  Interpolation_matrices_storage_pt[level]->
394  build_without_copy(ncol,nnz,value,col_index,row_st);
395  } // End of interpolation_matrix_set
396 
397  /// \short Builds a CRDoubleMatrix that is used to interpolate the
398  /// residual between levels. The transpose can be used as the full
399  /// weighting restriction.
400  void interpolation_matrix_set(const unsigned& level,
401  Vector<double>& value,
402  Vector<int>& col_index,
403  Vector<int>& row_st,
404  unsigned& ncol,
405  unsigned& nrow)
406  {
407  // Dynamically allocate the interpolation matrix
408  Interpolation_matrices_storage_pt[level]=new CRDoubleMatrix;
409 
410  // Make the distribution pointer
411  LinearAlgebraDistribution* dist_pt=
412  new LinearAlgebraDistribution(Mg_hierarchy[level]->
413  communicator_pt(),nrow,false);
414 
415 #ifdef PARANOID
416 #ifdef OOMPH_HAS_MPI
417  // Set up the warning messages
418  std::string warning_message="Setup of interpolation matrix distribution ";
419  warning_message+="has not been tested with MPI.";
420 
421  // If we're not running the code in serial
422  if (dist_pt->communicator_pt()->nproc()>1)
423  {
424  // Throw a warning
425  OomphLibWarning(warning_message,
426  OOMPH_CURRENT_FUNCTION,
427  OOMPH_EXCEPTION_LOCATION);
428  }
429 #endif
430 #endif
431 
432  // Build the matrix itself
433  Interpolation_matrices_storage_pt[level]->
434  build(dist_pt,ncol,value,col_index,row_st);
435 
436  // Delete the newly created distribution pointer
437  delete dist_pt;
438 
439  // Make it a null pointer
440  dist_pt=0;
441  } // End of interpolation_matrix_set
442 
443  /// \short Builds a CRDoubleMatrix on each level that is used to
444  /// restrict the residual between levels. The transpose can be used
445  /// as the interpolation matrix
447  {
448  for (unsigned i=0;i<Nlevel-1;i++)
449  {
450  // Dynamically allocate the restriction matrix
451  Restriction_matrices_storage_pt[i]=new CRDoubleMatrix;
452 
453  // Set the restriction matrix to be the transpose of the
454  // interpolation matrix
455  Interpolation_matrices_storage_pt[i]->
456  get_matrix_transpose(Restriction_matrices_storage_pt[i]);
457  }
458  } // End of set_restriction_matrices_as_interpolation_transposes
459 
460  /// \short Restrict residual (computed on level-th MG level) to the next
461  /// coarser mesh and stick it into the coarse mesh RHS vector.
462  void restrict_residual(const unsigned& level);
463 
464  /// \short Interpolate solution at current level onto next finer mesh
465  /// and correct the solution x at that level
466  void interpolate_and_correct(const unsigned& level);
467 
468  /// \short Given the son_type of an element and a local node number
469  /// j in that element with nnode_1d nodes per coordinate direction,
470  /// return the local coordinate s in its father element. Needed in
471  /// the setup of the interpolation matrices
472  void level_up_local_coord_of_node(const int& son_type,
473  Vector<double>& s);
474 
475  /// \short Setup the interpolation matrix on each level
476  void setup_interpolation_matrices();
477 
478  /// \short Setup the interpolation matrix on each level (used for
479  /// unstructured meshes)
480  void setup_interpolation_matrices_unstructured();
481 
482  /// \short Setup the transfer matrices on each level
483  void setup_transfer_matrices();
484 
485  /// \short Do a full setup (assumes everything will be setup around the
486  /// MGProblem pointer given in the constructor)
487  void full_setup();
488 
489  /// \short Virtual function in the base class that needs to be implemented
490  /// later but for now just leave it empty
491  void solve(Problem* const& problem_pt, DoubleVector& result)
492  {
493  // Dynamically cast problem_pt of type Problem to a MGProblem pointer
494  MGProblem* mg_problem_pt=dynamic_cast<MGProblem*>(problem_pt);
495 
496 #ifdef PARANOID
497  // PARANOID check - If the dynamic_cast produces a null pointer the
498  // input was not a MGProblem
499  if (0==mg_problem_pt)
500  {
501  throw OomphLibError("Input problem must be of type MGProblem.",
502  OOMPH_CURRENT_FUNCTION,
503  OOMPH_EXCEPTION_LOCATION);
504  }
505  // PARANOID check - If a node in the input problem has more than
506  // one value we cannot deal with it so arbitarily check the first one
507  if (problem_pt->mesh_pt()->node_pt(0)->nvalue()!=1)
508  {
509  // How many dofs are there in the first node
510  unsigned n_value=problem_pt->mesh_pt()->node_pt(0)->nvalue();
511 
512  // Make the error message
513  std::ostringstream error_message_stream;
514  error_message_stream << "Cannot currently deal with more than 1 dof"
515  << " per node. This problem has " << n_value
516  << " dofs per node."
517  << std::endl;
518 
519  // Throw the error message
520  throw OomphLibError(error_message_stream.str(),
521  OOMPH_CURRENT_FUNCTION,
522  OOMPH_EXCEPTION_LOCATION);
523  }
524 #endif
525 
526  // Assign the new MGProblem pointer to Mg_problem_pt
527  Mg_problem_pt=mg_problem_pt;
528 
529  // Set up all of the required MG structures
530  full_setup();
531 
532  // Run the MG method and assign the solution to result
533  mg_solve(result);
534 
535  // Only output if the V-cycle output isn't suppressed
536  if (!Suppress_v_cycle_output)
537  {
538  // Notify user that the hierarchy of levels is complete
539  oomph_info << "\n================="
540  << "Multigrid Solve Complete"
541  << "=================\n"
542  << std::endl;
543  }
544 
545  // If the user did not request all output be suppressed
546  if (!Suppress_all_output)
547  {
548  // If the user requested all V-cycle output be suppressed
549  if (Suppress_v_cycle_output)
550  {
551  // Create an extra line spacing
552  oomph_info << std::endl;
553  }
554 
555  // Output number of V-cycles taken to solve
556  if (Has_been_solved)
557  {
558  oomph_info << "Total number of V-cycles required for solve: "
559  << V_cycle_counter << std::endl;
560  }
561  else
562  {
563  oomph_info << "Total number of V-cycles used: "
564  << V_cycle_counter << std::endl;
565  }
566  } // if (!Suppress_all_output)
567 
568  // Only enable and assign the stream pointer again if we originally
569  // suppressed everything otherwise it won't be set yet
570  if (Suppress_all_output)
571  {
572  // Now enable the stream pointer again
573  oomph_info.stream_pt()=Stream_pt;
574  }
575  } // End of solve
576 
577  /// Number of iterations
578  unsigned iterations() const
579  {
580  // Return the number of V-cycles which have been done
581  return V_cycle_counter;
582  } // End of iterations
583 
584  protected:
585 
586  /// \short Do the actual solve -- this is called through the pure virtual
587  /// solve function in the LinearSolver base class. The function is stored
588  /// as protected to allow the MGPreconditioner derived class to use the
589  /// solver
590  void mg_solve(DoubleVector& result);
591 
592  /// \short Normalise the rows of the restriction matrices to avoid
593  /// amplifications when projecting to the coarser level
594  void modify_restriction_matrices();
595 
596  /// \short Maximum number of V-cycles (this is set as a protected variable so
597  // that it can be changed in the MGPreconditioner class)
598  unsigned Nvcycle;
599 
600  /// \short Pointer to the MG problem (deep copy). This is protected to provide
601  /// access to the MG preconditioner
603 
604  /// \short Vector to store the RHS vectors (Rhs_mg). This is protected to
605  /// allow the multigrid preconditioner to assign the RHS vector during
606  /// preconditioner_solve()
608 
609  /// \short Indicates whether or not the V-cycle output should be
610  /// suppressed. Needs to be protected member data for the multigrid
611  /// preconditioner to know whether or not to output information
612  /// with each preconditioning step
614 
615  /// \short If this is set to true then all output from the solver is
616  /// suppressed. This is protected member data so that the multigrid
617  /// preconditioner knows whether or not to restore the stream pointer
619 
620  /// \short Pointer to the output stream -- defaults to std::cout.
621  /// This is protected member data to allow the preconditioner to
622  /// restore normal output if everything was chosen to be
623  /// suppressed by the user
624  std::ostream* Stream_pt;
625 
626  private:
627 
628  /// \short Function to create pre-smoothers
629  PreSmootherFactoryFctPt Pre_smoother_factory_function_pt;
630 
631  /// \short Function to create post-smoothers
632  PostSmootherFactoryFctPt Post_smoother_factory_function_pt;
633 
634  /// \short Function to set up the hierachy of levels. Creates a vector
635  /// of pointers to each MG level
636  void setup_mg_hierarchy();
637 
638  /// \short Function to set up the hierachy of levels. Creates a vector
639  /// of pointers to each MG level
640  void setup_mg_structures();
641 
642  /// \short Function to set up all of the smoothers once the system matrices
643  /// have been set up
644  void setup_smoothers();
645 
646  /// The number of levels in the multigrid heirachy
647  unsigned Nlevel;
648 
649  /// Vector containing pointers to problems in hierarchy
651 
652  /// Vector to store the system matrices
654 
655  /// Vector to store the interpolation matrices
657 
658  /// Vector to store the restriction matrices
660 
661  /// Vector to store the solution vectors (X_mg)
663 
664  /// Vector to store the residual vectors
666 
667  /// \short Vector to store the result of interpolation on each level (only
668  /// required if the user wishes to document the output of interpolation
669  /// and restriction on each level)
671 
672  /// \short Vector to store the result of restriction on each level (only
673  /// required if the user wishes to document the output of interpolation
674  /// and restriction on each level)
676 
677  /// Vector to store the pre-smoothers
679 
680  /// Vector to store the post-smoothers
682 
683  /// Number of pre-smoothing steps
684  unsigned Npre_smooth;
685 
686  /// Number of post-smoothing steps
687  unsigned Npost_smooth;
688 
689  /// \short If this is set to true we document everything. In addition
690  /// to outputting the information of the setup timings and V-cycle
691  /// data we document the refinement and unrefinement patterns given
692  /// by the transfer operators which is done by keeping the coarser
693  /// MG problem pointers alive
695 
696  /// Boolean variable to indicate whether or not the solver has been setup
698 
699  /// Boolean variable to indicate whether or not the problem was
700  /// successfully solved
702 
703  /// Pointer to counter for V-cycles
704  unsigned V_cycle_counter;
705  };
706 
707 //====================================================================
708 /// An interface to allow scalar MG to be used as a Preconditioner
709 //====================================================================
710  template<unsigned DIM>
711  class MGPreconditioner : public MGSolver<DIM>, public Preconditioner
712  {
713  public:
714 
715  /// Constructor.
716  MGPreconditioner(MGProblem* mg_problem_pt) : MGSolver<DIM>(mg_problem_pt)
717  {
718  // Set the number of V-cycles to be 1 (as expected as a preconditioner)
719  this->Nvcycle=2;
720  } // End of MGPreconditioner (constructor)
721 
722  /// Destructor (empty)
724 
725  /// Broken copy constructor.
727  {
728  BrokenCopy::broken_copy("MGPreconditioner");
729  }
730 
731  /// Broken assignment operator.
733  {
734  BrokenCopy::broken_assign("MGPreconditioner");
735  }
736 
737  /// \short Function to set up a preconditioner for the linear system
738  void setup()
739  {
740 #ifdef OOMPH_HAS_MPI
741  // Make sure that this is running in serial. Can't guarantee it'll
742  // work when the problem is distributed over several processors
743  if (MPI_Helpers::communicator_pt()->nproc()>1)
744  {
745  // Throw a warning
746  OomphLibWarning("Can't guarantee the MG solver will work in parallel!",
747  OOMPH_CURRENT_FUNCTION,
748  OOMPH_EXCEPTION_LOCATION);
749  }
750 #endif
751 
752  // Call the helper function that actually does all the work
753  this->full_setup();
754 
755  // Only enable and assign the stream pointer again if we originally
756  // suppressed everything otherwise it won't be set yet
757  if (this->Suppress_all_output)
758  {
759  // Now enable the stream pointer again
760  oomph_info.stream_pt()=this->Stream_pt;
761  }
762  } // End of setup
763 
764  /// \short Function applies MG to the vector r for a full solve
765  virtual void preconditioner_solve(const DoubleVector& rhs,DoubleVector &z)
766  {
767 #ifdef PARANOID
768  if (this->Mg_problem_pt->ndof()!=rhs.nrow())
769  {
770  throw OomphLibError("Matrix and RHS vector sizes incompatible.",
771  OOMPH_CURRENT_FUNCTION,
772  OOMPH_EXCEPTION_LOCATION);
773  }
774 #endif
775 
776  // Set the right-hand side vector on the finest level to r
777  this->Rhs_mg_vectors_storage[0]=rhs;
778 
779  // Run the MG method and assign the solution to z
780  this->mg_solve(z);
781 
782  // Only output if the V-cycle output isn't suppressed
783  if (!(this->Suppress_v_cycle_output))
784  {
785  // Notify user that the hierarchy of levels is complete
786  oomph_info << "\n==========Multigrid Preconditioner Solve Complete========="
787  << "\n" << std::endl;
788  }
789 
790  // Only enable and assign the stream pointer again if we originally
791  // suppressed everything otherwise it won't be set yet
792  if (this->Suppress_all_output)
793  {
794  // Now enable the stream pointer again
795  oomph_info.stream_pt()=this->Stream_pt;
796  }
797  } // End of preconditioner_solve
798 
799  /// Clean up memory
801  };
802 
803 
804 //===================================================================
805 /// Runs a full setup of the MG solver
806 //===================================================================
807  template<unsigned DIM>
809  {
810  // Initialise the timer start variable
811  double t_fs_start=0.0;
812 
813  // If we're allowed to output
814  if (!Suppress_all_output)
815  {
816  // Start the timer
817  t_fs_start=TimingHelpers::timer();
818 
819  // Notify user that the hierarchy of levels is complete
820  oomph_info << "\n===============Starting Multigrid Full Setup=============="
821  << std::endl;
822 
823  // Notify user that the hierarchy of levels is complete
824  oomph_info << "\nStarting the full setup of the multigrid solver."
825  << std::endl;
826  }
827 
828 #ifdef PARANOID
829  // PARANOID check - Make sure the dimension of the solver matches the
830  // dimension of the elements used in the problems mesh
831  if (dynamic_cast<FiniteElement*>(Mg_problem_pt->
832  mesh_pt()->element_pt(0))->dim()!=DIM)
833  {
834  std::string err_strng="The dimension of the elements used in the mesh ";
835  err_strng+="does not match the dimension of the solver.";
836  throw OomphLibError(err_strng,
837  OOMPH_CURRENT_FUNCTION,
838  OOMPH_EXCEPTION_LOCATION);
839  }
840 
841  // PARANOID check - The elements of the bulk mesh must all be refineable
842  // elements otherwise we cannot deal with this
843  if (Mg_problem_pt->mg_bulk_mesh_pt()!=0)
844  {
845  // Find the number of elements in the bulk mesh
846  unsigned n_elements=Mg_problem_pt->mg_bulk_mesh_pt()->nelement();
847 
848  // Loop over the elements in the mesh and ensure that they are
849  // all refineable elements
850  for (unsigned el_counter=0;el_counter<n_elements;el_counter++)
851  {
852  // Upcast global mesh element to a refineable element
853  RefineableElement* el_pt=dynamic_cast<RefineableElement*>
854  (Mg_problem_pt->mg_bulk_mesh_pt()->element_pt(el_counter));
855 
856  // Check if the upcast worked or not; if el_pt is a null pointer the
857  // element is not refineable
858  if (el_pt==0)
859  {
860  throw OomphLibError
861  ("Element in global mesh could not be upcast to a refineable "
862  "element. We cannot deal with elements that are not refineable.",
863  OOMPH_CURRENT_FUNCTION,OOMPH_EXCEPTION_LOCATION);
864  }
865  }
866  }
867  else
868  {
869  throw OomphLibError
870  ("The provided bulk mesh pointer is set to be a null pointer. "
871  "The multigrid solver operates on the bulk mesh thus a pointer "
872  "to the correct mesh must be given.",
873  OOMPH_CURRENT_FUNCTION,
874  OOMPH_EXCEPTION_LOCATION);
875  }
876 #endif
877 
878  // If this is not the first Newton step then we will already have things
879  // in storage. If this is the case, delete them
880  clean_up_memory();
881 
882  // Resize the Mg_hierarchy vector
883  Mg_hierarchy.resize(1,0);
884 
885  // Set the pointer to the finest level as the first entry in Mg_hierarchy
886  Mg_hierarchy[0]=Mg_problem_pt;
887 
888  // Create the hierarchy of levels
889  setup_mg_hierarchy();
890 
891  // Set up the interpolation and restriction matrices
892  setup_transfer_matrices();
893 
894  // Set up the data structures on each level, i.e. the system matrix,
895  // LHS and RHS vectors
896  setup_mg_structures();
897 
898  // Set up the smoothers on all of the levels
899  setup_smoothers();
900 
901  // If we do not want to document everything we want to delete all the
902  // coarse-grid problems
903  if (!Doc_everything)
904  {
905  // Loop over all of the coarser levels
906  for (unsigned i=1;i<Nlevel;i++)
907  {
908  // Delete the i-th coarse-grid MGProblem
909  delete Mg_hierarchy[i];
910 
911  // Set it to be a null pointer
912  Mg_hierarchy[i]=0;
913  }
914  }
915  // Otherwise, document everything!
916  else
917  {
918  // If the user wishes to document everything we run the self-test
919  self_test();
920  } // if (!Doc_everything)
921 
922  // Indicate that the full setup has been completed
923  Has_been_setup=true;
924 
925  // If we're allowed to output to the screen
926  if (!Suppress_all_output)
927  {
928  // Output the time taken to complete the full setup
929  double t_fs_end=TimingHelpers::timer();
930  double full_setup_time=t_fs_end-t_fs_start;
931 
932  // Output the CPU time
933  oomph_info << "\nCPU time for full setup [sec]: "
934  << full_setup_time << std::endl;
935 
936  // Notify user that the hierarchy of levels is complete
937  oomph_info << "\n===============Multigrid Full Setup Complete=============="
938  << std::endl;
939  } // if (!Suppress_all_output)
940  } // End of full_setup
941 
942  //===================================================================
943  /// \short Set up the MG hierarchy
944  //===================================================================
945  // Function to set up the hierachy of levels. Creates a vector of
946  // pointers to each MG level
947  template<unsigned DIM>
949  {
950  // Initialise the timer start variable
951  double t_m_start=0.0;
952 
953  // Notify the user if it is allowed
954  if (!Suppress_all_output)
955  {
956  // Notify user of progress
957  oomph_info << "\n===============Creating Multigrid Hierarchy==============="
958  << std::endl;
959 
960  // Start clock
961  t_m_start=TimingHelpers::timer();
962  }
963 
964  // Create a bool to indicate whether or not we could create an unrefined
965  // copy. This bool will be assigned the value FALSE when the current copy
966  // is the last level of the multigrid hierarchy
967  bool managed_to_create_unrefined_copy=true;
968 
969  // Now keep making copies and try to make an unrefined copy of
970  // the mesh
971  unsigned level=0;
972 
973  // Set up all of the levels by making a completely unrefined copy
974  // of the problem using the function make_new_problem
975  while (managed_to_create_unrefined_copy)
976  {
977  // Make a new object of the same type as the derived problem
978  MGProblem* new_problem_pt=Mg_problem_pt->make_new_problem();
979 
980  // Do anything that needs to be done before we can refine the mesh
981  new_problem_pt->actions_before_adapt();
982 
983  // To create the next level in the hierarchy we need to create a mesh
984  // which matches the refinement of the current problem and then unrefine
985  // the mesh. This can alternatively be done using the function
986  // refine_base_mesh_as_in_reference_mesh_minus_one which takes a
987  // reference mesh to do precisely the above with
988  managed_to_create_unrefined_copy=
989  new_problem_pt->mg_bulk_mesh_pt()->
990  refine_base_mesh_as_in_reference_mesh_minus_one(
991  Mg_hierarchy[level]->mg_bulk_mesh_pt());
992 
993  // If we were able to unrefine the problem on the current level
994  // then add the unrefined problem to a vector of the levels
995  if (managed_to_create_unrefined_copy)
996  {
997  // Another level has been created so increment the level counter
998  level++;
999 
1000  // If the documentation of everything has not been suppressed
1001  // then tell the user we managed to create another level
1002  if (!Suppress_all_output)
1003  {
1004  // Notify user that unrefinement was successful
1005  oomph_info << "\nSuccess! Level " << level
1006  << " has been created." << std::endl;
1007  }
1008 
1009  // Do anything that needs to be done after refinement
1010  new_problem_pt->actions_after_adapt();
1011 
1012  // Do the equation numbering for the new problem
1013  oomph_info << "\n - Number of equations: "
1014  << new_problem_pt->assign_eqn_numbers() << std::endl;
1015 
1016  // Add the new problem pointer onto the vector of MG levels
1017  // and increment the value of level by 1
1018  Mg_hierarchy.push_back(new_problem_pt);
1019  }
1020  // If we weren't able to create an unrefined copy
1021  else
1022  {
1023  // Delete the new problem
1024  delete new_problem_pt;
1025 
1026  // Make it a null pointer
1027  new_problem_pt=0;
1028 
1029  // Assign the number of levels to Nlevel
1030  Nlevel=Mg_hierarchy.size();
1031 
1032  // If we're allowed to document then tell the user we've reached
1033  // the coarsest level of the hierarchy
1034  if (!Suppress_all_output)
1035  {
1036  // Notify the user
1037  oomph_info << "\n Reached the coarsest level! "
1038  << "Number of levels: " << Nlevel << std::endl;
1039  }
1040  } // if (managed_to_unrefine)
1041  } // while (managed_to_unrefine)
1042 
1043  // Given that we know the number of levels in the hierarchy we can resize the
1044  // vectors which will store all the information required for our solver:
1045  // Resize the vector storing all of the system matrices
1046  Mg_matrices_storage_pt.resize(Nlevel,0);
1047 
1048  // Resize the vector storing all of the solution vectors (X_mg)
1049  X_mg_vectors_storage.resize(Nlevel);
1050 
1051  // Resize the vector storing all of the RHS vectors (Rhs_mg)
1052  Rhs_mg_vectors_storage.resize(Nlevel);
1053 
1054  // Resize the vector storing all of the residual vectors
1055  Residual_mg_vectors_storage.resize(Nlevel);
1056 
1057  // Allocate space for the pre-smoother storage vector (remember, we do
1058  // not need a smoother on the coarsest level we use a direct solve there)
1059  Pre_smoothers_storage_pt.resize(Nlevel-1,0);
1060 
1061  // Allocate space for the post-smoother storage vector (remember, we do
1062  // not need a smoother on the coarsest level we use a direct solve there)
1063  Post_smoothers_storage_pt.resize(Nlevel-1,0);
1064 
1065  // Resize the vector storing all of the interpolation matrices
1066  Interpolation_matrices_storage_pt.resize(Nlevel-1,0);
1067 
1068  // Resize the vector storing all of the restriction matrices
1069  Restriction_matrices_storage_pt.resize(Nlevel-1,0);
1070 
1071  if (!Suppress_all_output)
1072  {
1073  // Stop clock
1074  double t_m_end=TimingHelpers::timer();
1075  double total_setup_time=double(t_m_end-t_m_start);
1076  oomph_info << "\nCPU time for creation of hierarchy of MG problems [sec]: "
1077  << total_setup_time << std::endl;
1078 
1079  // Notify user that the hierarchy of levels is complete
1080  oomph_info << "\n===============Hierarchy Creation Complete================"
1081  << "\n" << std::endl;
1082  }
1083  } // End of setup_mg_hierarchy
1084 
1085  //===================================================================
1086  /// \short Set up the transfer matrices. Both the pure injection and
1087  /// full weighting method have been implemented here but it is highly
1088  /// recommended that full weighting is used in general. In both
1089  /// methods the transpose of the transfer matrix is used to transfer
1090  /// a vector back
1091  //===================================================================
1092  template<unsigned DIM>
1094  {
1095  // Initialise the timer start variable
1096  double t_r_start=0.0;
1097 
1098  // Notify the user (if we're allowed)
1099  if (!Suppress_all_output)
1100  {
1101  // Notify user of progress
1102  oomph_info << "Creating the transfer matrices ";
1103 
1104  // Start the clock!
1105  t_r_start=TimingHelpers::timer();
1106  }
1107 
1108  // If we're allowed to output information
1109  if (!Suppress_all_output)
1110  {
1111  // Say what method we're using
1112  oomph_info << "using full weighting (recommended).\n" << std::endl;
1113  }
1114 
1115  // Using full weighting so use setup_interpolation_matrices.
1116  // Note: There are two methods to choose from here, the ideal choice is
1117  // setup_interpolation_matrices() but that requires a refineable mesh base
1118  if (dynamic_cast<TreeBasedRefineableMeshBase*>
1119  (Mg_problem_pt->mg_bulk_mesh_pt()))
1120  {
1121  setup_interpolation_matrices();
1122  }
1123  // If the mesh is unstructured we have to use the locate_zeta function
1124  // to set up the interpolation matrices
1125  else
1126  {
1127  setup_interpolation_matrices_unstructured();
1128  }
1129 
1130  // Loop over all levels that will be assigned a restriction matrix
1131  set_restriction_matrices_as_interpolation_transposes();
1132 
1133  // If we're allowed
1134  if (!Suppress_all_output)
1135  {
1136  // Stop the clock
1137  double t_r_end=TimingHelpers::timer();
1138  double total_G_setup_time=double(t_r_end-t_r_start);
1139  oomph_info << "CPU time for transfer matrices setup [sec]: "
1140  << total_G_setup_time << std::endl;
1141 
1142  // Notify user that the hierarchy of levels is complete
1143  oomph_info << "\n============Transfer Matrices Setup Complete=============="
1144  << "\n" << std::endl;
1145  }
1146  } // End of setup_transfer_matrices function
1147 
1148  //===================================================================
1149  /// \short Set up the MG hierarchy structures
1150  //===================================================================
1151  // Function to set up the hierachy of levels. Creates a vector of
1152  // pointers to each MG level
1153  template<unsigned DIM>
1155  {
1156  // Initialise the timer start variable
1157  double t_m_start=0.0;
1158 
1159  // Start the clock (if we're allowed to time things)
1160  if (!Suppress_all_output)
1161  {
1162  // Start the clock
1163  t_m_start=TimingHelpers::timer();
1164  }
1165 
1166  // Allocate space for the system matrix on each level
1167  for (unsigned i=0;i<Nlevel;i++)
1168  {
1169  // Dynamically allocate a new CRDoubleMatrix
1170  Mg_matrices_storage_pt[i]=new CRDoubleMatrix;
1171  }
1172 
1173  // Loop over each level and extract the system matrix, solution vector
1174  // right-hand side vector and residual vector (to store the value of r=b-Ax)
1175  for (unsigned i=0;i<Nlevel;i++)
1176  {
1177  // If we're allowed to output
1178  if (!Suppress_all_output)
1179  {
1180  // Output the level we're working on
1181  oomph_info << "Setting up MG structures on level: " << i
1182  << "\n" << std::endl;
1183  }
1184 
1185  // Resize the solution and RHS vector
1186  unsigned n_dof=Mg_hierarchy[i]->ndof();
1187  LinearAlgebraDistribution* dist_pt=
1188  new LinearAlgebraDistribution(Mg_hierarchy[i]->communicator_pt(),
1189  n_dof,false);
1190 
1191 #ifdef PARANOID
1192 #ifdef OOMPH_HAS_MPI
1193  // Set up the warning messages
1194  std::string warning_message="Setup of distribution has not been ";
1195  warning_message+="tested with MPI.";
1196 
1197  // If we're not running the code in serial
1198  if (dist_pt->communicator_pt()->nproc()>1)
1199  {
1200  // Throw a warning
1201  OomphLibWarning(warning_message,
1202  OOMPH_CURRENT_FUNCTION,
1203  OOMPH_EXCEPTION_LOCATION);
1204  }
1205 #endif
1206 #endif
1207 
1208  // Build the approximate solution
1209  X_mg_vectors_storage[i].clear();
1210  X_mg_vectors_storage[i].build(dist_pt);
1211 
1212  // Build the point source function
1213  Rhs_mg_vectors_storage[i].clear();
1214  Rhs_mg_vectors_storage[i].build(dist_pt);
1215 
1216  // Build the residual vector (r=b-Ax)
1217  Residual_mg_vectors_storage[i].clear();
1218  Residual_mg_vectors_storage[i].build(dist_pt);
1219 
1220  // Delete the distribution pointer
1221  delete dist_pt;
1222 
1223  // Make it a null pointer
1224  dist_pt=0;
1225 
1226  // Build the matrix distribution
1227  Mg_matrices_storage_pt[i]->clear();
1228  Mg_matrices_storage_pt[i]->distribution_pt()->
1229  build(Mg_hierarchy[i]->communicator_pt(),n_dof,false);
1230 
1231  // Compute system matrix on the current level. On the finest level of the
1232  // hierarchy the system matrix and RHS vector is given by the Jacobian and
1233  // vector of residuals which define the original problem which the Galerkin
1234  // approximation to the system matrix is used on the subsequent levels so
1235  // that the correct contributions are taken from each dof on the level above
1236  // (that is to say, it should match the contribution taken from the solution
1237  // vector and RHS vector on the level above)
1238  if (i==0)
1239  {
1240  // Initialise the timer start variable
1241  double t_jac_start=0.0;
1242 
1243  // If we're allowed to output things
1244  if (!Suppress_all_output)
1245  {
1246  // Start timer for Jacobian setup
1247  t_jac_start=TimingHelpers::timer();
1248  }
1249 
1250  // The system matrix on the finest level is the Jacobian and the RHS
1251  // vector is given by the residual vector which accompanies the Jacobian
1252  Mg_hierarchy[0]->get_jacobian(Rhs_mg_vectors_storage[0],
1253  *Mg_matrices_storage_pt[0]);
1254 
1255  if (!Suppress_all_output)
1256  {
1257  // Document the time taken
1258  double t_jac_end=TimingHelpers::timer();
1259  double jacobian_setup_time=t_jac_end-t_jac_start;
1260  oomph_info << " - Time for setup of Jacobian [sec]: "
1261  << jacobian_setup_time << "\n" << std::endl;
1262  }
1263  }
1264  else
1265  {
1266  // Initialise the timer start variable
1267  double t_gal_start=0.0;
1268 
1269  // If we're allowed
1270  if (!Suppress_all_output)
1271  {
1272  // Start timer for Galerkin matrix calculation
1273  t_gal_start=TimingHelpers::timer();
1274  }
1275 
1276  // The system matrix on the coarser levels must be formed using the
1277  // Galerkin approximation which we do by calculating the product
1278  // A^2h = I^2h_h * A^h * I^h_2h, i.e. the coarser version of the
1279  // finer grid system matrix is formed by multiplying by the (fine grid)
1280  // restriction matrix from the left and the (fine grid) interpolation
1281  // matrix from the left
1282 
1283  // First we need to calculate A^h * I^h_2h which we store as A^2h
1284  Mg_matrices_storage_pt[i-1]->
1285  multiply(*Interpolation_matrices_storage_pt[i-1],
1286  *Mg_matrices_storage_pt[i]);
1287 
1288  // Now calculate I^2h_h * (A^h * I^h_2h) where the quantity in brackets
1289  // was just calculated. This updates A^2h to give us the true
1290  // Galerkin approximation to the finer grid matrix
1291  Restriction_matrices_storage_pt[i-1]->multiply(*Mg_matrices_storage_pt[i],
1292  *Mg_matrices_storage_pt[i]);
1293 
1294  // If the user did not choose to suppress everything
1295  if (!Suppress_all_output)
1296  {
1297  // End timer for Galerkin matrix calculation
1298  double t_gal_end=TimingHelpers::timer();
1299 
1300  // Calculate setup time
1301  double galerkin_matrix_calculation_time=t_gal_end-t_gal_start;
1302 
1303  // Document the time taken
1304  oomph_info << " - Time for system matrix formation using the Galerkin "
1305  << "approximation [sec]: " << galerkin_matrix_calculation_time
1306  << "\n" << std::endl;
1307  }
1308  } // if (i==0) else
1309  } // for (unsigned i=0;i<Nlevel;i++)
1310 
1311  // If we're allowed to output
1312  if (!Suppress_all_output)
1313  {
1314  // Stop clock
1315  double t_m_end=TimingHelpers::timer();
1316  double total_setup_time=double(t_m_end-t_m_start);
1317  oomph_info << "Total CPU time for setup of MG structures [sec]: "
1318  << total_setup_time << std::endl;
1319 
1320  // Notify user that the hierarchy of levels is complete
1321  oomph_info << "\n============"
1322  << "Multigrid Structures Setup Complete"
1323  << "===========\n" << std::endl;
1324  }
1325  } // End of setup_mg_structures
1326 
1327 
1328 //=========================================================================
1329 /// \short Set up the smoothers on all levels
1330 //=========================================================================
1331  template<unsigned DIM>
1333  {
1334  // Initialise the timer start variable
1335  double t_m_start=0.0;
1336 
1337  // Start the clock (if we're allowed to time things)
1338  if (!Suppress_all_output)
1339  {
1340  // Notify user
1341  oomph_info << "Starting the setup of all smoothers.\n" << std::endl;
1342 
1343  // Start the clock
1344  t_m_start=TimingHelpers::timer();
1345  }
1346 
1347  // Loop over the levels and assign the pre- and post-smoother points
1348  for (unsigned i=0;i<Nlevel-1;i++)
1349  {
1350  // If the pre-smoother factory function pointer hasn't been assigned
1351  // then we simply create a new instance of the DampedJacobi smoother
1352  // which is the default pre-smoother
1353  if (0==Pre_smoother_factory_function_pt)
1354  {
1355  Pre_smoothers_storage_pt[i]=new DampedJacobi<CRDoubleMatrix>;
1356  }
1357  // Otherwise we use the pre-smoother factory function pointer to
1358  // generate a new pre-smoother
1359  else
1360  {
1361  // Get a pointer to an object of the same type as the pre-smoother
1362  Pre_smoothers_storage_pt[i]=(*Pre_smoother_factory_function_pt)();
1363  }
1364 
1365  // If the post-smoother factory function pointer hasn't been assigned
1366  // then we simply create a new instance of the DampedJacobi smoother
1367  // which is the default post-smoother
1368  if (0==Post_smoother_factory_function_pt)
1369  {
1370  Post_smoothers_storage_pt[i]=new DampedJacobi<CRDoubleMatrix>;
1371  }
1372  // Otherwise we use the post-smoother factory function pointer to
1373  // generate a new post-smoother
1374  else
1375  {
1376  // Get a pointer to an object of the same type as the post-smoother
1377  Post_smoothers_storage_pt[i]=(*Post_smoother_factory_function_pt)();
1378  }
1379  }
1380 
1381  // Set the tolerance for the pre- and post-smoothers. The norm of the
1382  // solution which is compared against the tolerance is calculated
1383  // differently to the multigrid solver. To ensure that the smoother
1384  // continues to solve for the prescribed number of iterations we
1385  // lower the tolerance
1386  for (unsigned i=0;i<Nlevel-1;i++)
1387  {
1388  // Set the tolerance of the i-th level pre-smoother
1389  Pre_smoothers_storage_pt[i]->tolerance()=1.0e-16;
1390 
1391  // Set the tolerance of the i-th level post-smoother
1392  Post_smoothers_storage_pt[i]->tolerance()=1.0e-16;
1393  }
1394 
1395  // Set the number of pre- and post-smoothing iterations in each
1396  // pre- and post-smoother
1397  for (unsigned i=0;i<Nlevel-1;i++)
1398  {
1399  // Set the number of pre-smoothing iterations as the value of Npre_smooth
1400  Pre_smoothers_storage_pt[i]->max_iter()=Npre_smooth;
1401 
1402  // Set the number of pre-smoothing iterations as the value of Npost_smooth
1403  Post_smoothers_storage_pt[i]->max_iter()=Npost_smooth;
1404  }
1405 
1406  // Complete the setup of all of the smoothers
1407  for (unsigned i=0;i<Nlevel-1;i++)
1408  {
1409  // Pass a pointer to the system matrix on the i-th level to the i-th
1410  // level pre-smoother
1411  Pre_smoothers_storage_pt[i]->smoother_setup(Mg_matrices_storage_pt[i]);
1412 
1413  // Pass a pointer to the system matrix on the i-th level to the i-th
1414  // level post-smoother
1415  Post_smoothers_storage_pt[i]->smoother_setup(Mg_matrices_storage_pt[i]);
1416  }
1417 
1418  // Set up the distributions of each smoother
1419  for (unsigned i=0;i<Nlevel-1;i++)
1420  {
1421  // Get the number of dofs on the i-th level of the hierarchy.
1422  // This will be the same as the size of the solution vector
1423  // associated with the i-th level
1424  unsigned n_dof=X_mg_vectors_storage[i].nrow();
1425 
1426  // Create a LinearAlgebraDistribution which will be passed to the
1427  // linear solver
1428  LinearAlgebraDistribution dist(Mg_hierarchy[i]->communicator_pt(),
1429  n_dof,false);
1430 
1431 #ifdef PARANOID
1432 #ifdef OOMPH_HAS_MPI
1433  // Set up the warning messages
1434  std::string warning_message="Setup of pre- and post-smoother distribution ";
1435  warning_message+="has not been tested with MPI.";
1436 
1437  // If we're not running the code in serial
1438  if (dist.communicator_pt()->nproc()>1)
1439  {
1440  // Throw a warning
1441  OomphLibWarning(warning_message,
1442  OOMPH_CURRENT_FUNCTION,
1443  OOMPH_EXCEPTION_LOCATION);
1444  }
1445 #endif
1446 #endif
1447 
1448  // Build the distribution of the pre-smoother
1449  Pre_smoothers_storage_pt[i]->build_distribution(dist);
1450 
1451  // Build the distribution of the post-smoother
1452  Post_smoothers_storage_pt[i]->build_distribution(dist);
1453  }
1454 
1455  // Disable the smoother output on this level
1456  if (!Doc_time)
1457  {
1458  disable_smoother_and_superlu_doc_time();
1459  }
1460 
1461  // If we're allowed to output
1462  if (!Suppress_all_output)
1463  {
1464  // Stop clock
1465  double t_m_end=TimingHelpers::timer();
1466  double total_setup_time=double(t_m_end-t_m_start);
1467  oomph_info << "CPU time for setup of smoothers on all levels [sec]: "
1468  << total_setup_time << std::endl;
1469 
1470  // Notify user that the extraction is complete
1471  oomph_info << "\n==================Smoother Setup Complete================="
1472  << std::endl;
1473  }
1474  } // End of setup_smoothers
1475 
1476 
1477 //===================================================================
1478 /// \short Setup the interpolation matrices
1479 //===================================================================
1480  template<unsigned DIM>
1482  {
1483  // Variable to hold the number of sons in an element
1484  unsigned n_sons;
1485 
1486  // Number of son elements
1487  if (DIM==2)
1488  {
1489  n_sons=4;
1490  }
1491  else if (DIM==3)
1492  {
1493  n_sons=8;
1494  }
1495  else
1496  {
1497  std::ostringstream error_message_stream;
1498  error_message_stream << "DIM should be 2 or 3 not "
1499  << DIM << std::endl;
1500  throw OomphLibError(error_message_stream.str(),
1501  OOMPH_CURRENT_FUNCTION,
1502  OOMPH_EXCEPTION_LOCATION);
1503  }
1504 
1505  // Vector of local coordinates in the element
1506  Vector<double> s(DIM,0.0);
1507 
1508  // Loop over each level (apart from the coarsest level; an interpolation
1509  // matrix and thus a restriction matrix is not needed here), with 0 being
1510  // the finest level and Nlevel-1 being the coarsest level
1511  for (unsigned level=0;level<Nlevel-1;level++)
1512  {
1513  // Assign values to a couple of variables to help out
1514  unsigned coarse_level=level+1;
1515  unsigned fine_level=level;
1516 
1517  // Make a pointer to the mesh on the finer level and dynamic_cast
1518  // it as an object of the refineable mesh class
1519  RefineableMeshBase* ref_fine_mesh_pt=
1520  dynamic_cast<RefineableMeshBase*>
1521  (Mg_hierarchy[fine_level]->mg_bulk_mesh_pt());
1522 
1523  // Make a pointer to the mesh on the coarse level and dynamic_cast
1524  // it as an object of the refineable mesh class
1525  RefineableMeshBase* ref_coarse_mesh_pt=
1526  dynamic_cast<RefineableMeshBase*>
1527  (Mg_hierarchy[coarse_level]->mg_bulk_mesh_pt());
1528 
1529  // Access information about the number of elements in the fine mesh
1530  // from the pointer to the fine mesh (to loop over the rows of the
1531  // interpolation matrix)
1532  unsigned fine_n_element=ref_fine_mesh_pt->nelement();
1533 
1534  // The numbers of rows and columns in the interpolation matrix. The
1535  // number of unknowns has been divided by 2 since there are 2 dofs at
1536  // each node in the mesh corresponding to the real and imaginary part
1537  // of the solution
1538  unsigned n_rows=Mg_hierarchy[fine_level]->ndof();
1539  unsigned n_cols=Mg_hierarchy[coarse_level]->ndof();
1540 
1541  // Mapping relating the pointers to related elements in the coarse and
1542  // fine meshes: coarse_mesh_element_pt[fine_mesh_element_pt]
1543  // If the element in the fine mesh has been unrefined between these two
1544  // levels, this map returns the father element in the coarsened mesh.
1545  // If this element in the fine mesh has not been unrefined,
1546  // the map returns the pointer to the same-sized equivalent
1547  // element in the coarsened mesh.
1548  std::map<RefineableQElement<DIM>*,RefineableQElement<DIM>*>
1549  coarse_mesh_reference_element_pt;
1550 
1551  // Counter of elements in coarse and fine meshes: Start with element
1552  // zero in both meshes.
1553  unsigned e_coarse=0;
1554  unsigned e_fine=0;
1555 
1556  // While loop over fine elements (while because we're
1557  // incrementing the counter internally if the element was
1558  // unrefined...)
1559  while(e_fine<fine_n_element)
1560  {
1561  // Pointer to element in fine mesh
1562  RefineableQElement<DIM>* el_fine_pt=
1563  dynamic_cast<RefineableQElement<DIM>*>
1564  (ref_fine_mesh_pt->finite_element_pt(e_fine));
1565 
1566  // Pointer to element in coarse mesh
1567  RefineableQElement<DIM>* el_coarse_pt=
1568  dynamic_cast<RefineableQElement<DIM>*>
1569  (ref_coarse_mesh_pt->finite_element_pt(e_coarse));
1570 
1571  // If the levels are different then the element in the fine
1572  // mesh has been unrefined between these two levels
1573  if (el_fine_pt->tree_pt()->level()!=el_coarse_pt->tree_pt()->level())
1574  {
1575  // The element in the fine mesh has been unrefined between these two
1576  // levels. Hence it and its three brothers (ASSUMED to be stored
1577  // consecutively in the Mesh's vector of pointers to its constituent
1578  // elements -- we'll check this!) share the same father element in
1579  // the coarse mesh, currently pointed to by el_coarse_pt.
1580  for(unsigned i=0;i<n_sons;i++)
1581  {
1582  // Set mapping to father element in coarse mesh
1583  coarse_mesh_reference_element_pt[
1584  dynamic_cast<RefineableQElement<DIM>*>
1585  (ref_fine_mesh_pt->finite_element_pt(e_fine))]=el_coarse_pt;
1586 
1587  // Increment counter for elements in fine mesh
1588  e_fine++;
1589  }
1590  }
1591  // The element in the fine mesh has not been unrefined between
1592  // these two levels, so the reference element is the same-sized
1593  // equivalent element in the coarse mesh
1594  else
1595  {
1596  // Set the mapping between the two elements since they are
1597  // the same element
1598  coarse_mesh_reference_element_pt[el_fine_pt]=el_coarse_pt;
1599 
1600  // Increment counter for elements in fine mesh
1601  e_fine++;
1602  }
1603  // Increment counter for elements in coarse mesh
1604  e_coarse++;
1605  } // End of while loop for setting up element-coincidences
1606 
1607  // To allow update of a row only once we use stl vectors for bools
1608  std::vector<bool> contribution_made(n_rows,false);
1609 
1610  // Make storage vectors to form the interpolation matrix using a
1611  // condensed row matrix (CRDoubleMatrix). The i-th value of the vector
1612  // row_start contains entries which tells us at which entry of the
1613  // vector column_start we start on the i-th row of the actual matrix.
1614  // The entries of column_start indicate the column position of each
1615  // non-zero entry in every row. This runs through the first row
1616  // from left to right then the second row (again, left to right)
1617  // and so on until the end. The entries in value are the entries in
1618  // the interpolation matrix. The vector column_start has the same length
1619  // as the vector value.
1620  Vector<double> value;
1621  Vector<int> column_index;
1622  Vector<int> row_start(n_rows+1);
1623 
1624  // The value of index will tell us which row of the interpolation matrix
1625  // we're working on in the following for loop
1626  unsigned index=0;
1627 
1628  // New loop to go over each element in the fine mesh
1629  for (unsigned k=0;k<fine_n_element;k++)
1630  {
1631  // Set a pointer to the element in the fine mesh
1632  RefineableQElement<DIM>* el_fine_pt=
1633  dynamic_cast<RefineableQElement<DIM>*>
1634  (ref_fine_mesh_pt->finite_element_pt(k));
1635 
1636  // Get the reference element (either the father element or the
1637  // same-sized element) in the coarse mesh
1638  RefineableQElement<DIM>* el_coarse_pt=
1639  coarse_mesh_reference_element_pt[el_fine_pt];
1640 
1641  // Find out what type of son it is (set to OMEGA if no unrefinement
1642  // took place)
1643  int son_type=0;
1644 
1645  // Check if the elements are different on both levels (i.e. to check
1646  // if any unrefinement took place)
1647  if (el_fine_pt->tree_pt()->level()!=el_coarse_pt->tree_pt()->level())
1648  {
1649  // If there was refinement we need to find the son type
1650  son_type=el_fine_pt->tree_pt()->son_type();
1651  }
1652  else
1653  {
1654  // If there was no refinement then the son_type is given by the
1655  // value of Tree::OMEGA
1656  son_type=Tree::OMEGA;
1657  }
1658 
1659  // Find the number of nodes in the fine mesh element
1660  unsigned nnod_fine=el_fine_pt->nnode();
1661 
1662  // Loop through all the nodes in an element in the fine mesh
1663  for(unsigned i=0;i<nnod_fine;i++)
1664  {
1665  // Row number in interpolation matrix: Global equation number
1666  // of the d.o.f. stored at this node in the fine element
1667  int ii=el_fine_pt->node_pt(i)->eqn_number(0);
1668 
1669  // Check whether or not the node is a proper d.o.f.
1670  if (ii>=0)
1671  {
1672  // Only assign values to the given row of the interpolation
1673  // matrix if they haven't already been assigned
1674  if(contribution_made[ii]==false)
1675  {
1676  // The value of index was initialised when we allocated space
1677  // for the three vectors to store the CRDoubleMatrix. We use
1678  // index to go through the entries of the row_start vector.
1679  // The next row starts at the value.size() (draw out the entries
1680  // in value if this doesn't make sense noting that the storage
1681  // for the vector 'value' is dynamically allocated)
1682  row_start[index]=value.size();
1683 
1684  // Calculate the local coordinates of the given node
1685  el_fine_pt->local_coordinate_of_node(i,s);
1686 
1687  // Find the local coordinates s, of the present node, in the
1688  // reference element in the coarse mesh, given the element's
1689  // son_type (e.g. SW,SE... )
1690  level_up_local_coord_of_node(son_type,s);
1691 
1692  // Allocate space for shape functions in the coarse mesh
1693  Shape psi(el_coarse_pt->nnode());
1694 
1695  // Set the shape function in the reference element
1696  el_coarse_pt->shape(s,psi);
1697 
1698  // Auxiliary storage
1699  std::map<unsigned,double> contribution;
1700  Vector<unsigned> keys;
1701 
1702  // Find the number of nodes in an element in the coarse mesh
1703  unsigned nnod_coarse=el_coarse_pt->nnode();
1704 
1705  // Loop through all the nodes in the reference element
1706  for (unsigned j=0;j<nnod_coarse;j++)
1707  {
1708  // Column number in interpolation matrix: Global equation
1709  // number of the d.o.f. stored at this node in the coarse
1710  // element
1711  int jj=el_coarse_pt->node_pt(j)->eqn_number(0);
1712 
1713  // If the value stored at this node is pinned or hanging
1714  if (jj<0)
1715  {
1716  // Hanging node: In this case we need to accumulate the
1717  // contributions from the master nodes
1718  if (el_coarse_pt->node_pt(j)->is_hanging())
1719  {
1720  // Find the number of master nodes of the hanging
1721  // the node in the reference element
1722  HangInfo* hang_info_pt=el_coarse_pt->node_pt(j)->hanging_pt();
1723  unsigned nmaster=hang_info_pt->nmaster();
1724 
1725  // Loop over the master nodes
1726  for (unsigned i_master=0;i_master<nmaster;i_master++)
1727  {
1728  // Set up a pointer to the master node
1729  Node* master_node_pt=hang_info_pt->master_node_pt(i_master);
1730 
1731  // The column number in the interpolation matrix: the
1732  // global equation number of the d.o.f. stored at this master
1733  // node for the coarse element
1734  int master_jj=master_node_pt->eqn_number(0);
1735 
1736  // Is the master node a proper d.o.f.?
1737  if (master_jj>=0)
1738  {
1739  // If the weight of the master node is non-zero
1740  if (psi(j)*hang_info_pt->master_weight(i_master)!=0.0)
1741  {
1742  contribution[master_jj]+=
1743  psi(j)*hang_info_pt->master_weight(i_master);
1744  }
1745  } // if (master_jj>=0)
1746  } // for (unsigned i_master=0;i_master<nmaster;i_master++)
1747  } // if (el_coarse_pt->node_pt(j)->is_hanging())
1748  }
1749  // In the case that the node is not pinned or hanging
1750  else
1751  {
1752  // If we can get a nonzero contribution from the shape function
1753  if (psi(j)!=0.0)
1754  {
1755  contribution[jj]+=psi(j);
1756  }
1757  } // if (jj<0) else
1758  } // for (unsigned j=0;j<nnod_coarse;j++)
1759 
1760  // Put the contributions into the value vector
1761  for (std::map<unsigned,double>::iterator it=contribution.begin();
1762  it!=contribution.end(); ++it)
1763  {
1764  if (it->second!=0)
1765  {
1766  column_index.push_back(it->first);
1767  value.push_back(it->second);
1768  }
1769  } // for (std::map<unsigned,double>::iterator it=...)
1770 
1771  // Increment the value of index by 1 to indicate that we have
1772  // finished the row, or equivalently, covered another global
1773  // node in the fine mesh
1774  index++;
1775 
1776  // Change the entry in contribution_made to true now to indicate
1777  // that the row has been filled
1778  contribution_made[ii]=true;
1779  } // if(contribution_made[ii]==false)
1780  } // if (ii>=0)
1781  } // for(unsigned i=0;i<nnod_element;i++)
1782  } // for (unsigned k=0;k<fine_n_element;k++)
1783 
1784  // Set the last entry in the row_start vector
1785  row_start[n_rows]=value.size();
1786 
1787  // Set the interpolation matrix to be that formed as the CRDoubleMatrix
1788  // using the vectors value, row_start, column_index and the value
1789  // of fine_n_unknowns and coarse_n_unknowns
1790  interpolation_matrix_set(level,
1791  value,
1792  column_index,
1793  row_start,
1794  n_cols,
1795  n_rows);
1796  } // for (unsigned level=0;level<Nlevel-1;level++)
1797  } // End of setup_interpolation_matrices
1798 
1799 //===================================================================
1800 /// \short Setup the interpolation matrices
1801 //===================================================================
1802  template<unsigned DIM>
1804  {
1805  // Vector of local coordinates in the element
1806  Vector<double> s(DIM,0.0);
1807 
1808  // Loop over each level (apart from the coarsest level; an interpolation
1809  // matrix and thus a restriction matrix is not needed here), with 0 being
1810  // the finest level and Nlevel-1 being the coarsest level
1811  for (unsigned level=0;level<Nlevel-1;level++)
1812  {
1813  // Assign values to a couple of variables to help out
1814  unsigned coarse_level=level+1;
1815  unsigned fine_level=level;
1816 
1817  // Make a pointer to the mesh on the finer level and dynamic_cast
1818  // it as an object of the refineable mesh class
1819  Mesh* ref_fine_mesh_pt=Mg_hierarchy[fine_level]->mg_bulk_mesh_pt();
1820 
1821  // To use the locate zeta functionality the coarse mesh must be of the
1822  // type MeshAsGeomObject
1823  MeshAsGeomObject* coarse_mesh_from_obj_pt=
1824  new MeshAsGeomObject(Mg_hierarchy[coarse_level]->mg_bulk_mesh_pt());
1825 
1826  // Access information about the number of degrees of freedom
1827  // from the pointers to the problem on each level
1828  unsigned coarse_n_unknowns=Mg_hierarchy[coarse_level]->ndof();
1829  unsigned fine_n_unknowns=Mg_hierarchy[fine_level]->ndof();
1830 
1831  // Make storage vectors to form the interpolation matrix using a
1832  // condensed row matrix (CRDoubleMatrix). The i-th value of the vector
1833  // row_start contains entries which tells us at which entry of the
1834  // vector column_start we start on the i-th row of the actual matrix.
1835  // The entries of column_start indicate the column position of each
1836  // non-zero entry in every row. This runs through the first row
1837  // from left to right then the second row (again, left to right)
1838  // and so on until the end. The entries in value are the entries in
1839  // the interpolation matrix. The vector column_start has the same length
1840  // as the vector value.
1841  Vector<double> value;
1842  Vector<int> column_index;
1843  Vector<int> row_start(fine_n_unknowns+1);
1844 
1845  // Vector to contain the (Eulerian) spatial location of the fine node
1846  Vector<double> fine_node_position(DIM);
1847 
1848  // Find the number of nodes in the mesh
1849  unsigned n_node_fine_mesh=ref_fine_mesh_pt->nnode();
1850 
1851  // Loop over the unknowns in the mesh
1852  for (unsigned i_fine_node=0;i_fine_node<n_node_fine_mesh;i_fine_node++)
1853  {
1854  // Set a pointer to the i_fine_unknown-th node in the fine mesh
1855  Node* fine_node_pt=ref_fine_mesh_pt->node_pt(i_fine_node);
1856 
1857  // Get the global equation number
1858  int i_fine=fine_node_pt->eqn_number(0);
1859 
1860  // If the node is a proper d.o.f.
1861  if (i_fine>=0)
1862  {
1863  // Row number in interpolation matrix: Global equation number
1864  // of the d.o.f. stored at this node in the fine element
1865  row_start[i_fine]=value.size();
1866 
1867  // Get the (Eulerian) spatial location of the fine node
1868  fine_node_pt->position(fine_node_position);
1869 
1870  // Create a null pointer to the GeomObject class
1871  GeomObject* el_pt=0;
1872 
1873  // Get the reference element (either the father element or the
1874  // same-sized element) in the coarse mesh using locate_zeta
1875  coarse_mesh_from_obj_pt->locate_zeta(fine_node_position,el_pt,s);
1876 
1877  // Upcast GeomElement as a FiniteElement
1878  FiniteElement* el_coarse_pt=dynamic_cast<FiniteElement*>(el_pt);
1879 
1880  // Find the number of nodes in the element
1881  unsigned n_node=el_coarse_pt->nnode();
1882 
1883  // Allocate space for shape functions in the coarse mesh
1884  Shape psi(n_node);
1885 
1886  // Calculate the geometric shape functions at local coordinate s
1887  el_coarse_pt->shape(s,psi);
1888 
1889  // Auxiliary storage
1890  std::map<unsigned,double> contribution;
1891  Vector<unsigned> keys;
1892 
1893  // Loop through all the nodes in the (coarse mesh) element containing the
1894  // node pointed to by fine_node_pt (fine mesh)
1895  for(unsigned j_node=0;j_node<n_node;j_node++)
1896  {
1897  // Get the j_coarse_unknown-th node in the coarse element
1898  Node* coarse_node_pt=el_coarse_pt->node_pt(j_node);
1899 
1900  // Column number in interpolation matrix: Global equation number of
1901  // the d.o.f. stored at this node in the coarse element
1902  int j_coarse=coarse_node_pt->eqn_number(0);
1903 
1904  // If the value stored at this node is pinned or hanging
1905  if (j_coarse<0)
1906  {
1907  // Hanging node: In this case we need to accumulate the
1908  // contributions from the master nodes
1909  if (el_coarse_pt->node_pt(j_node)->is_hanging())
1910  {
1911  // Find the number of master nodes of the hanging
1912  // the node in the reference element
1913  HangInfo* hang_info_pt=coarse_node_pt->hanging_pt();
1914  unsigned nmaster=hang_info_pt->nmaster();
1915 
1916  // Loop over the master nodes
1917  for (unsigned i_master=0;i_master<nmaster;i_master++)
1918  {
1919  // Set up a pointer to the master node
1920  Node* master_node_pt=hang_info_pt->master_node_pt(i_master);
1921 
1922  // The column number in the interpolation matrix: the
1923  // global equation number of the d.o.f. stored at this master
1924  // node for the coarse element
1925  int master_jj=master_node_pt->eqn_number(0);
1926 
1927  // Is the master node a proper d.o.f.?
1928  if (master_jj>=0)
1929  {
1930  // If the weight of the master node is non-zero
1931  if (psi(j_node)*hang_info_pt->master_weight(i_master)!=0.0)
1932  {
1933  contribution[master_jj]+=
1934  psi(j_node)*hang_info_pt->master_weight(i_master);
1935  }
1936  } // End of if statement (check it's not a boundary node)
1937  } // End of the loop over the master nodes
1938  } // End of the if statement for only hanging nodes
1939  } // End of the if statement for pinned or hanging nodes
1940  // In the case that the node is not pinned or hanging
1941  else
1942  {
1943  // If we can get a nonzero contribution from the shape function
1944  // at the j_node-th node in the element
1945  if (psi(j_node)!=0.0)
1946  {
1947  contribution[j_coarse]+=psi(j_node);
1948  }
1949  } // End of the if-else statement (check if the node was pinned/hanging)
1950  } // Finished loop over the nodes j in the reference element (coarse)
1951 
1952  // Put the contributions into the value vector
1953  for (std::map<unsigned,double>::iterator it=contribution.begin();
1954  it!=contribution.end(); ++it)
1955  {
1956  if (it->second!=0)
1957  {
1958  value.push_back(it->second);
1959  column_index.push_back(it->first);
1960  }
1961  } // End of putting contributions into the value vector
1962  } // End check (whether or not the fine node was a d.o.f.)
1963  } // End of the for-loop over nodes in the fine mesh
1964 
1965  // Set the last entry of row_start
1966  row_start[fine_n_unknowns]=value.size();
1967 
1968  // Set the interpolation matrix to be that formed as the CRDoubleMatrix
1969  // using the vectors value, row_start, column_index and the value
1970  // of fine_n_unknowns and coarse_n_unknowns
1971  interpolation_matrix_set(level,
1972  value,
1973  column_index,
1974  row_start,
1975  coarse_n_unknowns,
1976  fine_n_unknowns);
1977  } // End of loop over each level
1978  } // End of setup_interpolation_matrices_unstructured
1979 
1980  //===================================================================
1981  /// \short Restrict residual (computed on current MG level) to
1982  /// next coarser mesh and stick it into the coarse mesh RHS vector
1983  /// using the restriction matrix (if restrict_flag=1) or the transpose
1984  /// of the interpolation matrix (if restrict_flag=2)
1985  //===================================================================
1986  template<unsigned DIM>
1987  void MGSolver<DIM>::restrict_residual(const unsigned& level)
1988  {
1989 #ifdef PARANOID
1990  // Check to make sure we can actually restrict the vector
1991  if (!(level<Nlevel-1))
1992  {
1993  throw OomphLibError("Input exceeds the possible parameter choice.",
1994  OOMPH_CURRENT_FUNCTION,
1995  OOMPH_EXCEPTION_LOCATION);
1996  }
1997 #endif
1998 
1999  // Multiply the residual vector by the restriction matrix on the level-th
2000  // level (to restrict the vector down to the next coarser level)
2001  Restriction_matrices_storage_pt[level]->
2002  multiply(Residual_mg_vectors_storage[level],
2003  Rhs_mg_vectors_storage[level+1]);
2004  } // End of restrict_residual
2005 
2006  //===================================================================
2007  /// \short Interpolate solution at current level onto
2008  /// next finer mesh and correct the solution x at that level
2009  //===================================================================
2010  template<unsigned DIM>
2011  void MGSolver<DIM>::interpolate_and_correct(const unsigned& level)
2012  {
2013 #ifdef PARANOID
2014  // Check to make sure we can actually restrict the vector
2015  if (!(level>0))
2016  {
2017  throw OomphLibError("Input level exceeds the possible parameter choice.",
2018  OOMPH_CURRENT_FUNCTION,
2019  OOMPH_EXCEPTION_LOCATION);
2020  }
2021 #endif
2022 
2023  // Build distribution of a temporary vector
2024  DoubleVector temp_soln(X_mg_vectors_storage[level-1].distribution_pt());
2025 
2026  // Interpolate the solution vector
2027  Interpolation_matrices_storage_pt[level-1]->
2028  multiply(X_mg_vectors_storage[level],temp_soln);
2029 
2030  // Update
2031  X_mg_vectors_storage[level-1]+=temp_soln;
2032  } // End of interpolate_and_correct
2033 
2034 //===================================================================
2035 /// \short Modify the restriction matrices
2036 //===================================================================
2037  template<unsigned DIM>
2039  {
2040  // Create a null pointer
2041  CRDoubleMatrix* restriction_matrix_pt=0;
2042 
2043  // Loop over the levels
2044  for (unsigned level=0;level<Nlevel-1;level++)
2045  {
2046  // Store a pointer to the (level)-th restriction matrix
2047  restriction_matrix_pt=Restriction_matrices_storage_pt[level];
2048 
2049  // Get access to the row start data
2050  const int* row_start_pt=restriction_matrix_pt->row_start();
2051 
2052  // Get access to the matrix entries
2053  double* value_pt=restriction_matrix_pt->value();
2054 
2055  // Initialise an auxiliary variable to store the index of the start
2056  // of the i-th row
2057  unsigned start_index=0;
2058 
2059  // Initialise an auxiliary variable to store the index of the start
2060  // of the (i+1)-th row
2061  unsigned end_index=0;
2062 
2063  // Store the number of rows in the matrix
2064  unsigned n_row=restriction_matrix_pt->nrow();
2065 
2066  // Loop over the rows of the matrix
2067  for (unsigned i=0;i<n_row;i++)
2068  {
2069  // Index associated with the start of the i-th row
2070  start_index=row_start_pt[i];
2071 
2072  // Index associated with the start of the (i+1)-th row
2073  end_index=row_start_pt[i+1];
2074 
2075  // Variable to store the sum of the absolute values of the off-diagonal
2076  // entries in the i-th row
2077  double row_sum=0.0;
2078 
2079  // Loop over the entries in the row
2080  for (unsigned j=start_index;j<end_index;j++)
2081  {
2082  // Add the absolute value of this entry to the off-diagonal sum
2083  row_sum+=value_pt[j];
2084  }
2085 
2086  // Loop over the entries in the row
2087  for (unsigned j=start_index;j<end_index;j++)
2088  {
2089  // Normalise the row entry
2090  value_pt[j]/=row_sum;
2091  }
2092  } // for (unsigned i=0;i<n_row;i++)
2093  } // for (unsigned level=0;level<Nlevel-1;level++)
2094  } // End of modify_restriction_matrices
2095 
2096  //===================================================================
2097  /// \short Makes a vector, restricts it down the levels of the hierarchy
2098  /// and documents it at each level. After this is done the vector is
2099  /// interpolated up the levels of the hierarchy with the output
2100  /// being documented at each level
2101  //===================================================================
2102  template<unsigned DIM>
2104  {
2105  // Start the timer
2106  double t_st_start=TimingHelpers::timer();
2107 
2108  // Notify user that the hierarchy of levels is complete
2109  oomph_info << "\nStarting the multigrid solver self-test."
2110  << std::endl;
2111 
2112  // Resize the vector storing all of the restricted vectors
2113  Restriction_self_test_vectors_storage.resize(Nlevel);
2114 
2115  // Resize the vector storing all of the interpolated vectors
2116  Interpolation_self_test_vectors_storage.resize(Nlevel);
2117 
2118  // Find the number of dofs on the finest level
2119  unsigned n_dof=X_mg_vectors_storage[0].nrow();
2120 
2121  // Need to set up the distribution of the finest level vector
2122  LinearAlgebraDistribution* dist_pt=
2123  new LinearAlgebraDistribution(Mg_problem_pt->communicator_pt(),n_dof,false);
2124 
2125 #ifdef PARANOID
2126 #ifdef OOMPH_HAS_MPI
2127  // Set up the warning messages
2128  std::string warning_message="Setup of distribution has not been ";
2129  warning_message+="tested with MPI.";
2130 
2131  // If we're not running the code in serial
2132  if (dist_pt->communicator_pt()->nproc()>1)
2133  {
2134  // Throw a warning
2135  OomphLibWarning(warning_message,
2136  OOMPH_CURRENT_FUNCTION,
2137  OOMPH_EXCEPTION_LOCATION);
2138  }
2139 #endif
2140 #endif
2141 
2142  // Build the vector using the distribution of the restriction matrix
2143  Restriction_self_test_vectors_storage[0].build(dist_pt);
2144 
2145  // Delete the distribution pointer
2146  delete dist_pt;
2147 
2148  // Make it a null pointer
2149  dist_pt=0;
2150 
2151  // Now assign the values to the first vector. This will be restricted down
2152  // the levels of the hierarchy then back up
2153  set_self_test_vector();
2154 
2155  // Call the restriction self-test
2156  restriction_self_test();
2157 
2158  // Set the coarest level vector in Restriction_self_test_vectors_storage
2159  // to be the last entry in Interpolation_self_test_vectors_storage. This
2160  // will be interpolated up to the finest level in interpolation_self_test()
2161  Interpolation_self_test_vectors_storage[Nlevel-1]=
2162  Restriction_self_test_vectors_storage[Nlevel-1];
2163 
2164  // Call the interpolation self-test
2165  interpolation_self_test();
2166 
2167  // Destroy all of the created restriction self-test vectors
2168  Restriction_self_test_vectors_storage.resize(0);
2169 
2170  // Destroy all of the created interpolation self-test vectors
2171  Interpolation_self_test_vectors_storage.resize(0);
2172 
2173  // Stop the clock
2174  double t_st_end=TimingHelpers::timer();
2175  double total_st_time=double(t_st_end-t_st_start);
2176  oomph_info << "\nCPU time for self-test [sec]: " << total_st_time << std::endl;
2177 
2178  // Notify user that the hierarchy of levels is complete
2179  oomph_info << "\n====================Self-Test Complete===================="
2180  << std::endl;
2181  } // End of self_test
2182 
2183  //===================================================================
2184  /// \short Sets the initial vector to be used in the restriction and
2185  /// interpolation self-tests
2186  //===================================================================
2187  template<unsigned DIM>
2189  {
2190  // Set up a pointer to the refineable mesh
2191  TreeBasedRefineableMeshBase* bulk_mesh_pt=
2192  Mg_problem_pt->mg_bulk_mesh_pt();
2193 
2194  // Find the number of elements in the bulk mesh
2195  unsigned n_el=bulk_mesh_pt->nelement();
2196 
2197  // Get the dimension of the problem (assumed to be the dimension of any
2198  // node in the mesh)
2199  unsigned n_dim=bulk_mesh_pt->node_pt(0)->ndim();
2200 
2201  // Find the number of nodes in an element
2202  unsigned nnod=bulk_mesh_pt->finite_element_pt(0)->nnode();
2203 
2204  // Loop over all elements
2205  for (unsigned e=0;e<n_el;e++)
2206  {
2207  // Get pointer to element
2208  FiniteElement* el_pt=bulk_mesh_pt->finite_element_pt(e);
2209 
2210  // Loop over nodes
2211  for (unsigned j=0;j<nnod;j++)
2212  {
2213  // Pointer to node
2214  Node* nod_pt=el_pt->node_pt(j);
2215 
2216  // Sanity check
2217  if (nod_pt->nvalue()!=1)
2218  {
2219  // Set up an output stream
2220  std::ostringstream error_stream;
2221 
2222  // Output the error message
2223  error_stream << "Sorry, not sure what to do here! I can't deal with "
2224  << nod_pt->nvalue() << " values!" << std::endl;
2225 
2226  // Throw an error to indicate that it was not possible to plot the data
2227  throw OomphLibError(error_stream.str(),
2228  OOMPH_CURRENT_FUNCTION,
2229  OOMPH_EXCEPTION_LOCATION);
2230  }
2231 
2232  // Free or pinned?
2233  int eqn_num=nod_pt->eqn_number(0);
2234 
2235  // If it's a free node
2236  if (eqn_num>=0)
2237  {
2238  // Initialise the variable coordinate_sum
2239  double coordinate_sum=0.0;
2240 
2241  // Loop over the coordinates of the node
2242  for (unsigned i=0;i<n_dim;i++)
2243  {
2244  // Increment coordinate_sum by the value of x(i)
2245  coordinate_sum+=nod_pt->x(i);
2246  }
2247 
2248  // Store the value of pi in cache
2249  double pi=MathematicalConstants::Pi;
2250 
2251  // Make the vector represent a constant function
2252  Restriction_self_test_vectors_storage[0][eqn_num]=
2253  sin(pi*(nod_pt->x(0)))*sin(pi*(nod_pt->x(1)));
2254  }
2255  } // for (unsigned j=0;j<nnod;j++)
2256  } // for (unsigned e=0;e<n_el;e++)
2257 
2258  // // Get the size of the vector
2259  // unsigned n_row=Restriction_self_test_vectors_storage[0].nrow();
2260 
2261  // // Loop over the entries of the vector
2262  // for (unsigned i=0;i<n_row;i++)
2263  // {
2264  // // Make the vector represent a constant function
2265  // Restriction_self_test_vectors_storage[0][i]=1.0;
2266  // }
2267  } // End of set_self_test_vector
2268 
2269  //===================================================================
2270  /// \short Function which implements a self-test to restrict the
2271  /// residual vector down all of the coarser grids and output
2272  /// the restricted vectors to file
2273  //===================================================================
2274  template<unsigned DIM>
2276  {
2277  // Set the start of the output file name. The full names will
2278  // set after we calculate the restricted vectors
2279  std::string outputfile="RESLT/restriction_self_test";
2280 
2281  // Loop over the levels of the hierarchy
2282  for (unsigned level=0;level<Nlevel-1;level++)
2283  {
2284  // Restrict the vector down to the next level
2285  Restriction_matrices_storage_pt[level]->
2286  multiply(Restriction_self_test_vectors_storage[level],
2287  Restriction_self_test_vectors_storage[level+1]);
2288  } // End of the for loop over the hierarchy levels
2289 
2290  // Loop over the levels of hierarchy to plot the restricted vectors
2291  for (unsigned level=0;level<Nlevel;level++)
2292  {
2293  // Set output file name
2294  std::string filename=outputfile;
2295  std::stringstream string;
2296  string << level;
2297  filename+=string.str();
2298  filename+=".dat";
2299 
2300  // Plot the RHS vector on the current level
2301  plot(level,Restriction_self_test_vectors_storage[level],filename);
2302 
2303  } // End of for-loop to output the RHS vector on each level
2304  } // End of restriction_self_test
2305 
2306  //=======================================================================
2307  /// \short Function which implements a self-test to interpolate a
2308  /// vector up all of levels of the MG hierarchy and outputs the
2309  /// restricted vectors to file
2310  //=======================================================================
2311  template<unsigned DIM>
2313  {
2314  // Set the start of the output file name. The full names will
2315  // set after we calculate the interpolated vectors
2316  std::string outputfile="RESLT/interpolation_self_test";
2317 
2318  // Loop over the levels of the hierarchy in reverse order
2319  for (unsigned level=Nlevel-1;level>0;level--)
2320  {
2321  // Interpolate the vector up a level
2322  Interpolation_matrices_storage_pt[level-1]->
2323  multiply(Interpolation_self_test_vectors_storage[level],
2324  Interpolation_self_test_vectors_storage[level-1]);
2325  } // End of the for loop over the hierarchy levels
2326 
2327  for (unsigned level=0;level<Nlevel;level++)
2328  {
2329  // Set output file name
2330  std::string filename=outputfile;
2331  std::stringstream string;
2332  string << level;
2333  filename+=string.str();
2334  filename+=".dat";
2335 
2336  // Plot the RHS vector on the current level
2337  plot(level,Interpolation_self_test_vectors_storage[level],filename);
2338 
2339  } // End of for-loop to output the RHS vector on each level
2340  } // End of interpolation_self_test
2341 
2342  //===================================================================
2343  /// \short Plots the input vector (assuming we're dealing with scalar
2344  /// nodal data, otherwise I don't know how to implement this...)
2345  //===================================================================
2346  template<unsigned DIM>
2347  void MGSolver<DIM>::plot(const unsigned& hierarchy_level,
2348  const DoubleVector& input_vector,
2349  const std::string& filename)
2350  {
2351  // Setup an output file
2352  std::ofstream some_file;
2353  some_file.open(filename.c_str());
2354 
2355  // Set up a pointer to the refineable mesh
2356  TreeBasedRefineableMeshBase* bulk_mesh_pt=
2357  Mg_hierarchy[hierarchy_level]->mg_bulk_mesh_pt();
2358 
2359  // Find the number of elements in the bulk mesh
2360  unsigned n_el=bulk_mesh_pt->nelement();
2361 
2362  // Get the dimension of the problem (assumed to be the dimension of any
2363  // node in the mesh)
2364  unsigned n_dim=bulk_mesh_pt->node_pt(0)->ndim();
2365 
2366  // Find the number of nodes in an element
2367  unsigned nnod=bulk_mesh_pt->finite_element_pt(0)->nnode();
2368 
2369  // Find the number of nodes in an element in one direction
2370  unsigned nnod_1d=bulk_mesh_pt->finite_element_pt(0)->nnode_1d();
2371 
2372  // Loop over all elements
2373  for (unsigned e=0;e<n_el;e++)
2374  {
2375  // Get pointer to element
2376  FiniteElement* el_pt=bulk_mesh_pt->finite_element_pt(e);
2377 
2378  // Input string for tecplot zone header (when plotting nnode_1d
2379  // points in each coordinate direction)
2380  some_file << el_pt->tecplot_zone_string(nnod_1d) << std::endl;
2381 
2382  // Loop over nodes
2383  for (unsigned j=0;j<nnod;j++)
2384  {
2385  // Pointer to node
2386  Node* nod_pt=el_pt->node_pt(j);
2387 
2388  // Sanity check
2389  if (nod_pt->nvalue()!=1)
2390  {
2391  std::ostringstream error_stream;
2392 
2393  error_stream << "Sorry, not sure what to do here!"
2394  << nod_pt->nvalue() << std::endl;
2395 
2396  // Output the value of dimension to screen
2397  error_stream << "The dimension is: " << n_dim << std::endl;
2398 
2399  // Output the values of x_i for all i
2400  for (unsigned i=0;i<n_dim;i++)
2401  {
2402  error_stream << nod_pt->x(i) << " ";
2403  }
2404 
2405  // End line
2406  error_stream << std::endl;
2407 
2408  // Throw an error to indicate that it was
2409  // not possible to plot the data
2410  throw OomphLibError(error_stream.str(),
2411  OOMPH_CURRENT_FUNCTION,
2412  OOMPH_EXCEPTION_LOCATION);
2413  }
2414 
2415  // Output the coordinates of the node
2416  for (unsigned i=0;i<n_dim;i++)
2417  {
2418  some_file << nod_pt->x(i) << " ";
2419  }
2420 
2421  // Free or pinned?
2422  int e=nod_pt->eqn_number(0);
2423  if (e>=0)
2424  {
2425  // Output the e-th entry if the node is free
2426  some_file << input_vector[e] << std::endl;
2427  }
2428  else
2429  {
2430  // Boundary node
2431  if (nod_pt->is_on_boundary())
2432  {
2433  // On the finest level the boundary data is pinned so set to 0
2434  if (hierarchy_level==0)
2435  {
2436  some_file << 0.0 << std::endl;
2437  }
2438  // On any other level we output this data since it corresponds
2439  // to the correction on the boundary nodes
2440  else
2441  {
2442  some_file << input_vector[e] << std::endl;
2443  }
2444  }
2445  // Hanging node
2446  else if (nod_pt->is_hanging())
2447  {
2448  // Allocate space for a double to store the weighted sum
2449  // of the surrounding master nodes of the hanging node
2450  double hang_sum=0.0;
2451 
2452  // Find the number of master nodes of the hanging
2453  // the node in the reference element
2454  HangInfo* hang_info_pt=nod_pt->hanging_pt();
2455  unsigned nmaster=hang_info_pt->nmaster();
2456 
2457  // Loop over the master nodes
2458  for (unsigned i_master=0;i_master<nmaster;i_master++)
2459  {
2460  // Set up a pointer to the master node
2461  Node* master_node_pt=hang_info_pt->master_node_pt(i_master);
2462 
2463  // Get the global equation number of this node
2464  int master_jj=master_node_pt->eqn_number(0);
2465 
2466  // Is the master node a proper d.o.f.?
2467  if (master_jj>=0)
2468  {
2469  // If the weight of the master node is non-zero
2470  if (hang_info_pt->master_weight(i_master)!=0.0)
2471  {
2472  hang_sum+=hang_info_pt->
2473  master_weight(i_master)*input_vector[master_jj];
2474  }
2475  } // if (master_jj>=0)
2476  } // for (unsigned i_master=0;i_master<nmaster;i_master++)
2477 
2478  // Output the weighted sum of the surrounding master nodes
2479  some_file << hang_sum << std::endl;
2480  }
2481  } // if (e>=0)
2482  } // for (unsigned j=0;j<nnod;j++)
2483  } // for (unsigned e=0;e<n_el;e++)
2484 
2485  // Close output file
2486  some_file.close();
2487  } // End of plot
2488 
2489  //===================================================================
2490  /// \short Linear solver
2491  //===================================================================
2492  template<unsigned DIM>
2494  {
2495  // If we're allowed to time things
2496  double t_mg_start=0.0;
2497  if (!Suppress_v_cycle_output)
2498  {
2499  // Start the clock!
2500  t_mg_start=TimingHelpers::timer();
2501  }
2502 
2503  // Current level
2504  unsigned finest_level=0;
2505 
2506  // Initialise the V-cycle counter
2507  V_cycle_counter=0;
2508 
2509  // Calculate the norm of the residual then output
2510  double normalised_residual_norm=residual_norm(finest_level);
2511  if (!Suppress_v_cycle_output)
2512  {
2513  oomph_info << "\nResidual on finest level for V-cycle: "
2514  << normalised_residual_norm << std::endl;
2515  }
2516 
2517  // Outer loop over V-cycles
2518  //-------------------------
2519  // While the tolerance is not met and the maximum number of
2520  // V-cycles has not been completed
2521  while((normalised_residual_norm>(this->Tolerance)) &&
2522  (V_cycle_counter!=Nvcycle))
2523  {
2524  if (!Suppress_v_cycle_output)
2525  {
2526  // Output the V-cycle counter
2527  oomph_info << "\nStarting V-cycle: " << V_cycle_counter << std::endl;
2528  }
2529 
2530  // Loop downwards over all levels that have coarser levels beneath them
2531  //---------------------------------------------------------------------
2532  for (unsigned i=0;i<Nlevel-1;i++)
2533  {
2534  // Initialise x_mg and residual_mg to 0.0 except for the finest level
2535  // since x_mg contains the current approximation to the solution and
2536  // residual_mg contains the RHS vector on the finest level
2537  if(i!=0)
2538  {
2539  // Loop over the entries in x_mg and residual_mg
2540  X_mg_vectors_storage[i].initialise(0.0);
2541  Residual_mg_vectors_storage[i].initialise(0.0);
2542  }
2543 
2544  // Perform a few pre-smoothing steps and return vector that contains
2545  // the residuals of the linear system at this level.
2546  pre_smooth(i);
2547 
2548  // Restrict the residual to the next coarser mesh and
2549  // assign it to the RHS vector at that level
2550  restrict_residual(i);
2551  } // Moving down the V-cycle
2552 
2553  // Reached the lowest level: Do a direct solve, using the RHS vector
2554  //------------------------------------------------------------------
2555  // obtained by restriction from above.
2556  //------------------------------------
2557  direct_solve();
2558 
2559  // Loop upwards over all levels that have finer levels above them
2560  //---------------------------------------------------------------
2561  for (unsigned i=Nlevel-1;i>0;i--)
2562  {
2563  // Interpolate solution at current level onto
2564  // next finer mesh and correct the solution x at that level
2565  interpolate_and_correct(i);
2566 
2567  // Perform a few post-smoothing steps (ignore
2568  // vector that contains the residuals of the linear system
2569  // at this level)
2570  post_smooth(i-1);
2571  }
2572 
2573  // Calculate the new residual norm then output (if allowed)
2574  normalised_residual_norm=residual_norm(finest_level);
2575 
2576  // If required, we will document the convergence history to screen or file
2577  // (if stream open)
2578  if (Doc_convergence_history)
2579  {
2580  if (!Output_file_stream.is_open())
2581  {
2582  oomph_info << V_cycle_counter << " "
2583  << normalised_residual_norm << std::endl;
2584  }
2585  else
2586  {
2587  Output_file_stream << V_cycle_counter << " "
2588  << normalised_residual_norm << std::endl;
2589  }
2590  } // if (Doc_convergence_history)
2591 
2592  // Set counter for number of cycles (for doc)
2593  V_cycle_counter++;
2594 
2595  // Print the residual on the finest level
2596  if (!Suppress_v_cycle_output)
2597  {
2598  oomph_info << "Residual on finest level of V-cycle: "
2599  << normalised_residual_norm << std::endl;
2600  }
2601  } // End of the V-cycles
2602 
2603  // Copy the solution into the result vector
2604  result=X_mg_vectors_storage[finest_level];
2605 
2606  // Need an extra line space if V-cycle output is suppressed
2607  if (!Suppress_v_cycle_output)
2608  {
2609  oomph_info << std::endl;
2610  }
2611 
2612  // If all output is to be suppressed
2613  if (!Suppress_all_output)
2614  {
2615  // Output number of V-cycles taken to solve
2616  if (normalised_residual_norm<(this->Tolerance))
2617  {
2618  // Indicate that the problem has been solved
2619  Has_been_solved=true;
2620  }
2621  } // if (!Suppress_all_output)
2622 
2623  // If the V-cycle output isn't suppressed
2624  if (!Suppress_v_cycle_output)
2625  {
2626  // Stop the clock
2627  double t_mg_end=TimingHelpers::timer();
2628  double total_G_setup_time=double(t_mg_end-t_mg_start);
2629  oomph_info << "CPU time for MG solver [sec]: "
2630  << total_G_setup_time << std::endl;
2631  }
2632  } // End of mg_solve
2633 } // End of namespace oomph
2634 
2635 #endif
virtual std::string tecplot_zone_string(const unsigned &nplot) const
Return string for tecplot zone header (when plotting nplot points in each "coordinate direction") ...
Definition: elements.h:2990
MGPreconditioner(const MGPreconditioner &)
Broken copy constructor.
MGProblem * Mg_problem_pt
Pointer to the MG problem (deep copy). This is protected to provide access to the MG preconditioner...
void setup_smoothers()
Function to set up all of the smoothers once the system matrices have been set up.
void setup_mg_hierarchy()
Function to set up the hierachy of levels. Creates a vector of pointers to each MG level...
bool Doc_everything
If this is set to true we document everything. In addition to outputting the information of the setup...
void broken_copy(const std::string &class_name)
Issue error message and terminate execution.
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
double & tolerance()
Access to convergence tolerance.
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 ...
Nullstream oomph_nullstream
Single (global) instantiation of the Nullstream.
bool Suppress_v_cycle_output
Indicates whether or not the V-cycle output should be suppressed. Needs to be protected member data f...
Vector< MGProblem * > Mg_hierarchy
Vector containing pointers to problems in hierarchy.
void disable_smoother_and_superlu_doc_time()
Suppress the output of both smoothers and SuperLU.
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
std::ostream * Stream_pt
Pointer to the output stream – defaults to std::cout. This is protected member data to allow the pre...
virtual ~MGProblem()
Destructor (empty)
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...
cstr elem_len * i
Definition: cfortran.h:607
Vector< Smoother * > Pre_smoothers_storage_pt
Vector to store the pre-smoothers.
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...
const double Pi
50 digits from maple
virtual void actions_after_adapt()
Actions that are to be performed after a mesh adaptation.
Definition: problem.h:1007
Vector< Smoother * > Post_smoothers_storage_pt
Vector to store the post-smoothers.
void direct_solve()
Call the direct solver (SuperLU) to solve the problem exactly.
The Problem class.
Definition: problem.h:152
OomphCommunicator * communicator_pt() const
const access to the communicator pointer
MGSolver(MGProblem *mg_problem_pt)
Constructor: Set up default values for number of V-cycles and pre- and post-smoothing steps...
bool Has_been_setup
Boolean variable to indicate whether or not the solver has been setup.
void setup_interpolation_matrices_unstructured()
Setup the interpolation matrix on each level (used for unstructured meshes)
A general Finite Element class.
Definition: elements.h:1274
unsigned iterations() const
Number of iterations.
void setup_mg_structures()
Function to set up the hierachy of levels. Creates a vector of pointers to each MG level...
double const & master_weight(const unsigned &i) const
Return weight for dofs on i-th master node.
Definition: nodes.h:753
PostSmootherFactoryFctPt Post_smoother_factory_function_pt
Function to create post-smoothers.
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 set_post_smoother_factory_function(PostSmootherFactoryFctPt post_smoother_fn)
Access function to set the post-smoother creation function.
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...
void clean_up_memory()
Clean up memory.
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
static OomphCommunicator * communicator_pt()
access to the global oomph-lib communicator
void plot(const unsigned &hierarchy_level, const DoubleVector &input_vector, const std::string &filename)
Given a level in the hierarchy, an input vector and a filename this function will document the given ...
Mesh *& mesh_pt()
Return a pointer to the global mesh.
Definition: problem.h:1264
OomphInfo oomph_info
void setup()
Function to set up a preconditioner for the linear system.
bool is_hanging() const
Test whether the node is geometrically hanging.
Definition: nodes.h:1207
long & eqn_number(const unsigned &i)
Return the equation number of the i-th stored variable.
Definition: nodes.h:365
unsigned nvalue() const
Return number of values stored in data object (incl pinned ones).
Definition: nodes.h:448
unsigned Npre_smooth
Number of pre-smoothing steps.
unsigned Nvcycle
Maximum number of V-cycles (this is set as a protected variable so.
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_interpolation_matrices()
Setup the interpolation matrix on each level.
PreSmootherFactoryFctPt Pre_smoother_factory_function_pt
Function to create pre-smoothers.
Vector< DoubleVector > X_mg_vectors_storage
Vector to store the solution vectors (X_mg)
unsigned long nelement() const
Return number of elements in the mesh.
Definition: mesh.h:587
virtual MGProblem * make_new_problem()=0
This function needs to be implemented in the derived problem: Returns a pointer to a new object of th...
void set_pre_smoother_factory_function(PreSmootherFactoryFctPt pre_smoother_fn)
Access function to set the pre-smoother creation function.
unsigned ndim() const
Return (Eulerian) spatial dimension of the node.
Definition: nodes.h:992
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...
void enable_doc_everything()
Enable the output from anything that could have been suppressed.
virtual void preconditioner_solve(const DoubleVector &rhs, DoubleVector &z)
Function applies MG to the vector r for a full solve.
~MGSolver()
Delete any dynamically allocated data.
void full_setup()
Do a full setup (assumes everything will be setup around the MGProblem pointer given in the construct...
std::ostream *& stream_pt()
Access function for the stream pointer.
Base class for tree-based refineable meshes.
void mg_solve(DoubleVector &result)
Do the actual solve – this is called through the pure virtual solve function in the LinearSolver bas...
double residual_norm(const unsigned &level)
Return norm of residual r=b-Ax and the residual vector itself on the level-th level.
static char t char * s
Definition: cfortran.h:572
void set_restriction_matrices_as_interpolation_transposes()
Builds a CRDoubleMatrix on each level that is used to restrict the residual between levels...
Vector< CRDoubleMatrix * > Mg_matrices_storage_pt
Vector to store the system matrices.
void position(Vector< double > &pos) const
Compute Vector of nodal positions either directly or via hanging node representation.
Definition: nodes.cc:2413
Vector< DoubleVector > Interpolation_self_test_vectors_storage
Vector to store the result of interpolation on each level (only required if the user wishes to docume...
void interpolate_and_correct(const unsigned &level)
Interpolate solution at current level onto next finer mesh and correct the solution x at that level...
void set_self_test_vector()
Makes a vector which will be used in the self-test. Is currently set to make the entries of the vecto...
void modify_restriction_matrices()
Normalise the rows of the restriction matrices to avoid amplifications when projecting to the coarser...
void enable_v_cycle_output()
Enable the output of the V-cycle timings and other output.
Vector< DoubleVector > Residual_mg_vectors_storage
Vector to store the residual vectors.
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...
Node *const & master_node_pt(const unsigned &i) const
Return a pointer to the i-th master node.
Definition: nodes.h:736
void post_smooth(const unsigned &level)
Post-smoother: Perform max_iter smoothing steps on the linear system Ax=b with current RHS vector...
Node *& node_pt(const unsigned &n)
Return a pointer to the local node n.
Definition: elements.h:2109
double timer()
returns the time in seconds after some point in past
bool Suppress_all_output
If this is set to true then all output from the solver is suppressed. This is protected member data s...
void clean_up_memory()
Clean up anything that needs to be cleaned up.
Class that contains data for hanging nodes.
Definition: nodes.h:684
An interface to allow scalar MG to be used as a Preconditioner.
Node *& node_pt(const unsigned long &n)
Return pointer to global node n.
Definition: mesh.h:456
unsigned Nlevel
The number of levels in the multigrid heirachy.
unsigned long nnode() const
Return number of nodes in the mesh.
Definition: mesh.h:590
void restriction_self_test()
Make a self-test to make sure that the interpolation matrices are doing the same thing to restrict th...
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...
Preconditioner base class. Gives an interface to call all other preconditioners through and stores th...
FiniteElement * finite_element_pt(const unsigned &e) const
Upcast (downcast?) to FiniteElement (needed to access FiniteElement member functions).
Definition: mesh.h:477
static const int OMEGA
Default value for an unassigned neighbour.
Definition: tree.h:241
virtual unsigned nnode_1d() const
Return the number of nodes along one edge of the element Default is to return zero — must be overloa...
Definition: elements.h:2151
void broken_assign(const std::string &class_name)
Issue error message and terminate execution.
unsigned nrow() const
access function to the number of global rows.
Vector< DoubleVector > Restriction_self_test_vectors_storage
Vector to store the result of restriction on each level (only required if the user wishes to document...
std::string string(const unsigned &i)
Return the i-th string or "" if the relevant string hasn&#39;t been defined.
MGPreconditioner(MGProblem *mg_problem_pt)
Constructor.
Vector< DoubleVector > Rhs_mg_vectors_storage
Vector to store the RHS vectors (Rhs_mg). This is protected to allow the multigrid preconditioner to ...
void solve(Problem *const &problem_pt, DoubleVector &result)
Virtual function in the base class that needs to be implemented later but for now just leave it empty...
unsigned & npost_smooth()
Return the number of post-smoothing iterations (lvalue)
~MGPreconditioner()
Destructor (empty)
unsigned long assign_eqn_numbers(const bool &assign_local_eqn_numbers=true)
Assign all equation numbers for problem: Deals with global data (= data that isn&#39;t attached to any el...
Definition: problem.cc:1966
virtual bool is_on_boundary() const
Test whether the Node lies on a boundary. The "bulk" Node cannot lie on a boundary, so return false. This will be overloaded by BoundaryNodes.
Definition: nodes.h:1290
Vector< CRDoubleMatrix * > Interpolation_matrices_storage_pt
Vector to store the interpolation matrices.
void self_test()
Makes a vector, restricts it down the levels of the hierarchy and documents it at each level...
void operator=(const MGPreconditioner &)
Broken assignment operator.
unsigned self_test()
Self-test: Check meshes and global data. Return 0 for OK.
Definition: problem.cc:13080
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
void clear()
clears the distribution
unsigned V_cycle_counter
Pointer to counter for V-cycles.
MGProblem class; subclass of Problem.
int * row_start()
Access to C-style row_start array.
Definition: matrices.h:1050
Vector< CRDoubleMatrix * > Restriction_matrices_storage_pt
Vector to store the restriction matrices.
unsigned nnode() const
Return the number of nodes.
Definition: elements.h:2146
void interpolation_self_test()
Make a self-test to make sure that the interpolation matrices are doing the same thing to interpolate...
A class for compressed row matrices. This is a distributable object.
Definition: matrices.h:872
void enable_output()
Enable the output from anything that could have been suppressed.
void disable_output()
Suppress anything that can be suppressed, i.e. any timings. Things like mesh adaptation can not howev...
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
unsigned & npre_smooth()
Return the number of pre-smoothing iterations (lvalue)
MGProblem()
Constructor. Initialise pointers to coarser and finer levels.
void setup_transfer_matrices()
Setup the transfer matrices on each level.
Base class for all linear iterative solvers. This merely defines standard interfaces for linear itera...
unsigned Npost_smooth
Number of post-smoothing steps.
void disable_v_cycle_output()
Disable all output from mg_solve apart from the number of V-cycles used to solve the problem...