8from .Solver
import Solver
9from .actionsets.CollocatedMGSolver.InitDofs
import InitDofsCollocatedMG
10from .actionsets.CollocatedMGSolver.ResetAndUpdateResidual
import ResetAndUpdateResidual
11from .actionsets.CollocatedMGSolver.UpdateSolution
import UpdateSolution
12from .actionsets.CollocatedMGSolver.Restriction
import Restriction
13from .actionsets.CollocatedMGSolver.Prolongation
import Prolongation
14from .actionsets.PlotVertexDataInPeanoBlockFormat
import (
15 PlotVertexDataInPeanoBlockFormat,
18from abc
import abstractmethod
25 A full mathematical description will follow later, but for now we outline some algorithmic steps here.
27 We borrow heavily from the original Collocated Solver. See \ref matrixfree_collocated_solver.
29 We intialise by setting the values for u and the RHS in implementation files. So far this has been tested
30 with the standard poisson set up ( \f$ 8\pi^2 sin(x)sin(y) \f$ on the rhs, with zeroes in the initial guess).
31 We do this only on the finest level, and every other level receives 0.
33 We enumerate each level according to its coarseness. Ie the top level is marked as 0, and we increase this number
36 ## Algorithmic Blueprint
38 1. On the finest level:
39 - Solve with Jacobi for a pre-determined number of pre-smoothing steps. Use standard mass matrix
40 - On the final sweep, we take the final residual and write this into the rhs of the vertex and one level above. We set its initial guess to 0.
41 - We then move up one level coarser
43 2. On the next level up:
44 - Solve with Jacobi for a pre-determined number of pre-smoothing steps. Use identity instead of mass matrix
45 - On the final sweep, we take the final residual and write this into the rhs of the vertex and one level above. We set its initial guess to 0.
46 - We then move up one level coarser
48 3. We continue until we are on the coarsest level. We then solve with Jacobi for the predetermined number of pre-smoothing steps.
50 4. We then solve on the coarsest level with the predetermined number of post-smoothing steps.
51 - After this, we move one level finer
53 5. On the next level down:
54 - When we touch a vertex for the first time, we add prolongation of the solution from the level above to our solution.
55 - We then solve with the predetermined number of post-smoothing steps
56 - After this, we move one level finer
58 6. We continue until we reach the finest level, at which point we add prolongation from the level above, and run for predetermined number of post-smoothing steps. We then go back to step 1.
60 Every time we run a sweep on the finest level, we update the global residuals. We terminate when the solution on the
61 finest level conforms to the given stopping criterion.
65 We have 4 fundamental action sets, which we discuss here in order of invocation. Each of their operations is guarded by a bool function that lives in the abstract solver class.
69 \see mghype.matrixfree.solvers.api.actionsets.CollocatedMGSolver.UpdateSolution
71 This is where we take the residual and diag, and set the solution on vertices to \f$ u + du = u + \omega / (diag * residual) \f$. We want to avoid
72 zero-divsion, as reset the diagonal every sweep, meaning we need to wait until the second pre/post-smoothing sweep to update the solution. We also
73 need to make sure we finalise the solution updates before we restrict/prolongate as well. updateSolution() in the abstract solver class handles all
76 To make sure the solver works in multiple trees, we tell the vertices that lie on parallel domain boundaries to share residual and diagonal with
77 each other, to make sure contributions from neighbouring cells are registered properly. See get_vertex_merge_method in this class.
79 This action set also makes sure that the RHS is correctly consolidated - we write our restrictions into the vertex field NewRhs. When we touch
80 a vertex for the first time after restriction, we make sure that the rhs is set to the value of what we restricted (ie the newrhs).
82 Ditto for delta after prolongation.
85 \see mghype.matrixfree.solvers.api.actionsets.CollocatedMGSolver.Prolongation
87 When we touch a vertex on the coarse level, we set the delta field to be \f$ U - OldU \f$. When we touch a vertex on the fine level, we prolongate
88 the coarse delta into the fine level delta. On the next sweep, we update the fine solution to be \f$ u + delta \f$.
90 The abstract solver just needs to check if its smoothing state is set to prolongation, so the logic is much simpler.
92 ### ResetAndUpdateResidual
94 \see mghype.matrixfree.solvers.api.actionsets.CollocatedMGSolver.ResetAndUpdateResidual
96 When we touch the vertex for the first time, we reset residual and diag to 0, or apply boundary conditions.
97 When we touch the cell for the first time, we perform the jacobi iteration and write the residual into the \f$ 2^D \f$ adjacent vertices.
99 The abstract solver class does this if updateResidual() returns true, which it does when:
100 - The refinement level matches the active level
101 - The smoothing state is presmoothing or postsmoothing
105 \see mghype.matrixfree.solvers.api.actionsets.CollocatedMGSolver.Restriction
107 When we touch a coarse vertex for the first time, we reset everything to 0, i.e. rhs and solution.
108 When we touch a fine vertex for the first time, we hand over to the mghype::matrixfree::solvers::cgmultigrid::restrictToNextLevel
109 which writes the fine level residual into the newRhs of the level above.
113 You can now specify the number of levels that you want to use in the solver itself. The finest level is set by the refinement of
114 the mesh and is currently determined at runtime when the mesh is descended for the first time. If we find that the finest level
115 isn't at least one larger than the coarest level, we throw a std::runtime_error.
126 local_assembly_matrix,
127 local_assembly_matrix_scaling,
131 correction_matrix_scaling,
134 post_smoothing_steps,
139 smoother_relaxation=0.6,
140 plot_predicate="true",
142 super(CollocatedMG, self).
__init__(name, min_h=min_h, max_h=max_h)
144 assert pre_smoothing_steps >= 1
145 assert post_smoothing_steps >= 1
146 assert coarsest_level >= 0
184 self.
_vertex_data.peano4_mpi_and_storage_aspect.merge_implementation = (
187 self.
_vertex_data.peano4_mpi_and_storage_aspect.implementation_file_includes += (
188 """\n#include "repositories/SolverRepository.h"\n"""
217 Solution initialisation
227 observer.add_action_set(
228 UpdateSolution(self, descend_invocation_order=invocation_order + 0)
230 observer.add_action_set(
231 Prolongation(self, descend_invocation_order=invocation_order + 1)
233 observer.add_action_set(
236 observer.add_action_set(
237 Restriction(self, descend_invocation_order=invocation_order + 3)
242 cell_predicate = f
"""fineGridCell{self.typename()}.getLevel() == repositories::{self.instance_name()}.getFinestLevel() """
243 observer.add_action_set(
249 cell_predicate=cell_predicate,
252 observer.add_action_set(
258 cell_predicate=cell_predicate,
266 The solver creates two classes: An abstract base class and its
267 implementations. The abstract base class will be overwritten if
268 there is one in the directory. I pipe all the Python constants
269 into this base class, so they are available in the C++ code.
272 templatefile_prefix = (
273 os.path.dirname(os.path.realpath(__file__)) +
"/templates/"
275 templatefile_prefix += self.__class__.__name__
281 "SOLVER_INCLUDES":
"",
301 generated_abstract_header_file = (
303 templatefile_prefix +
".Abstract.template.h",
304 templatefile_prefix +
".Abstract.template.cpp",
313 generated_solver_files = (
315 templatefile_prefix +
".template.h",
316 templatefile_prefix +
".template.cpp",
326 output.add(generated_abstract_header_file)
327 output.add(generated_solver_files)
328 output.makefile.add_h_file(
329 subdirectory +
"Abstract" + self.
_name +
".h", generated=
True
331 output.makefile.add_h_file(subdirectory + self.
_name +
".h", generated=
True)
332 output.makefile.add_cpp_file(
333 subdirectory +
"Abstract" + self.
_name +
".cpp", generated=
True
335 output.makefile.add_cpp_file(subdirectory + self.
_name +
".cpp", generated=
True)
343 // we can get away with only doing these on the active level
345 repositories::{{SOLVER_INSTANCE}}.consolidateResidualAndDiag( getLevel() )
347 // naive += with the diag
348 setDiag( getDiag() + neighbour.getDiag() );
350 // ditto for residual
351 setResidual( getResidual() + neighbour.getResidual() );
356 return jinja2.Template(output).render(**d)
Specialisation of array using Peano's tarch.
Action set (reactions to events)
A full mathematical description will follow later, but for now we outline some algorithmic steps here...
add_to_init_solution(self, observer)
Solution initialisation.
__init__(self, name, unknowns_per_vertex, dimensions, min_h, max_h, local_assembly_matrix, local_assembly_matrix_scaling, mass_matrix, mass_matrix_scaling, correction_matrix, correction_matrix_scaling, solver_tolerance, pre_smoothing_steps, post_smoothing_steps, descend_order, restriction_matrix, prolongation_matrix, coarsest_level=0, smoother_relaxation=0.6, plot_predicate="true")
add_to_Peano4_datamodel(self, datamodel, verbose)
_unknowns_per_vertex_node
add_to_plot(self, observer)
_correction_matrix_scaling
add_to_create_grid(self, observer)
add_implementation_files_to_project(self, namespace, output, subdirectory="")
The solver creates two classes: An abstract base class and its implementations.
get_vertex_merge_method(self)
_local_assembly_matrix_scaling
add_to_solve(self, observer)
add_use_statements(self, observer)
This routine should still be called even if overwritten in child class.
Abstract base class for matrix free solvers.
instance_name(self)
Return the name of the object that will be created for this solver.
This also should be called before the solver stuff.
This class comes last in the pecking order so that we restrict the residual after we've finished comp...
We use this action set to apply the residual and solution updates to the vertices.