Peano
Loading...
Searching...
No Matches
DiscontinuousGalerkinDiscretisationPointWiseRiemannSolver.py
Go to the documentation of this file.
1"""
2 This file is part of the MGHyPE project within Peano 4. For conditions of distribution and
3 use, please see the copyright notice at www.peano-framework.org
4"""
5import peano4.api
7import dastgen2
8import os
9import jinja2
10
11from .Solver import Solver
12
13from .actionsets.InitHigherOrderDofs import InitHigherOrderDofs
14from .actionsets.DGSolver.UpdateResidual import UpdateResidual
15from .actionsets.DGSolver.ProjectOntoFaces import ProjectOntoFaces
16from .actionsets.DGSolver.UpdateFaceSolution import UpdateFaceSolution
17from .actionsets.DGSolver.ProjectIntoCellAndUpdateCellSolution import ProjectIntoCellAndUpdateCellSolution
18from .actionsets.PlotDGDataInPeanoBlockFormat import PlotDGDataInPeanoBlockFormat
19
20
22 r"""!
23
24 Most efficient solver we have for this type of problem class
25
26 You can find each of the action sets here:
27 solvers.api.actionsets.DGSolver.InitHigherOrderDofs.InitHigherOrderDofs
28
29 The text below specifies the semantics and behaviour of this matrix-free
30 DG solver. A tutorial running step by step through the steps how to use
31 it can be found @ref tutorials_multigrid_matrixfree_discontinuous_galerkin_poisson "in the tutorials section".
32 The solver in general tackles a PDE
33
34 @f$ \mathcal{L}(u) = f @f$
35
36 resulting in a linear equation system
37
38 @f$ Au = b. @f$
39
40 Throughout the discussion, we @ref page_multigrid_unfolding "unfold the compute steps over the compute mesh".
41
42 ## Storage
43
44 This solver holds \f$ K^c (p+1)^d \f$ unknowns (doubles) per cell for the
45 solution, \f$ K^c (p+1)^d \f$ per cell for the right-hand side, and the
46 same number for the residual. Holding the residual in the cell allows us
47 to separate some algorithm steps into different action sets. In particular,
48 we project the face solution back into the cells after updating the cell
49 solution (in separate sweeps).
50 These are the primary variables, i.e. those directly resulting from the
51 PDE formulation. The correspond to the unknowns directly from @f$ u @f$
52 and the right-hand side.
53
54 Further to that, we hold auxiliary variables on the faces:
55 Our Riemann solver accepts the left and the right solution. Here, each
56 degree of freedom has @f$ K^f_{f \gets c} @f$ unknowns. It will return a result
57 (the flux), which has @f$ K^f @f$ unknowns.
58 Per face node, we hence hold, in total, \f$ ( K^f + 2K^f_{f \gets c} ) (p+1)^{d-1} \f$ unknowns.
59 That is the solution for each unknown per face node, as
60 well as a projection from the left and right for each unknown per cell.
61 \f$ K^c \f$ and \f$ K^f \f$ can be different, but often they are the same.
62 The user can choose how many of the \f$ K^c \f$ to project onto the face
63 by modifying \f$ K^f_{f \gets c} \f$.
64
65 ## Operators
66
67 With the auxiliary variables, in place, we can write down the DG
68 problem as
69
70 \f$
71 \left(
72 \begin{array}{cccc}
73 A^{cc} & & & P_{c \gets f} \\
74 P_{f \gets c} & id & & \\
75 P_{f \gets c} & & id \\
76 & id & id & A^{ff}
77 \end{array}
78 \right)
79 \left(
80 \begin{array}{c}
81 u^c \\
82 u^+ \\
83 u^- \\
84 q
85 \end{array}
86 \right)
87 =
88 \left(
89 \begin{array}{c}
90 M b \\
91 0 \\
92 0 \\
93 0
94 \end{array}
95 \right)
96 \f$
97
98 in line with @ref xxxx. The @f$ q @f$ is the result of the Riemann solver.
99
100 ## Solver
101
102 We now realise a Block-Jacobi smoother subject to two relaxation parameters
103 \f$ \omega _c, \omega _f \in [0,1] \f$. We tackle the first line in the
104 block system with one smoothing step of a stationary iterative scheme, solve two and three exact (as they are
105 trivial with the identity on the diagonal) and apply Jacobi to the last
106 row again. As the first and last block are updated in parallel by the
107 Jacobi scheme, the resuling algorithm is globally a Jacobi relaxation, too.
108
109 - Per cell, we tackle the equation
110 \f$
111 A^{cc} u^c + P_{c \gets f} q = M b
112 \f$
113 which yields the update rule
114 \f{eqnarray*}{
115 u|_c & \gets & u|_c + \omega _c \tilde {A}^{-1}\left( Mb - A^{cc} u^c - P_{c \gets f} q \right).
116 \f}
117 Here, the \f$ q \f$ is the solution of the PDE term on the faces which we
118 assume to be fixed. At this point, we are quite generic and dump the
119 responsibility onto the user to give us an approximation of @f$ A_{cc} @f$
120 that we can invert. Notably, we highlight that the user may give us
121 @f$ \tilde{A} = A^{cc} @f$ in a DG sense, as we cannot invert @f$ A^{cc} @f$.
122 - Per face, we use a plain Jacobi. We assume that we have an explicit
123 expression of the Riemann solution (simple averaging would be an example)
124 and hence \f$ \hat A = id \f$. We consequently get
125 \f{eqnarray*}{
126 q|_f & \gets & q|_f + \omega _f \left( \mathcal{R}(u^-, u^+) - q|_f \right) \\
127 & = & (1-\omega _f) q|_f + \omega _f \mathcal{R}(u^-, u^+).
128 \f}
129
130
131
132 ## Algorithmical blueprint
133
134 1. Enter cell: Whenever a cell is entered, we create a temporary
135 \f$ K^c(p+1)^d \f$ array (on the heap).
136
137 1. Into this temporary array
138 \f$ r \f$, we write \f$ Mb \f$. This is the mass matrix (right-hand side
139 matrix of the class constructor, as we are not restricted to a
140 traditional mass matrix) times the right-hand sides within the
141 cell. So
142 \f$
143 r \gets Mb.
144 \f$
145 2. Once complete, we substract \f$ P^{c \gets f} q|_f \f$. That is, we take the
146 solution on the face and we project it (cell from face) onto the
147 cell, where we substract it from the previous \f$ r \f$ value. We also
148 do the same with the cell-to-cell couplings. So:
149 \f$
150 r \gets Mb - Pq|_f -A^{cc}u^c.
151 \f$
152 We also do the same with the cell couplings
153 3. The third step is where the MFlops come from: We take the user's
154 approximation of the cell
155 matrix and explicitly invert it. For a linear, invariant matrix
156 \f$ A \f$, we could do so once, but the solver itself makes no
157 assumption if the matrix is invariant or not. So it inverts it
158 every step and uses BLAS routines or this. The result is multiplied
159 with the residual as accumulated so far:
160 \f$
161 r \gets \tilde{A}^{-1} \left( Mb - Pq|_f -A^{cc}u^c \right).
162 \f$
163 4. We run over the solution within the cell and scale each entry
164 with \f$ (1-\omega _c) \f$. If \f$ \omega _c =1 \f$, this means we
165 set the entries to zero. In general, the entries however will
166 only be damped. After that, we add the \f$ r \f$ values to each
167 point. This gives us a new solution guess.
168 5. We project the updated solution \f$ u|_c \f$ onto the faces, i.e.
169 obtain a new \f$ u|_f^{\pm } \f$. This is realised within the
170 action set ProjectOntoFaces.
171 2. touchFaceFirstTime(): Whenever a face is touched, we solve the Riemann
172 problem and update the Riemann guess accordingly. Initially, we assume
173 that the \f$ q \f$ entries on the face are all set to zero.
174
175 1. We evaluate per node on the face the expression \f$ \mathcal{R}(u^-,u^+) \f$.
176 For this, we loop over the \f$ (p+1)^{d-1} \f$ points and call the
177 C++ solver's routine. The user can thus implement the actual
178 Riemann solver themself.
179 2. We take the result and update the value on the face according to
180 \f$
181 q|_f \gets (1-\omega _f) q|_f + \omega _f \mathcal{R}(u^-, u^+).
182 \f$
183
184
185
186 All of this functionality can be found in the subpackage actionsets.UpdateResidual.
187
188 ## How does it work?
189
190 ### Python stuff
191
192 There is a @ref tutorials_multigrid_matrixfree_discontinuous_galerkin_poisson "very simple Poisson solver tutorial"
193 in the tutorials section which illustrates how to use this solver.
194
195 The user needs to supply a few crucial matrices, referred to in the maths above. These are noted and their
196 shapes discussed in the parameters section of the documentation of this class.
197
198 ### Under the hood
199
200 Inside the cells we store \f$ K^c (p+1)^{d} \f$ values for the solution, and the same number for the rhs
201 and residual. We store the residual in the cell so that we can calculate the update to the cell solution
202 separately from the projection of face solution back into the cells, but crucially are able to update
203 the global residual before we apply the preconditioner.
204 On each face we store \f$ K^f (p+1)^{d-1} \f$ values
205 for the solution, and then \f$ 2K^f_{f \gets c} (p+1)^{d-1} \f$ for the projections in a separate array.
206 We note that this factor of two comes from the fact that we have projections from cells on both the left
207 and right. The user is free to choose how many variables from the cell they want to project onto the face.
208 The value K^f_{f \gets c} is exposed in dg.py as the argument ``` --projections_per_face" ```.
209
210 ### Matrices we pass in to the solver class
211 See documentation for this class itself...
212
213
214 So far we have three non-trivial action sets.
215
216 ### InitHigherOrderDofs
217 Code for this is in actionsets.InitHigherOrderDofs. So far nothing happens, other than determining which cells/faces are
218 on the boundary and putting in initial guesses for each cell and face dof. We pass over to the implementation
219 cpp file which should be left in situ in the directory for the application.
220
221 ### UpdateFaceSolution
222 Code for this is in actionsets.DGSolver.UpdateFaceSolution. This action set only contains something nontrivial at
223 touchFaceFirstTime. We apply the Riemann matrix that is present in the Abstract Solver class. This action set goes first,
224 the idea being that we updated the face solution using the updated face projections from the previous sweep.
225
226 ### ProjectIntoCellAndUpdateCellSolution
227 Code for this is in actionsets.DGSolver.ProjectIntoCellAndUpdateCellSolution. This action set works when we touch the cell for the first time.
228 We project the newly updated face solution back into the cells. In particular we:
229 - Write the updated face solution into the residual stored in the cell. This finishes the cell update from the previous sweep.
230 - Update the global residual
231 - Update the cell solution, which is projected back onto faces in the next action set.
232
233 ### ProjectOntoFaces
234 Code for this is in actionsets.DGSolver.ProjectOntoFaces. This occurs strictly after ProjectIntoCellAndUpdateCellSolution, so that we project
235 freshly updated Cell values onto the Faces.
236
237 ### UpdateResidual
238 Code for this is in actionsets.DGSolver.UpdateResidual. We do something nontrivial in touchCellFirstTime, namely perform the cell update
239 rule as above. We set the residual to be \f$ Mb - A^{cc} u^c \f$.
240
241
242 @param solver_includes C++ code snippet
243 Arbitrary C++ code snippet. Usually used to include additional files
244 (therefore the name), though you can use it for preprocessing macros
245 or using statements as well. Has to be a valid C++ expression, i.e.
246 something similar to
247 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
248#include "myfile.h"
249 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
250
251 """
252 def __init__(self,
253 name,
254 dimensions,
255 poly_degree,
256 unknowns_per_cell_node,
257 solutions_per_face_node,
258 projections_per_face_node,
259 min_h,
260 max_h,
261 assembly_matrix,
262 assembly_matrix_scaling,
263 mass_matrix,
264 mass_matrix_scaling,
265 face_from_cell_projection,
266 face_from_cell_projection_scaling,
267 cell_from_face_projection,
268 cell_from_face_projection_scaling,
269 riemann_matrix,
270 boundary_matrix,
271 cell_relaxation,
272 face_relaxation,
273 approximate_system_matrix,
274 approximate_system_matrix_scaling,
275 integration_points,
276 solver_tolerance,
277 custom_init_actionset = None,
278 custom_plot_actionset = None, # just pass in a list of tuples, one for the variable name and the other for the getter
279 custom_init_cell = "",
280 enable_plotting = True, # sometimes we want to disable standard plotting...,
281 plot_predicate = "true", # enable custom plotting behaviour
282 solver_includes = "",
283 use_smart_pointer = True,
284 track_solution_between_cycles = False,
285 ):
286 r"""!
287
288 Construct the solver
289
290 Please consult @ref tutorials_peano4_mask_out_data_usage "the tutorial on data masking"
291 for a discussion of the load_store_compute_flag settings.
292
293 @param poly_degree: Integer
294 The degree of the polynomial that we use. Has to be bigger or equal to one.
295
296 @param unknowns_per_cell_node: Positive Integer
297 This is the @f$ K^c @f$ from the solver description.
298
299 @param solutions_per_face_node: Positive Integer
300 This is the @f$ K^f @f$ from the solver description.
301
302 @param projections_per_face_node: Positive Integer
303 This is the @f$ K^f_{f \gets c} @f$ from the solver description.
304
305 @param cell_relaxation: Float
306 This is the @f$ \omega _c @f$ from the description above and typically
307 chosen form @f$ ]0,1[ @f$ though some scenarios benefit from a slight
308 overrelaxation.
309
310 @param assembly_matrix: \f$ \mathbb{R}^{K^c * (p+1)^{d} \times K^c * (p+1)^{d}}\f$
311 This is the cell-to-cell matrix which defines how the cell nodes couple
312 to one another. Is is a square matrix. You can pass in None. In this
313 case, the generated C++ code will not compile, but you will be allowed
314 to fill it in manually.
315
316 @param assembly_matrix_scaling: \f$ \mathbb{R}^{K^c * (p+1)^{d} \times K^c * (p+1)^{d}}\f$
317 This is a matrix of ints that prescribes how we scale each element in the assembly_matrix
318 ie for each i,j, we do: assembly_matrix[i,j] *= ( h )^( assembly_matrix_scaling[i,j] )
319
320
321 @param mass_matrix: \f$ \mathbb{R}^{K^c * (p+1)^{d} \times K^c * (p+1)^{d}}\f$
322
323 @param mass_matrix_scaling: \f$ \mathbb{R}^{K^c * (p+1)^{d} \times K^c * (p+1)^{d}}\f$
324 This is a matrix of ints that prescribes how we scale each element in the mass_matrix
325 ie for each i,j, we do: mass_matrix[i,j] *= ( h )^( mass_matrix_scaling[i,j] )
326
327 @param face_from_cell: \f$ \mathbb{R}^{2d * 2K^f_{f \gets c} * (p+1)^{d-1} \times K^c * (p+1)^{d}}\f$
328 This matrix projects the cell solution onto all of the faces. Note the
329 extra factor of @f$ 2d @f$. The structure of this matrix is quite important. The rows
330 should be structured in blocks of \f$ 2K^f_{f \gets c} * (p+1)^{d-1} \f$, ie one for each
331 face, in the familiar Peano ordering. We note that this is the same as the number of projections
332 we store per face. We note that, since the 0th face has left-side projections which
333 should be set outside of the cell, that at least half of the rows of the matrix should
334 be empty.
335 We also note that each face contains multiple nodes, and they should be ordered sequentially
336 in the typical Peano fashion, i.e. greedily along each coordinate axis until full, starting
337 from the negative. For each face, with two nodes, with two values (q and u) being projected onto the face,
338 the rows should be ordered as follows:
339 1. Negative projection of q onto node 0
340 2. Negative projection of q onto node 1
341 3. Negative projection of u onto node 0
342 4. Negative projection of u onto node 1
343 5. Positive projection of q onto node 0
344 6. Positive projection of q onto node 1
345 7. Positive projection of u onto node 0
346 8. Positive projection of u onto node 1
347 In two dimensions, all of the negative projections should be 0 on faces 0 and 1. Ditto for the
348 positive projections on faces 2 and 3.
349
350 @param cell_from_face: \f$ \mathbb{R}^{K^c * (p+1)^{d} \times 2d K^f * (p+1)^{d-1}}\f$
351 This matrix projects the face solution onto the cells. Note the
352 extra factor of @f$ 2d @f$, similar to face_from_cell matrix. Some clever indexing
353 is required in the backend here. The enumeration of the cols should follow a similar
354 pattern to the rows of face_from_cell.
355
356 @param cell_from_face_scaling: \f$ \mathbb{R}^{K^c * (p+1)^{d} \times 2d K^f * (p+1)^{d-1}}\f$
357 This is a matrix of ints that prescribes how we scale each element in the cell_from_face
358 ie for each i,j, we do: cell_from_face[i,j] *= ( h )^( cell_from_face_scaling[i,j] )
359
360 @param Riemann_matrix: \f$ \mathbb{R}^{ K^f (p+1)^{d-1} \times 2K^f_{f \gets c} (p+1)^{d-1}} @f$
361 The matrix that combines the left/right projections on the cell and outputs a value
362 which determines @f$ q @f$.
363
364 @param face_relaxation: Float
365 This is the @f$ \omega _f @f$ from above. Chosen from @f$ ]0,1[ @f$.
366
367 @param approximate_system_matrix: @f$ \mathbb{R}^{K^c (p+1)^d \times K^c (p+1)^d} @f$
368 Approximation @f$ \tilde{A} @f$ which will be internally inverted, so it
369 has to be non-singular.
370
371 @param approximate_system_matrix_scaling: @f$ \mathbb{R}^{K^c (p+1)^d \times K^c (p+1)^d} @f$
372
373 @param integration_points: [Float]
374 Sequence of polynomial_degree+1 entries which identify the position of
375 the integration points along the unit interval.
376
377 """
378 super( DiscontinuousGalerkinDiscretisationPointWiseRiemannSolver, self ).__init__(name, min_h, max_h)
379
380 # store everything we pass in anyhow
381 self._dimensions = dimensions
382 self._poly_degree = poly_degree
383 self._unknowns_per_cell_node = unknowns_per_cell_node
384 self._solutions_per_face_node = solutions_per_face_node
385 self._projections_per_face_node = projections_per_face_node
386 self._assembly_matrix = assembly_matrix
387 self._assembly_matrix_scaling = assembly_matrix_scaling
388 self._mass_matrix = mass_matrix
389 self._mass_matrix_scaling = mass_matrix_scaling
390 self._face_from_cell_projection = face_from_cell_projection
391 self._face_from_cell_projection_scaling = face_from_cell_projection_scaling
392 self._cell_from_face_projection = cell_from_face_projection
393 self._cell_from_face_projection_scaling = cell_from_face_projection_scaling
394 self._riemann_matrix = riemann_matrix
395 self._boundary_matrix = boundary_matrix
396 self._approximate_system_matrix = approximate_system_matrix
397 self._approximate_system_matrix_scaling = approximate_system_matrix_scaling
398
399 self._cell_relaxation = cell_relaxation
400 self._face_relaxation = face_relaxation
401
402 self._solver_tolerance = solver_tolerance
403 self._solver_includes = solver_includes
404
405 self._custom_init_actionset = custom_init_actionset
406 self._custom_plot_actionset = custom_plot_actionset
407
408 self._custom_init_cell = custom_init_cell
409
410 self._enable_plotting = enable_plotting
411 self._plot_predicate = plot_predicate
412
413 self._integration_points = integration_points
414
415 self._track_solution_between_cycles = track_solution_between_cycles
416 # K^c * (p+1)^d
417 self._unknowns_per_cell = unknowns_per_cell_node * ( poly_degree + 1) ** dimensions
418 #(K^f + 2K^c) * (p+1)^(d-1)
419 self._unknowns_per_face_solution = ( solutions_per_face_node) * ( poly_degree + 1) ** (dimensions-1)
420
421 # to each node we leave one copy of each of the cell unknowns from left and right
422 self._unknowns_per_face_projection = (2 * projections_per_face_node) * ( poly_degree + 1) ** (dimensions-1)
423
424 # ignore vertex data
425
426 self._use_smart_pointer = use_smart_pointer
427 if use_smart_pointer:
428 self._cell_data.data.add_attribute(peano4.api.dastgen2.Peano4SmartPointerDoubleArray( "solution", str(self._unknowns_per_cell) ))
429 self._cell_data.data.add_attribute(peano4.api.dastgen2.Peano4SmartPointerDoubleArray( "residual", str(self._unknowns_per_cell) ))
430 self._cell_data.data.add_attribute(peano4.api.dastgen2.Peano4SmartPointerDoubleArray( "rhs", str(self._unknowns_per_cell) ))
431
432 if (track_solution_between_cycles):
433 self._cell_data.data.add_attribute(peano4.api.dastgen2.Peano4SmartPointerDoubleArray( "prevCycleSolution", str(self._unknowns_per_cell) ))
434
435 self._face_data.data.add_attribute(peano4.api.dastgen2.Peano4SmartPointerDoubleArray( "solution", str(self._unknowns_per_face_solution ) ))
436 self._face_data.data.add_attribute(peano4.api.dastgen2.Peano4SmartPointerDoubleArray( "lProjection", str(self._unknowns_per_face_solution ) ))
437 self._face_data.data.add_attribute(peano4.api.dastgen2.Peano4SmartPointerDoubleArray( "rProjection", str(self._unknowns_per_face_solution ) ))
438 else:
439 self._cell_data.data.add_attribute(peano4.api.dastgen2.Peano4DoubleArray( "solution", str(self._unknowns_per_cell) ))
440 self._cell_data.data.add_attribute(peano4.api.dastgen2.Peano4DoubleArray( "residual", str(self._unknowns_per_cell) ))
441 self._cell_data.data.add_attribute(peano4.api.dastgen2.Peano4DoubleArray( "rhs", str(self._unknowns_per_cell) ))
442
443 if (track_solution_between_cycles):
444 self._cell_data.data.add_attribute(peano4.api.dastgen2.Peano4DoubleArray( "prevCycleSolution", str(self._unknowns_per_cell) ))
445
446 self._face_data.data.add_attribute(peano4.api.dastgen2.Peano4DoubleArray( "solution", str(self._unknowns_per_face_solution ) ))
447 self._face_data.data.add_attribute(peano4.api.dastgen2.Peano4DoubleArray( "lProjection", str(self._unknowns_per_face_solution ) ))
448 self._face_data.data.add_attribute(peano4.api.dastgen2.Peano4DoubleArray( "rProjection", str(self._unknowns_per_face_solution ) ))
449
450# @todo These lines have to be commented in. They are correct. But
451# then we realise that the face data structure is not unified.
452# We do store w elft and right, and not, how we write in the
453# paper, only one value
454 #! [mghype_matrixfree_solvers_api_DiscontinuousGalerkinDiscretisationPointWiseRiemannSolver_storage_guards]
455# self._cell_data.peano4_mpi_and_storage_aspect.load_store_compute_flag = "::peano4::grid::constructLoadStoreComputeFlag({},{},{})".format(
456# self._hold_cell_and_face_data_guard(),
457
465 self._cell_data.peano4_mpi_and_storage_aspect.implementation_file_includes += self._additional_includes()
466 self._face_data.peano4_mpi_and_storage_aspect.implementation_file_includes += self._additional_includes()
467 #! [mghype_matrixfree_solvers_api_DiscontinuousGalerkinDiscretisationPointWiseRiemannSolver_storage_guards]
468
469 self._face_data.peano4_mpi_and_storage_aspect.merge_implementation = self.get_face_merge_method()
470
471 self.preprocessing_action_set = None
472 self.postprocessing_action_set = None
473
474 self._basic_descend_order = 0
475
476 # assertions for matrix shapes
477 # assert mass_matrix.shape == (self._unknowns_per_cell, self._unknowns_per_cell)
478 # assert assembly_matrix.shape == mass_matrix.shape
479
480 # assert face_from_cell_projection.shape == (self._unknowns_per_face_projection * 2*self._dimensions, self._unknowns_per_cell)
481 # assert (face_from_cell_projection.T).shape == self._cell_from_face_projection.shape
482
483 descend_order = 0
484 self._action_set_update_face_solution = UpdateFaceSolution(self, descend_order+1, False)
485 self._action_set_project_into_cell_and_update_cell_solution = ProjectIntoCellAndUpdateCellSolution(self, descend_order+2, False)
486 self._action_set_project_onto_faces = ProjectOntoFaces(self, descend_order+3, True)
487 self._action_set_update_residual = UpdateResidual(self, descend_order+3, True)
488
489
490 #! [mghype_matrixfree_solvers_api_DiscontinuousGalerkinDiscretisationPointWiseRiemannSolver_hold_cell_and_face_data_guard]
492 """!
493
494 Goard when to hold data in mesh
495
496 Compare with the discussion in the @ref tutorials_peano4_mask_out_data_usage "Peano tutorial on when and how to load data".
497 We don't need the data throughout the mesh construction, where it only
498 blows up the memory consumption and makes the load balancing more expensive
499 as we exchange data via MPI which, at this point, doesn't hold anything
500 meaningful.
501
502 """
503 return """(
504 not marker.hasBeenRefined()
505 and
506 repositories::{}.getState()!=Abstract{}::State::GridConstruction
507 )""".format( self.instance_name(),
508 self._name,
509 )
510
512 """!
513
514 Goard when to hold data in mesh
515
516 Compare with the discussion in the @ref tutorials_peano4_mask_out_data_usage "Peano tutorial on when and how to load data".
517 We don't need the data throughout the mesh construction, where it only
518 blows up the memory consumption and makes the load balancing more expensive
519 as we exchange data via MPI which, at this point, doesn't hold anything
520 meaningful.
521
522 """
523 return """(
524 not marker.hasBeenRefined()
525 and
526 repositories::{}.getState()!=Abstract{}::State::GridConstruction
527 and
528 repositories::{}.getState()!=Abstract{}::State::InitSolution
529 )""".format( self.instance_name(),
530 self._name,
531 self.instance_name(),
532 self._name,
533 )
534
536 return """
537#include "repositories/SolverRepository.h"
538 """
539 #! [mghype_matrixfree_solvers_api_DiscontinuousGalerkinDiscretisationPointWiseRiemannSolver_hold_cell_and_face_data_guard]
540
541
542 def add_to_Peano4_datamodel( self, datamodel, verbose ):
543 super( DiscontinuousGalerkinDiscretisationPointWiseRiemannSolver, self ).add_to_Peano4_datamodel(datamodel,verbose)
544
545 def add_use_statements(self, observer):
546 super( DiscontinuousGalerkinDiscretisationPointWiseRiemannSolver, self ).add_use_statements(observer)
547
548 def add_to_plot(self, observer):
549 """!
550
551 Tell the project's observer how to plot the data
552
553 Nothing fancy here. We add plotters from Peano's toolbox to visualise
554 solution and right-hand side.
555
556 """
557 if self._enable_plotting: # true by default
558 observer.add_action_set( PlotDGDataInPeanoBlockFormat(self, "solution", "getSolution", plot_predicate=self._plot_predicate) )
559 observer.add_action_set( PlotDGDataInPeanoBlockFormat(self, "rhs", "getRhs", plot_predicate=self._plot_predicate) )
560
561 if self._custom_plot_actionset:
562 for name, getter in self._custom_plot_actionset:
563 observer.add_action_set( PlotDGDataInPeanoBlockFormat(self, name, getter, plot_predicate=self._plot_predicate) )
564
565
566 def add_to_create_grid(self, observer):
567 observer.add_action_set(peano4.api.toolbox.CreateRegularGrid(self.max_h))
568 pass
569
570
571 def add_to_init_solution(self, observer):
572 """!
573
574 Solution initialisation
575
576 Is given the project's observer and asks the solver to add all of its
577 action sets to this one.
578
579 @param observer: Observer
580 Observer object of the underlying project representing its algorithmic
581 step. Routine is asked to add its own action sets to this step.
582
583 """
584 if self._custom_init_actionset is None:
585 observer.add_action_set( InitHigherOrderDofs(self) )
586 else:
587 observer.add_action_set( self._custom_init_actionset(self) )
588
589
590 def add_to_solve(self, observer):
591 """!
592
593 I ignore the order of the action sets here and instead derive my
594 own ordering starting from _basic_descend_order.
595
596 """
597 descend_order = observer.highest_descend_invocation_order()
598 if self.preprocessing_action_set != None:
599 self.preprocessing_action_set.descend_invocation_order = 0
600 observer.add_action_set( self.preprocessing_action_set )
601 observer.add_action_set( self._action_set_update_face_solution )
602 observer.add_action_set( self._action_set_project_into_cell_and_update_cell_solution )
603 observer.add_action_set( self._action_set_project_onto_faces )
604 observer.add_action_set( self._action_set_update_residual )
605 if self.postprocessing_action_set != None:
606 self.postprocessing_action_set.descend_invocation_order = descend_order + 5
607 observer.add_action_set( self.postprocessing_action_set )
608
609
610 def add_implementation_files_to_project(self, namespace, output, subdirectory="", abstract_overwrite=True):
611 """!
612
613 The solver creates two classes: An abstract base class and its
614 implementations. The abstract base class will be overwritten if
615 there is one in the directory. I pipe all the Python constants
616 into this base class, so they are available in the C++ code.
617
618 """
619 templatefile_prefix = os.path.dirname( os.path.realpath(__file__) ) + "/templates/"
620 templatefile_prefix += self.__class__.__name__
621
622 if(subdirectory):
623 subdirectory += "/"
624
625 dictionary = self.create_dictionary()
626
628 templatefile_prefix + ".Abstract.template.h",
629 templatefile_prefix + ".Abstract.template.cpp",
630 "Abstract" + self.typename(),
631 namespace,
632 subdirectory + ".",
633 dictionary,
634 abstract_overwrite,
635 True)
637 templatefile_prefix + ".template.h",
638 templatefile_prefix + ".template.cpp",
639 self.typename(),
640 namespace,
641 subdirectory + ".",
642 dictionary,
643 False,
644 True)
645
646 output.add( generated_abstract_header_file )
647 output.add( generated_solver_files )
648 output.makefile.add_h_file( subdirectory + "Abstract" + self._name + ".h", generated=True )
649 output.makefile.add_h_file( subdirectory + self._name + ".h", generated=True )
650 output.makefile.add_cpp_file( subdirectory + "Abstract" + self._name + ".cpp", generated=True )
651 output.makefile.add_cpp_file( subdirectory + self._name + ".cpp", generated=True )
652
654 """!
655
656 The actual merge of faces is simple, as we compute the numerical flux
657 redundantly, i.e. we really only have to copy stuff one way or the
658 other.
659
660 """
661 return jinja2.Template("""
662 if (!marker.willBeRefined()
663 and
664 context == ::peano4::grid::TraversalObserver::SendReceiveContext::BoundaryExchange
665
666 ) { //todo change this to use the face's own enum
667
668 setLProjection(
669 getLProjection() + neighbour.getLProjection()
670 );
671 setRProjection(
672 getRProjection() + neighbour.getRProjection()
673 );
674 }
675 """).render(**self.create_dictionary())
676
678 return {
679 "SOLVER_INCLUDES" : self._solver_includes,
680 "MIN_H": self.min_h,
681 "MAX_H": self.max_h,
682 "SOLVER_NAME": self.typename(),
683 "SOLVER_INSTANCE": self.instance_name(),
684 "CELL_CARDINALITY" : self._unknowns_per_cell,
685 "FACE_CARDINALITY_SOLUTION" : self._unknowns_per_face_solution,
686 "FACE_CARDINALITY_PROJECTION" : self._unknowns_per_face_projection,
687 "VERTEX_CARDINALITY" : 1,
688 "NODES_PER_CELL" : (self._poly_degree + 1) ** self._dimensions,
689 "NODES_PER_FACE" : (self._poly_degree + 1) ** (self._dimensions-1),
690 "UNKNOWNS_PER_CELL_NODE" : self._unknowns_per_cell_node,
691 "SOLUTIONS_PER_FACE_NODE" : self._solutions_per_face_node,
692 "PROJECTIONS_PER_FACE_NODE" : self._projections_per_face_node,
693 "RHS_MATRIX" : self._mass_matrix,
694 "RHS_MATRIX_SCALING" : self._mass_matrix_scaling,
695 "ASSEMBLY_MATRIX" : self._assembly_matrix,
696 "ASSEMBLY_MATRIX_SCALING" : self._assembly_matrix_scaling,
697 "FACE_FROM_CELL_PROJECTION" : self._face_from_cell_projection,
698 "FACE_FROM_CELL_PROJECTION_SCALING": self._face_from_cell_projection_scaling,
699 "CELL_FROM_FACE_PROJECTION" : self._cell_from_face_projection,
700 "CELL_FROM_FACE_PROJECTION_SCALING" : self._cell_from_face_projection_scaling,
701 "RIEMANN_MATRIX" : self._riemann_matrix,
702 "BOUNDARY_MATRIX" : self._boundary_matrix,
703 "APPROX_SYSTEM_MATRIX" : self._approximate_system_matrix,
704 "APPROX_SYSTEM_MATRIX_SCALING": self._approximate_system_matrix_scaling,
705 "CELL_RELAXATION" : self._cell_relaxation,
706 "FACE_RELAXATION" : self._face_relaxation,
707 "POLY_DEGREE" : self._poly_degree,
708 "SOLVER_TOLERANCE" : self._solver_tolerance,
709 "CUSTOM_INIT_CELL" : self._custom_init_cell,
710 "INTEGRATION_POINTS" : self._integration_points,
711 "USE_SMART_POINTER" : self._use_smart_pointer,
712 "TRACK_SOLUTION_BETWEEN_CYCLES": self._track_solution_between_cycles
713 }
Specialisation of array using Peano's tarch.
Specialisation of array using Peano's tarch with a smart pointer.
__init__(self, name, dimensions, poly_degree, unknowns_per_cell_node, solutions_per_face_node, projections_per_face_node, min_h, max_h, assembly_matrix, assembly_matrix_scaling, mass_matrix, mass_matrix_scaling, face_from_cell_projection, face_from_cell_projection_scaling, cell_from_face_projection, cell_from_face_projection_scaling, riemann_matrix, boundary_matrix, cell_relaxation, face_relaxation, approximate_system_matrix, approximate_system_matrix_scaling, integration_points, solver_tolerance, custom_init_actionset=None, custom_plot_actionset=None, custom_init_cell="", enable_plotting=True, plot_predicate="true", solver_includes="", use_smart_pointer=True, track_solution_between_cycles=False)
Construct the solver.
Abstract base class for matrix free solvers.
Definition Solver.py:9
Init the degrees of freedom of a higher order scheme where nodes sit in cells.
get_face_merge_method(self)
The actual merge of faces is simple, as we compute the numerical flux redundantly,...
add_to_solve(self, observer)
I ignore the order of the action sets here and instead derive my own ordering starting from _basic_de...
add_implementation_files_to_project(self, namespace, output, subdirectory="", abstract_overwrite=True)
The solver creates two classes: An abstract base class and its implementations.