Peano
Loading...
Searching...
No Matches
DGCGCoupling.py
Go to the documentation of this file.
1from peano4.solversteps.ActionSet import ActionSet
2
3import dastgen2
4import peano4
5import jinja2
6
7
9 cg_solver,
10 ):
11 """!
12
13 Prepare CG solver for coupling and ensure that both solvers are invoked in the correct order
14
15 Throughtout the mesh traversal steps from coarse to fine grid (descend), the
16 DG solver is to be executed before(!) the CG solver. It is the DG solver
17 which we equip with a preprocessing step, so we know that we are on the safe
18 side here.
19
20 We also introduce a new helper value for the CG solver: newValue has,
21 after each traversal, the injected value of the solution. We can then roll
22 it over if we aim for a FAS-type implementation. Further to that, we have
23 the solvers new rhs, which we again use for the next iteration, and its
24 previous (old) value which we use to compute the delta.
25
26 @see AbstractDGCGCoupling for a description of where the individual values are used.
27
28 """
29 assert cg_solver._unknowns_per_vertex_node==dg_solver._unknowns_per_cell_node
30 cg_solver._vertex_data.data.add_or_replace_attribute( peano4.dastgen2.Peano4DoubleArray( "sumOfInjectedValues", str(cg_solver._unknowns_per_vertex_node) ) )
31 cg_solver._vertex_data.data.add_or_replace_attribute( peano4.dastgen2.Peano4DoubleArray( "oldU", str(cg_solver._unknowns_per_vertex_node) ) )
32 cg_solver._vertex_data.data.add_or_replace_attribute( peano4.dastgen2.Peano4DoubleArray( "newRestrictedRhs", str(cg_solver._unknowns_per_vertex_node) ) )
33 cg_solver._vertex_data.data.add_or_replace_attribute( peano4.dastgen2.Peano4DoubleArray( "delta", str(cg_solver._unknowns_per_vertex_node) ) )
34 cg_solver._vertex_data.data.add_or_replace_attribute( dastgen2.attributes.Integer( "numberOfRestrictions" ) )
35
36
38 cg_solver._vertex_data.peano4_mpi_and_storage_aspect.merge_implementation += """\n setNewRestrictedRhs( getNewRestrictedRhs() + neighbour.getNewRestrictedRhs() ); \n"""
39
40
42 """!
43
44 Couple the solution and updates of two different solvers
45
46
47 ## Usage
48
49 This action set has to be used as a preprocessing step of the CG:
50
51 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
52 solver.preprocessing_action_set = mghype.matrixfree.solvers.api.actionsets.AbstractDGCGCoupling(
53 solver, # dg_solver
54 ...
55 )
56 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
57
58 I recommend not to use the coupling directly, but one of its subclasses.
59
60
61 ## Behaviour
62
63 We have a new variable SumOfInjectedValues. This one is set to zero at the end
64 of touchVertexFirstTime(), i.e. at the begin of a mesh sweep. In each fine
65 grid cell, i.e. each cell carrying a DG polynomial, we evaluate this
66 polynomial's value in the @f$ 2^d @f$ vertices (corners) of the cell. Of
67 these values, we restrict @f$ 2^{=-d} @f$th of its value to the next coarser
68 mesh level. Prior to the start of a new mesh sweep, SumOfInjectedValues holds
69 the average value of the solution in a vertex. This is just before we
70 clear it again, i.e. we can plug into touchVertexFirstTime() just before we
71 clear this field to read our a coarsened representation of the solution at
72 this point.
73
74
75
76 ## Realisation
77
78 This class takes the solution of a DG solver and injects the current solution
79 onto the vertices. For this, it simply averages the @f$ 2^d @f$ different
80 values of the DG polynomials into the vertex. We do this only for unrefined
81 vertices obviously.
82
83 To realise this behaviour, we rely on functions held within
84 AbstractDGCGCoupling.h.
85 It is important that prepare_and_order_DG_CG_solvers() has been invoked for
86 the two solvers whenever we use this action set.
87
88 """
89
90
91 __TemplateTouchVertexFirstTime="""
92 if (
93 fineGridVertex{{CG_SOLVER_NAME}}.getType() != vertexdata::{{CG_SOLVER_NAME}}::Type::Coarse
94 ) {
95
96 if ({{PREDICATE_INJECT_AND_RESTRICT}}) {
97 mghype::matrixfree::solvers::dgcgcoupling::rollOverAndPrepareCGVertex(
98 fineGridVertex{{CG_SOLVER_NAME}},
99 true, // restrictRhsAndInjectSolution
100 {{USE_FAS}}
101 );
102 }
103
104
105 /*
106 We aim to visit this section just before we prolongate. Passing false to
107 restrictRhsAndInjectSolution has the effect of setting delta to the "value"
108 parameter, ie we pick up the final solution to be interpolated during
109 touchCellFirstTime.
110 */
111 else if ( {{PREDICATE_PROLONGATE}} ) {
112 mghype::matrixfree::solvers::dgcgcoupling::rollOverAndPrepareCGVertex(
113 fineGridVertex{{CG_SOLVER_NAME}},
114 false, // restrictRhsAndInjectSolution
115 {{USE_FAS}}
116 );
117 }
118 }
119 """
120
121
122 templateTouchCellFirstTime="""
123 // ============================
124 // Interpolation
125 // ============================
126 const double additiveMultigridRelaxation = 0.0;
127 if (
128 fineGridCell{{DG_SOLVER_NAME}}.getType() != celldata::{{DG_SOLVER_NAME}}::Type::Coarse
129 and
130 {{PREDICATE_PROLONGATE}}
131 ) {
132 logTraceInWith2Arguments("Prolongate()", (bool)({{PREDICATE_PROLONGATE}}), fineGridCell{{DG_SOLVER_NAME}}.toString());
133 constexpr int Cols = TwoPowerD * {{CG_SOLVER_NAME}}::VertexUnknowns;
134 constexpr int Rows = {{DG_SOLVER_NAME}}::NodesPerCell * {{DG_SOLVER_NAME}}::UnknownsPerCellNode;
135
136 static std::vector< tarch::la::Matrix< Rows, Cols, double > > matrices = {
137 {% for MATRIX in PROLONGATION_MATRIX %}
138 {
139 {{MATRIX[0]| join(", ")}}
140 {% for ROW in MATRIX[1:] %}
141 ,{{ROW | join(", ")}}
142 {% endfor %}
143 },
144 {% endfor %}
145 };
146
147 {% if PROLONGATION_MATRIX_SCALING is not defined %}
148 #error No scaling for prolongation matrix available.
149 {% endif %}
150
151 static std::vector<int> scaleFactors = {
152 {% for el in PROLONGATION_MATRIX_SCALING %}
153 {{el}},
154 {% endfor %}
155 };
156
157 tarch::la::Matrix< Rows, Cols, double > composedProlongationMatrix = ::mghype::composeMatrixFromHWeightedLinearCombination( matrices, scaleFactors, marker.h() );
158
159 fineGridCell{{DG_SOLVER_NAME}}.setSolution(
160 mghype::matrixfree::solvers::dgcgcoupling::prolongate<{{DG_SOLVER_NAME}}, {{CG_SOLVER_NAME}}>(
161 composedProlongationMatrix,
162 fineGridCell{{DG_SOLVER_NAME}}.getSolution(),
163 fineGridVertices{{CG_SOLVER_NAME}}
164 )
165 );
166 logTraceOutWith1Argument("Prolongate()", fineGridCell{{DG_SOLVER_NAME}}.toString());
167 }
168"""
169
170
171
172 __TemplateTouchCellLastTime_FAS="""
173 if (
174 fineGridCell{{DG_SOLVER_NAME}}.getType() != celldata::{{DG_SOLVER_NAME}}::Type::Coarse
175 and
176 {{PREDICATE_EVALUATE_RHS}}
177 ) {
178 assertionEquals( {{CG_SOLVER_NAME}}::VertexUnknowns, {{DG_SOLVER_NAME}}::UnknownsPerCellNode );
179
180 // ==========================================
181 // compute and restrict hierarchical residual
182 // ==========================================
183 {
184 static std::vector< tarch::la::Matrix< {{DG_SOLVER_NAME}}::NodesPerCell * {{DG_SOLVER_NAME}}::UnknownsPerCellNode, TwoPowerD * {{CG_SOLVER_NAME}}::VertexUnknowns, double > > prolongationMatrices = {
185 {% for MATRIX in PROLONGATION_MATRIX %}
186 {
187 {{MATRIX[0]| join(", ")}}
188 {% for ROW in MATRIX[1:] %}
189 ,{{ROW | join(", ")}}
190 {% endfor %}
191 },
192 {% endfor %}
193 };
194
195 {% if PROLONGATION_MATRIX_SCALING is not defined %}
196 #error No scaling for injection matrix available.
197 {% endif %}
198
199 static std::vector<int> prolongationMatricesScaleFactors = {
200 {% for el in PROLONGATION_MATRIX_SCALING %}
201 {{el}},
202 {% endfor %}
203 };
204
205 tarch::la::Matrix< {{DG_SOLVER_NAME}}::NodesPerCell * {{DG_SOLVER_NAME}}::UnknownsPerCellNode, TwoPowerD * {{CG_SOLVER_NAME}}::VertexUnknowns, double > composedProlongationMatrix = ::mghype::composeMatrixFromHWeightedLinearCombination( prolongationMatrices, prolongationMatricesScaleFactors, marker.h() );
206
207 static std::vector< tarch::la::Matrix< TwoPowerD * {{CG_SOLVER_NAME}}::VertexUnknowns, {{DG_SOLVER_NAME}}::NodesPerCell * {{DG_SOLVER_NAME}}::UnknownsPerCellNode, double > > restrictionMatrices = {
208 {% for MATRIX in RESTRICTION_MATRIX %}
209 {
210 {{MATRIX[0]| join(", ")}}
211 {% for ROW in MATRIX[1:] %}
212 ,{{ROW | join(", ")}}
213 {% endfor %}
214 },
215 {% endfor %}
216 };
217
218 {% if RESTRICTION_MATRIX_SCALING is not defined %}
219 #error No scaling for injection matrix available.
220 {% endif %}
221
222 static std::vector<int> restrictionMatricesScaleFactors = {
223 {% for el in INJECTION_MATRIX_SCALING %}
224 {{el}},
225 {% endfor %}
226 };
227
228 tarch::la::Matrix< TwoPowerD * {{CG_SOLVER_NAME}}::VertexUnknowns, {{DG_SOLVER_NAME}}::NodesPerCell * {{DG_SOLVER_NAME}}::UnknownsPerCellNode, double > composedRestrictionMatrix = ::mghype::composeMatrixFromHWeightedLinearCombination( restrictionMatrices, restrictionMatricesScaleFactors, marker.h() );
229
230 mghype::matrixfree::solvers::dgcgcoupling::computeAndRestrictHierarchicalResidual<{{DG_SOLVER_NAME}}, {{CG_SOLVER_NAME}}>(
231 composedProlongationMatrix,
232 composedRestrictionMatrix,
233 repositories::{{DG_SOLVER_INSTANCE}}.getRhsMatrix( marker.x(), marker.h() ),
234 repositories::{{DG_SOLVER_INSTANCE}}.getLocalAssemblyMatrix(marker.x(), marker.h()),
235 fineGridCell{{DG_SOLVER_NAME}}.getSolution(),
236 fineGridCell{{DG_SOLVER_NAME}}.getRhs(),
237 fineGridVertices{{CG_SOLVER_NAME}}
238 );
239 }
240
241 // ============================
242 // inject solution back (accumulation)
243 // ============================
244 {
245 constexpr int Rows = TwoPowerD * {{CG_SOLVER_NAME}}::VertexUnknowns;
246 constexpr int Cols = {{DG_SOLVER_NAME}}::NodesPerCell * {{DG_SOLVER_NAME}}::UnknownsPerCellNode;
247
248 static std::vector< tarch::la::Matrix< Rows, Cols, double > > injectionMatrices = {
249 {% for MATRIX in INJECTION_MATRIX %}
250 {
251 {{MATRIX[0]| join(", ")}}
252 {% for ROW in MATRIX[1:] %}
253 ,{{ROW | join(", ")}}
254 {% endfor %}
255 },
256 {% endfor %}
257 };
258
259 {% if INJECTION_MATRIX_SCALING is not defined %}
260 #error No scaling for injection matrix available.
261 {% endif %}
262
263 static std::vector<int> injectionMatricesScaleFactors = {
264 {% for el in INJECTION_MATRIX_SCALING %}
265 {{el}},
266 {% endfor %}
267 };
268
269 tarch::la::Matrix< Rows, Cols, double > composedInjectionMatrix = ::mghype::composeMatrixFromHWeightedLinearCombination( injectionMatrices, injectionMatricesScaleFactors, marker.h() );
270
271 mghype::matrixfree::solvers::dgcgcoupling::injectSolution<{{DG_SOLVER_NAME}}, {{CG_SOLVER_NAME}}>(
272 composedInjectionMatrix,
273 fineGridCell{{DG_SOLVER_NAME}}.getSolution(),
274 fineGridVertices{{CG_SOLVER_NAME}}
275 );
276 }
277
278 // ==============
279 // Update counter
280 // ==============
281 mghype::matrixfree::solvers::dgcgcoupling::updateRestrictionCounters(
282 fineGridVertices{{CG_SOLVER_NAME}}
283 );
284 }
285"""
286
287
288 __TemplateTouchCellLastTime_CorrectionScheme="""
289 if (
290 fineGridCell{{DG_SOLVER_NAME}}.getType() != celldata::{{DG_SOLVER_NAME}}::Type::Coarse
291 and
292 {{PREDICATE_EVALUATE_RHS}}
293 ) {
294 logTraceIn("ComputeAndRestrictResidual");
295 assertionEquals( {{CG_SOLVER_NAME}}::VertexUnknowns, {{DG_SOLVER_NAME}}::UnknownsPerCellNode );
296
297 // =============================
298 // compute and restrict residual
299 // =============================
300 {
301 static std::vector< tarch::la::Matrix< TwoPowerD * {{CG_SOLVER_NAME}}::VertexUnknowns, {{DG_SOLVER_NAME}}::NodesPerCell * {{DG_SOLVER_NAME}}::UnknownsPerCellNode, double > > restrictionMatrices = {
302 {% for MATRIX in RESTRICTION_MATRIX %}
303 {
304 {{MATRIX[0]| join(", ")}}
305 {% for ROW in MATRIX[1:] %}
306 ,{{ROW | join(", ")}}
307 {% endfor %}
308 },
309 {% endfor %}
310 };
311
312 {% if RESTRICTION_MATRIX_SCALING is not defined %}
313 #error No scaling for injection matrix available.
314 {% endif %}
315
316 static std::vector<int> restrictionMatricesScaleFactors = {
317 {% for el in INJECTION_MATRIX_SCALING %}
318 {{el}},
319 {% endfor %}
320 };
321
322 tarch::la::Matrix< TwoPowerD * {{CG_SOLVER_NAME}}::VertexUnknowns, {{DG_SOLVER_NAME}}::NodesPerCell * {{DG_SOLVER_NAME}}::UnknownsPerCellNode, double > composedRestrictionMatrix = ::mghype::composeMatrixFromHWeightedLinearCombination( restrictionMatrices, restrictionMatricesScaleFactors, marker.h() );
323
324 // ============================
325 // update the solutions on all 4 adjacent faces so that we
326 // can correctly restrict the residual. by now, all
327 // projections have been set correctly, but we might
328 // redundantly set solutions...
329 // ============================
330
331 // vector to hold "updated" solutions
332 tarch::la::Vector< repositories::{{DG_SOLVER_INSTANCE}}.FaceUnknownsSolution*TwoTimesD, double > faceSolutions;
333
334 for (int f=0; f<TwoPowerD; f++) {
335 for (int s=0; s<repositories::{{DG_SOLVER_INSTANCE}}.FaceUnknownsSolution; s++)
336 faceSolutions( f*repositories::{{DG_SOLVER_INSTANCE}}.FaceUnknownsSolution + s ) = fineGridFaces{{DG_SOLVER_NAME}}(f).getSolution(s);
337 }
338
339 mghype::matrixfree::solvers::dgcgcoupling::computeAndRestrictResidual<{{DG_SOLVER_NAME}}, {{CG_SOLVER_NAME}}>(
340 composedRestrictionMatrix,
341 repositories::{{DG_SOLVER_INSTANCE}}.getRhsMatrix( marker.x(), marker.h() ),
342 repositories::{{DG_SOLVER_INSTANCE}}.getLocalAssemblyMatrix(marker.x(), marker.h()),
343 fineGridCell{{DG_SOLVER_NAME}}.getSolution(),
344 fineGridCell{{DG_SOLVER_NAME}}.getRhs(),
345 repositories::{{DG_SOLVER_INSTANCE}}.getCellFromFaceMatrix( marker.x(), marker.h() ) * faceSolutions,
346 fineGridVertices{{CG_SOLVER_NAME}}
347 );
348 }
349
350 // ============================
351 // inject solution back (accumulation)
352 // ============================
353 {
354 constexpr int Rows = TwoPowerD * {{CG_SOLVER_NAME}}::VertexUnknowns;
355 constexpr int Cols = {{DG_SOLVER_NAME}}::NodesPerCell * {{DG_SOLVER_NAME}}::UnknownsPerCellNode;
356
357 static std::vector< tarch::la::Matrix< Rows, Cols, double > > injectionMatrices = {
358 {% for MATRIX in INJECTION_MATRIX %}
359 {
360 {{MATRIX[0]| join(", ")}}
361 {% for ROW in MATRIX[1:] %}
362 ,{{ROW | join(", ")}}
363 {% endfor %}
364 },
365 {% endfor %}
366 };
367
368 {% if INJECTION_MATRIX_SCALING is not defined %}
369 #error No scaling for injection matrix available.
370 {% endif %}
371
372 static std::vector<int> injectionMatricesScaleFactors = {
373 {% for el in INJECTION_MATRIX_SCALING %}
374 {{el}},
375 {% endfor %}
376 };
377
378 tarch::la::Matrix< Rows, Cols, double > composedInjectionMatrix = ::mghype::composeMatrixFromHWeightedLinearCombination( injectionMatrices, injectionMatricesScaleFactors, marker.h() );
379
380 mghype::matrixfree::solvers::dgcgcoupling::injectSolution<{{DG_SOLVER_NAME}}, {{CG_SOLVER_NAME}}>(
381 composedInjectionMatrix,
382 fineGridCell{{DG_SOLVER_NAME}}.getSolution(),
383 fineGridVertices{{CG_SOLVER_NAME}}
384 );
385 }
386
387 // ==============
388 // Update counter
389 // ==============
390 mghype::matrixfree::solvers::dgcgcoupling::updateRestrictionCounters(
391 fineGridVertices{{CG_SOLVER_NAME}}
392 );
393 logTraceOut("ComputeAndRestrictResidual");
394 }
395"""
396
397 def __init__(self,
398 dg_solver,
399 cg_solver,
400 prolongation_matrix,
401 prolongation_matrix_scaling,
402 restriction_matrix,
403 restriction_matrix_scaling,
404 injection_matrix,
405 injection_matrix_scaling,
406 use_fas
407 ):
408 r"""!
409
410 Construct action set
411
412 @param prolongation_matrix Please consult the action set ComputeAndRestrictBiasedHierarchicalResidual.
413
414 @param prolongation_matrix_scaling Please consult the action set ComputeAndRestrictBiasedHierarchicalResidual.
415
416 @param injection_matrix: [ @f$ \mathbb{R}^{2^dK \times (p+1}^dK } @f$ ]
417 Sequence of matrices. Each takes the solution over the cell spanned by
418 @f$ (p+1}^dK @f$ weights and maps them onto @f$ K @f$ quantities on the
419 vertices. These quantities are typically just the value of the polynomial
420 in the vertex, but you can pick other schemes, too. Most of the time, it
421 will be a single injection (with one scaling, see below). To be in line
422 with the other signatures, you can however specify the total injection through a
423 sequence of matrices, each one with its own @f$ h^s @f$ scaling.
424
425 @param injection_matrix_scaling: [ Integer ]
426 Specifies a sequence of values @f$ s @f$. They define with which
427 @f$ h^s @f$ the corresponding matrices in injection_matrix are to be
428 scaled. That is, if you pass in a [3,0], then the first matrix in
429 injection_matrix will be scaled with @f$ h^3 @f$ and the second one
430 with one. If you work with a simple function evaluation as injection,
431 then you are fine with one matrix, and its scaline will be [ 0 ].
432
433 @param use_fas: Boolean
434 If this flag is set, we realise a FAS following the HTMG idea by
435 Griebel et al.
436
437 """
438 super( AbstractDGCGCoupling, self ).__init__(
439 0, #descend_invocation_order -> will be ignored
440 False # parallel
441 )
442
444 cg_solver,
445 )
446
447 dg_solver._basic_descend_order = cg_solver._basic_descend_order - 1
448
449 self.d = {}
450 self.d["DG_SOLVER_INSTANCE"] = dg_solver.instance_name()
451 self.d["DG_SOLVER_NAME"] = dg_solver.typename()
452 self.d["CG_SOLVER_INSTANCE"] = cg_solver.instance_name()
453 self.d["CG_SOLVER_NAME"] = cg_solver.typename()
454
455 self.d["INJECTION_MATRIX"] = injection_matrix
456 self.d["INJECTION_MATRIX_SCALING"] = injection_matrix_scaling
457 self.d["PROLONGATION_MATRIX"] = prolongation_matrix
458 self.d["PROLONGATION_MATRIX_SCALING"] = prolongation_matrix_scaling
459 self.d["RESTRICTION_MATRIX"] = restriction_matrix
460 self.d["RESTRICTION_MATRIX_SCALING"] = restriction_matrix_scaling
461
462 self.d["PREDICATE_INJECT_AND_RESTRICT"] = "true"
463 self.d["PREDICATE_PROLONGATE"] = "true"
464 self.d["PREDICATE_EVALUATE_RHS"] = "true"
465
466 if use_fas:
467 self.d["USE_FAS"] = "true"
468 self._use_fas = use_fas
469 else:
470 self.d["USE_FAS"] = "false"
471 self._use_fas = use_fas
472
473
474 def get_body_of_operation(self,operation_name):
475 result = ""
476 if operation_name==peano4.solversteps.ActionSet.OPERATION_TOUCH_VERTEX_FIRST_TIME:
477 result = jinja2.Template(self.__TemplateTouchVertexFirstTime).render(**self.d)
478 pass
479 if operation_name==peano4.solversteps.ActionSet.OPERATION_TOUCH_CELL_FIRST_TIME:
480 result = jinja2.Template(self.templateTouchCellFirstTime).render(**self.d)
481 pass
482 if self._use_fas and operation_name==peano4.solversteps.ActionSet.OPERATION_TOUCH_CELL_LAST_TIME:
483 result = jinja2.Template(self.__TemplateTouchCellLastTime_FAS).render(**self.d)
484 pass
485 if not self._use_fas and operation_name==peano4.solversteps.ActionSet.OPERATION_TOUCH_CELL_LAST_TIME:
486 result = jinja2.Template(self.__TemplateTouchCellLastTime_CorrectionScheme).render(**self.d)
487 pass
488 return result
489
491 """!
492
493 Configure name of generated C++ action set
494
495 This action set will end up in the directory observers with a name that
496 reflects how the observer (initialisation) is mapped onto this action
497 set. The name pattern is ObserverName2ActionSetIdentifier where this
498 routine co-determines the ActionSetIdentifier. We make is reflect the
499 Python class name.
500
501 """
502 return __name__.replace(".py", "").replace(".", "_")
503
505 """!
506
507 The action set that Peano will generate that corresponds to this class
508 should not be modified by users and can safely be overwritten every time
509 we run the Python toolkit.
510
511 """
512 return False
513
514 def get_includes(self):
515 """!
516
517 We need the solver repository in this action set, as we directly access
518 the solver object. We also need access to Peano's d-dimensional loops.
519
520 """
521 return """
522#include "../repositories/SolverRepository.h"
523#include "peano4/utils/Loop.h"
524#include "mghype/mghype.h"
525#include "mghype/matrixfree/solvers/DGCGCoupling.h"
526"""
527
528
530 """!
531
532 Introduce additive CC Coupling
533
534 The control logic here is simplistic and prescripted. We follow the
535 following steps for the additive solver with one iteration:
536
537 1. Pre step
538 - Interpolate correction, i.e. sum all the hierarchies up.
539 This happens in touchCellFirstTime().
540 - Compute and restrict residual and inject solution (if FAS).
541 This happens in touchCellLastTime().
542 - Suspend both solvers, but let DG solver project its solution onto the
543 faces.
544 2. Compute step
545 - All solvers compute.
546 - DG solver can project solution onto faces.
547 - Return to step 1.
548
549 Nothing stops us from repeating the step (2) multiple times, which gives
550 us the opportunity to play around with various multigrid schemes.
551 If we are in the last sweep of (2), the projection onto the faces of the
552 DG solver is not necessary. We'll overwrite this solution a minute later
553 anyway.
554
555 ## Realisation
556
557 This action set will map onto a class of its own. We give the class a
558 class attribute which we increment by one in each step. We then make the
559 actual logic depend upon this counter, as we inject the predicates into
560 the superclass: That is, the superclass has some boolean expressions which
561 allow us to switch its features on and off. We make those guys depends upon
562 the new counter.
563
564 """
565 def __init__(self,
566 dg_solver,
567 cg_solver,
568 prolongation_matrix,
569 prolongation_matrix_scaling,
570 restriction_matrix,
571 restriction_matrix_scaling,
572 injection_matrix,
573 injection_matrix_scaling,
574 use_fas,
575 smoothing_steps_per_cycle = 1
576 ):
577 super( AdditiveDGCGCoupling, self ).__init__(
578 dg_solver,
579 cg_solver,
580 prolongation_matrix,
581 prolongation_matrix_scaling,
582 restriction_matrix,
583 restriction_matrix_scaling,
584 injection_matrix,
585 injection_matrix_scaling,
586 use_fas
587 )
588 self.dd["PREDICATE_INJECT_AND_RESTRICT"] = "CycleCounter==0"
589 self.dd["PREDICATE_PROLONGATE"] = "CycleCounter==0"
590 self.dd["PREDICATE_EVALUATE_RHS"] = "CycleCounter==0"
591 self.dd["SMOOTHING_STEPS_PER_CYCLE"] = smoothing_steps_per_cycle
592
593 assert smoothing_steps_per_cycle>=1, "at least one smoothing step required"
594
595
596 def get_attributes(self):
597 """!
598
599 Add a new static attribute to the class.
600
601 """
602 return """
603 static int CycleCounter;
604"""
605
606
607 def get_static_initialisations(self, full_qualified_classname):
608 """!
609
610 Initialise the new counter.
611
612 """
613 return "int " + full_qualified_classname + "::CycleCounter = -1;"
614
615
617 return jinja2.Template( """
618 CycleCounter++;
619
620 if (CycleCounter==0) {
621 logInfo( "prepareTraversal(...)", "pre-step: prolong updates and restrict (new) residual, but do not yet run any solver" );
622 repositories::instanceOf{{CG_SOLVER_NAME}}.suspend();
623 repositories::instanceOf{{DG_SOLVER_NAME}}.suspend(true);
624 }
625 else if (CycleCounter=={{SMOOTHING_STEPS_PER_CYCLE}}) {
626 logInfo( "prepareTraversal(...)", "last smoothing step" );
627 CycleCounter = -1;
628 }
629 else {
630 logInfo( "prepareTraversal(...)", "normal smoothing step" );
631 }
632""").render( **self.dd )
633
634
635
637 """!
638
639 Introduce multiplicative CC Coupling
640
641 The control logic here is simplistic and prescripted. We follow the
642 following steps for the additive solver with one iteration:
643
644 1. Pre First smoothing step
645 - When we touchVertexFirstTime, we pass over rollOverAndPrepareCGVertex,
646 which has the effect of taking whatever is in the CG solution
647 and writing it into the Delta. This is then interpolated during
648 touchCellFirstTime.
649 - Interpolate correction, i.e. sum all the hierarchies up.
650 This happens in touchCellFirstTime().
651 - Suspend CG solver
652 - Suspend DG solver
653 - Let DG solver project data onto faces
654 2. DG smoothing step
655 - Suspend CG solver
656 - Run DG solver
657 3. Nth DG smoothing step
658 - Still run DG solver (finish smoothing)
659 During touchCellLastTime, we:
660 - Compute hierarchical residual
661 - Restrict
662 - Suspend CG solver
663 These steps will set newRestrictedRhs correctly
664 4. Any CG solver
665 On the first CG smoothing step, we should, during
666 touchVertexFirstTime, pass over rollOverAndPrepareCGVertex
667 which takes newRestrictedRhs and writes it into rhs, and
668 we reset value to 0.
669 - Suspend DG solver
670
671 ## Realisation
672
673 This action set will map onto a class of its own. We give the class a
674 class attribute which we increment by one in each step. We then make the
675 actual logic depend upon this counter, as we inject the predicates into
676 the superclass: That is, the superclass has some boolean expressions which
677 allow us to switch its features on and off. We make those guys depends upon
678 the new counter.
679
680 """
681 def __init__(self,
682 dg_solver,
683 cg_solver,
684 prolongation_matrix,
685 prolongation_matrix_scaling,
686 restriction_matrix,
687 restriction_matrix_scaling,
688 injection_matrix,
689 injection_matrix_scaling,
690 use_fas,
691 smoothing_steps_DG = 4,
692 smoothing_steps_CG = -1,
693 vcycles=1, #toto: move vcycles to the solver input
694 ):
695 """!
696
697 Construct the solver
698
699 @param smoothing_steps_DG: positive integer
700
701 @param smoothing_steps_CG: positive integer or -1
702 The -1 indicates that we wait for full convergence on the coarse
703 mesh.
704
705 @param vcycles: positive integer or -1.
706 Terminate after the given number of full V-cycles.
707 The -1 indicates that we wait for full convergence on the coarse mesh.
708 If a positive integer is set, the correction solver tolerance needs to be set to a small value
709 to ensure the given number of cycles is completed before the tolerance is reached.
710 @todo: Implement ignoring the tolerance when the number of cycles is set as a stopping criterion.
711
712 """
713 super( MultiplicativeDGCGCoupling, self ).__init__(
714 dg_solver,
715 cg_solver,
716 prolongation_matrix,
717 prolongation_matrix_scaling,
718 restriction_matrix,
719 restriction_matrix_scaling,
720 injection_matrix,
721 injection_matrix_scaling,
722 use_fas
723
724 )
725
726 # f-string isn't happy unless i do it like this
727 cg_solver_name = self.dd["CG_SOLVER_NAME"]
728 dg_solver_name = self.dd["DG_SOLVER_NAME"]
729
730 # We have finished DG smoothing, and are on first stage of CG Smoothing. Everything important
731 # happens during touchVertexFirstTime, so we can combine. In particular, touchVertexFirstTime
732 # on the DGCGCoupling action set is where we bring the restrictedRhs to the acutal rhs, ie
733 # prepare us to solve the CG problem again.
734 # We incremented CGCycleCounter after we disabled the DG solver, hence the comparison with 1.
735 self.dd["PREDICATE_INJECT_AND_RESTRICT"] = f"DGCycleCounter>DGSmoothingSteps and CGCycleCounter==1"
736
737 # We've just reset everything, so it's time to interpolate.
738 self.dd["PREDICATE_PROLONGATE"] = f"DGCycleCounter==1 and CGCycleCounter==0"
739
740 # final DG stage. This is where we compute the residual for the CG vertices.
741 # we compare with > because the final time we enter prepareTraversal, DGCycleCounter==DGSmoothingSteps.
742 # Then we increment and continue on the DG solver anyway. Here we compute and restrict the residual
743 # during touchCellLastTime.
744 self.dd["PREDICATE_EVALUATE_RHS"] = f"DGCycleCounter>DGSmoothingSteps and CGCycleCounter==0"
745
746 assert smoothing_steps_DG>=1, "at least one smoothing step required"
747 assert smoothing_steps_CG>=1 or smoothing_steps_CG==-1, "at least one smoothing step required"
748
749 # Logically speaking, the only thing we do on the "final" sweep is send the face solutions down to
750 # the coupler to calculate the residual, which we restrict. So we add on another sweep here so that
751 # we perform the exact number of sweeps that the caller asked for.
752 self.smoothing_steps_DG = smoothing_steps_DG + 1
753 self.smoothing_steps_CG = smoothing_steps_CG
754 self.vcycles = vcycles
755
756 self.cg_solver = cg_solver
757
758
759 def get_attributes(self):
760 """!
761
762 Add a new static attribute to the class.
763
764 """
765 if self.vcycles==-1:
766 vcycles_string = "std::numeric_limits<int>::max()"
767 else:
768 vcycles_string = self.vcycles
769
770 if self.smoothing_steps_CG==-1:
771 smoothing_steps_CG_string = "std::numeric_limits<int>::max()"
772 else:
773 smoothing_steps_CG_string = self.smoothing_steps_CG
774
775 CG_solver_tolerance = self.cg_solver._solver_tolerance
776
777 return """
778 static constexpr int DGSmoothingSteps = {};
779 static constexpr int CGSmoothingSteps = {};
780 static constexpr double CGSolverTolerance = {};
781 static constexpr int VCycles = {};
782 static int DGCycleCounter;
783 static int CGCycleCounter;
784 static double initialGlobalResidualMaxNorm;
785""".format(self.smoothing_steps_DG, smoothing_steps_CG_string, CG_solver_tolerance, vcycles_string)
786
787
788 def get_static_initialisations(self, full_qualified_classname):
789 """!
790
791 Initialise the new counter.
792
793 """
794 return """
795int """ + full_qualified_classname + """::DGCycleCounter = 0;
796int """ + full_qualified_classname + """::CGCycleCounter = 0;
797double """ + full_qualified_classname + """::initialGlobalResidualMaxNorm = 0;
798"""
799
801 return jinja2.Template( """
802 logTraceInWith3Arguments("PrepareTraversal", DGCycleCounter, CGCycleCounter, repositories::instanceOf{{CG_SOLVER_NAME}}.getGlobalResidualMaxNorm());
803 if (DGCycleCounter>0 and DGCycleCounter<DGSmoothingSteps) {
804 repositories::instanceOf{{CG_SOLVER_NAME}}.suspend();
805 DGCycleCounter++;
806 logInfo( "prepareTraversal(...)", "run DG smoother step, disable CG solver" );
807 }
808 else if (
809 DGCycleCounter>0 and DGCycleCounter==DGSmoothingSteps
810 ) {
811 // same as above, but we need to make sure final update residual is halted
812 logInfo( "prepareTraversal(...)", "run final DG smoother step, disable CG solver" );
813 DGCycleCounter++;
814 repositories::instanceOf{{CG_SOLVER_NAME}}.suspend();
815 repositories::instanceOf{{DG_SOLVER_NAME}}.suspend(false, true);
816 }
817
818 else if (
819 DGCycleCounter>DGSmoothingSteps
820 and
821 repositories::instanceOf{{CG_SOLVER_NAME}}.getVCycleCounter()<VCycles
822 and not
823 (
824 repositories::instanceOf{{CG_SOLVER_NAME}}.getGlobalResidualMaxNorm() > 0.0
825 and
826 repositories::instanceOf{{CG_SOLVER_NAME}}.getGlobalResidualMaxNorm() <= std::max(1E-14,initialGlobalResidualMaxNorm*CGSolverTolerance)
827 )
828 ) {
829 /*
830 We haven't reached our termination condition for CG solver yet
831 */
832 if (CGCycleCounter==1) {
833 initialGlobalResidualMaxNorm = repositories::instanceOf{{CG_SOLVER_NAME}}.getGlobalResidualMaxNorm();
834 }
835 repositories::instanceOf{{DG_SOLVER_NAME}}.suspend(false, false);
836 CGCycleCounter++;
837 logInfo( "prepareTraversal(...)", "run CG smoother step, disable DG solver. CG residual="<< repositories::instanceOf{{CG_SOLVER_NAME}}.getGlobalResidualMaxNorm());
838 }
839 else {
840 DGCycleCounter = 1;
841 CGCycleCounter = 0;
842 repositories::instanceOf{{DG_SOLVER_NAME}}.suspend(true, false);
843 repositories::instanceOf{{CG_SOLVER_NAME}}.suspend();
844 logInfo( "prepareTraversal(...)", "suspend all solvers, interpolate CG->DG and project new solution onto faces" );
845 }
846 logTraceOutWith5Arguments("PrepareTraversal", DGCycleCounter, CGCycleCounter, repositories::instanceOf{{DG_SOLVER_NAME}}.isSuspended(), repositories::instanceOf{{DG_SOLVER_NAME}}.projectOntoFaces(), repositories::instanceOf{{CG_SOLVER_NAME}}.isSuspended());
847""").render( **self.dd )
Specialisation of array using Peano's tarch.
Action set (reactions to events)
Definition ActionSet.py:6
Couple the solution and updates of two different solvers.
user_should_modify_template(self)
The action set that Peano will generate that corresponds to this class should not be modified by user...
get_includes(self)
We need the solver repository in this action set, as we directly access the solver object.
__init__(self, dg_solver, cg_solver, prolongation_matrix, prolongation_matrix_scaling, restriction_matrix, restriction_matrix_scaling, injection_matrix, injection_matrix_scaling, use_fas)
Construct action set.
get_body_of_operation(self, operation_name)
Return actual C++ code snippets to be inserted into C++ code.
get_action_set_name(self)
Configure name of generated C++ action set.
get_attributes(self)
Add a new static attribute to the class.
get_static_initialisations(self, full_qualified_classname)
Initialise the new counter.
__init__(self, dg_solver, cg_solver, prolongation_matrix, prolongation_matrix_scaling, restriction_matrix, restriction_matrix_scaling, injection_matrix, injection_matrix_scaling, use_fas, smoothing_steps_per_cycle=1)
Construct action set.
get_static_initialisations(self, full_qualified_classname)
Initialise the new counter.
__init__(self, dg_solver, cg_solver, prolongation_matrix, prolongation_matrix_scaling, restriction_matrix, restriction_matrix_scaling, injection_matrix, injection_matrix_scaling, use_fas, smoothing_steps_DG=4, smoothing_steps_CG=-1, vcycles=1)
Construct the solver.
get_attributes(self)
Add a new static attribute to the class.
prepare_CG_correction_solvers(dg_solver, cg_solver)
Prepare CG solver for coupling and ensure that both solvers are invoked in the correct order.