Peano
Loading...
Searching...
No Matches
CollocatedLowOrderDiscretisation.py
Go to the documentation of this file.
1# This file is part of the ExaHyPE2 project. For conditions of distribution and
2# use, please see the copyright notice at www.peano-framework.org
3import peano4
4import jinja2
6import dastgen2
7
8import os
9
10from .Solver import Solver
11from multigrid.petsc.api.actionsets.EnumerateDoFs import EnumerateDoFs
12from multigrid.petsc.api.actionsets.InitVertexDoFs import InitVertexDoFs
13from multigrid.petsc.api.actionsets.ProjectPETScSolutionBackOntoMesh import ProjectPETScSolutionOnVerticesBackOntoMesh
14
15from abc import abstractmethod
16
17from peano4.solversteps.ActionSet import ActionSet
18
19
20
21
23 """!
24
25 Trigger assembly of PETSc matrix and project rhs values from mesh onto rhs vector entries
26
27 """
28 TemplateTouchCellFirstTime = """
29 if (fineGridCell{{SOLVER_NAME}}PETScData.getType() == celldata::{{SOLVER_NAME}}PETScData::Type::Interior) {
30 //init a vector to collect global indices, and recall their position in the
31 //enumeration so we can apply the stencil correctly
32 std::vector<int> vertexIndices(TwoPowerD, -1);
33
34 for (int i=0; i<TwoPowerD ; i++) {
35 if (fineGridVertices{{SOLVER_NAME}}PETScData(i).getType() == vertexdata::{{SOLVER_NAME}}PETScData::Type::Interior) {
36 //get a global index
37 std::pair<int,int> localIndex = std::make_pair(_spacetreeId, fineGridVertices{{SOLVER_NAME}}PETScData(i).getUnknownBaseNumber());
38 int globalIndex = repositories::{{SOLVER_INSTANCE}}.getLocalToGlobalMap().getGlobalIndex(localIndex, ::petsc::LocalToGlobalMap::Type::Vertex);
39 vertexIndices[i] = globalIndex;
40 }
41 }
42
43 //lhs
44 auto lhsMatrixEntry = repositories::{{SOLVER_INSTANCE}}.getLhsMatrix(marker.x(),marker.h());
45 // loop over the dofs
46 for (int d=0; d<{{VERTEX_CARDINALITY}}; d++)
47 {
48 for (int i=0; i<TwoPowerD; i++) {
49 for (int j=0; j<TwoPowerD; j++) {
50 if (vertexIndices[i] != -1 and vertexIndices[j] != -1) {
51 repositories::{{SOLVER_INSTANCE}}.getLinearEquationSystem().increment(
52 vertexIndices[i] + d, //d for dof
53 vertexIndices[j] + d,
54 lhsMatrixEntry(d*TwoPowerD+i,d*TwoPowerD+j)
55 );
56 }
57 }
58 }
59
60 // rhs
61 auto rhs = repositories::{{SOLVER_INSTANCE}}.getRhsMatrix(marker.x(),marker.h());
62 for (int i=0; i<TwoPowerD; i++) {
63 logTraceInWith2Arguments( "touchCellFirstTime::RHS", marker.toString(), rhs );
64 if (fineGridVertices{{SOLVER_NAME}}PETScData(i).getType() == vertexdata::{{SOLVER_NAME}}PETScData::Type::Interior) {
65 double rhsContribution = 0.0;
66 for (int j=0; j<TwoPowerD; j++) {
67 double rhsValue = fineGridVertices{{SOLVER_NAME}}(j).getRhs(d);
68
69 /*
70 we call d*TwoPowerD+i here because the rhs matrix typically has dimensions
71 TwoPowerD x TwoPowerD. When we promote to n unknowns, we make the matrix
72 larger: n*TwoPowerD x n*TwoPowerD. So, we call d*TwoPowerD+i so that we can
73 skip past the parts of the matrix we are no longer interested in.
74 */
75 rhsContribution += rhs(d*TwoPowerD+i,d*TwoPowerD+j) * rhsValue;
76 logTraceInWith3Arguments("AssembleRhs", rhs(d*TwoPowerD+i,d*TwoPowerD+j), rhsContribution, rhsValue);
77 logTraceOut("AssembleRhs");
78 }
79 repositories::{{SOLVER_INSTANCE}}.getLinearEquationSystem().increment(
80 vertexIndices[i] + d,
81 rhsContribution
82 );
83 }
84 }
85
86 }
87 logTraceOut( "touchCellFirstTime::RHS" );
88 }
89 """
90
91 TemplateTouchVertexFirstTime="""
92 //here we send the value, rhs from the mesh to petsc
93
94 if (fineGridVertex{{SOLVER_NAME}}PETScData.getType() == vertexdata::{{SOLVER_NAME}}PETScData::Type::Interior) {
95 //first, get global index
96 std::pair<int,int> localIndex = std::make_pair(_spacetreeId, fineGridVertex{{SOLVER_NAME}}PETScData.getUnknownBaseNumber());
97 int globalIndex = repositories::{{SOLVER_INSTANCE}}.getLocalToGlobalMap().getGlobalIndex(localIndex, ::petsc::LocalToGlobalMap::Type::Vertex);
98
99 assertion3(globalIndex>=0, globalIndex, marker.toString(), fineGridVertex{{SOLVER_NAME}}PETScData.toString() );
100
101 for (int i=0; i<{{VERTEX_CARDINALITY}}; i++)
102 {
103 //get the rhs that was present in the mesh
104 double rhs = fineGridVertex{{SOLVER_NAME}}.getRhs(i);
105
106 //send these to petsc
107 repositories::{{SOLVER_INSTANCE}}.getLinearEquationSystem().insert(globalIndex + i, rhs);
108 }
109
110 }
111 """
112
113 def __init__(self,
114 solver,
115 ):
116 """!
117
118Initialise vertex-associated degrees of freedom
119
120The initialisation requires a solver object, as we have to know what C++
121object this solver will produce.
122
123solver: petsc.solvers.CollocatedLowOrderDiscretisation or similar solver where
124 degrees of freedom are assigned exclusively to the vertices.
125
126 """
127
128 super( AssemblePetscMatrix, self ).__init__()
129 self.d = {}
130 self.d["SOLVER_INSTANCE"] = solver.instance_name()
131 self.d["SOLVER_NAME"] = solver.typename()
132 self.d["VERTEX_CARDINALITY"] = solver.number_of_matrix_entries_per_vertex
133
134 def get_body_of_operation(self,operation_name):
135 """!
136
137Provide C++ code snippet for peano4.solversteps.ActionSet.OPERATION_TOUCH_VERTEX_FIRST_TIME
138Provide C++ code snippet for peano4.solversteps.ActionSet.OPERATION_TOUCH_CELL_FIRST_TIME
139
140Only touchVertexFirstTime is an event where this action set actually
141does something: It inserts the template TemplateTouchVertexFirstTime and
142replaces it with entries from the dictionary. The latter is befilled
143in init().
144
145We actually do something during touchVertexFirstTime and touchCellFirstTime. We insert the
146appropriate template into each.
147
148 """
149 result = ""
150 if operation_name==peano4.solversteps.ActionSet.OPERATION_TOUCH_CELL_FIRST_TIME:
151 result = jinja2.Template(self.TemplateTouchCellFirstTime).render(**self.d)
152 pass
153 if operation_name==peano4.solversteps.ActionSet.OPERATION_TOUCH_VERTEX_FIRST_TIME:
154 result = jinja2.Template(self.TemplateTouchVertexFirstTime).render(**self.d)
155 pass
156 return result
157
158
160 """!
161
162 Configure name of generated C++ action set
163
164 This action set will end up in the directory observers with a name that
165 reflects how the observer (initialisation) is mapped onto this action
166 set. The name pattern is ObserverName2ActionSetIdentifier where this
167 routine co-determines the ActionSetIdentifier. We make is reflect the
168 Python class name.
169
170 """
171 return __name__.replace(".py", "").replace(".", "_")
172
173
175 """!
176
177 The action set that Peano will generate that corresponds to this class
178 should not be modified by users and can safely be overwritten every time
179 we run the Python toolkit.
180
181 """
182 return False
183
184
185 def get_includes(self):
186 """!
187
188Consult petsc.Project for details
189
190"""
191 return """
192#include "repositories/SolverRepository.h"
193#include "tarch/la/Matrix.h"
194"""
195
196 def get_attributes(self):
197 """!
198
199
200 """
201 return f"""
202 int _spacetreeId;
203"""
204
206 """!
207
208Define body of constructor
209
210@see get_attributes()
211
212 """
213 return f"""
214 _spacetreeId = treeNumber;
215"""
216
217
218
220 """!
221
222 This is one of the simplest solvers that you can think of. It places
223 one degree of freedom (can be scalar or a vector) on each vertex. As
224 we support solely element-wise assembly, this means you can implement
225 things like a 9-point (2d) or 27-point (3d) stencil, but not really
226 anything more sophisticated.
227
228 cell_lhs_matrix: [double] or []
229 Pass in [] if you prefer to assemble the matrix per cell yourself.
230
231 cell_rhs_matrix: [double] or []
232 Pass in [] if you prefer to assemble the matrix per cell yourself.
233
234 cell_lhs_matrix_scaling: Positive integer
235 The lhs matrix is scaled with @f$ h^x @f$ where the x is defined by this
236 parameter. If you don't specify an lhs matrix, i.e. you prefer to inject
237 this matrix manually, you can leave this parameter None, as it is not
238 used.
239
240 cell_rhs_matrix_scaling: Positive integer
241 The rhs matrix is scaled with @f$ h^x @f$ where the x is defined by this
242 parameter. If you don't specify a rhs matrix, i.e. you prefer to inject
243 this matrix manually, you can leave this parameter None, as it is not
244 used.
245
246 """
247 def __init__(self,
248 name,
249 unknowns,
250 dimensions,
251 min_h,
252 max_h,
253 cell_lhs_matrix,
254 cell_rhs_matrix,
255 cell_lhs_matrix_scaling,
256 cell_rhs_matrix_scaling,
257 ):
258 """!
259
260 Collocated low-order (d-linear) Finite Elements
261
262 """
263 super( CollocatedLowOrderDiscretisation, self ).__init__( name,
264 min_h,
265 max_h
266 )
267 self._unknowns = unknowns
269 self._vertex_pde_data.data.add_attribute( peano4.dastgen2.Peano4DoubleArray( "value", str(unknowns) ) )
270 self._vertex_pde_data.data.add_attribute( peano4.dastgen2.Peano4DoubleArray( "rhs", str(unknowns) ) )
271
272 self.cell_lhs_matrix = cell_lhs_matrix
273 self.cell_rhs_matrix = cell_rhs_matrix
274 self.cell_lhs_matrix_scaling = cell_lhs_matrix_scaling
275 self.cell_rhs_matrix_scaling = cell_rhs_matrix_scaling
276 pass
277
278
279 def add_to_Peano4_datamodel( self, datamodel, verbose ):
280 super( CollocatedLowOrderDiscretisation, self ).add_to_Peano4_datamodel(datamodel,verbose)
281 datamodel.add_vertex(self._vertex_pde_data)
282
283
284 def add_use_statements(self, observer):
285 super( CollocatedLowOrderDiscretisation, self ).add_use_statements(observer)
286 observer.use_vertex( self._vertex_pde_data )
287
288
289 def add_to_plot(self, observer):
290 """!
291
292 Tell the project's observer how to plot the data
293
294 Nothing fancy here. We add plotters from Peano's toolbox to visualise
295 solution and right-hand side.
296
297 """
298 observer.add_action_set( peano4.toolbox.PlotVertexDataInPeanoBlockFormat(
299 filename = "solution-" + self._name,
300 vertex_unknown = self._vertex_pde_data,
301 getter = "getValue().data()",
302 description = "u",
303 time_stamp_evaluation = peano4.toolbox.PlotVertexDataInPeanoBlockFormat.CountTimeSteps,
304 number_of_unknows_per_vertex = self._unknowns,
305 guard_predicate = "true",
306 ))
307 observer.add_action_set( peano4.toolbox.PlotVertexDataInPeanoBlockFormat(
308 filename = "rhs-" + self._name,
309 vertex_unknown = self._vertex_pde_data,
310 getter = "getRhs().data()",
311 description = "u",
312 time_stamp_evaluation = peano4.toolbox.PlotVertexDataInPeanoBlockFormat.CountTimeSteps,
313 number_of_unknows_per_vertex = self._unknowns,
314 guard_predicate = "true",
315 ))
316 pass
317
318
319 def add_to_create_grid(self, observer):
320 observer.add_action_set(peano4.toolbox.CreateRegularGrid(self.max_hmax_h))
321 pass
322
323
325 """!
326
327 Solution initialisation
328
329 Close to trivial: Just add the action set petsc.actionsets.InitVertexDoFs
330 to the observer.
331
332 """
333 observer.add_action_set( EnumerateDoFs(self, True, False) )
334 observer.add_action_set( InitVertexDoFs(self) )
335 pass
336
337
338 def add_to_assemble(self, observer):
339 observer.add_action_set( AssemblePetscMatrix(self) )
340 pass
341
342
343 def add_to_init_petsc(self, observer):
344 pass
345
346
347 def add_to_map_solution_onto_mesh(self, observer):
348 observer.add_action_set( ProjectPETScSolutionOnVerticesBackOntoMesh(self) )
349 pass
350
351
352 def add_implementation_files_to_project(self, namespace, output, subdirectory=""):
353 """
354
355 The solver creates two classes: An abstract base class and its
356 implementations. The abstract base class will be overwritten if
357 there is one in the directory. I pipe all the Python constants
358 into this base class, so they are available in the C++ code.
359
360 The implementation file will not be overwritten, as I assume that
361 the users will write their own domain-specific stuff in there. If
362 it does not exist yet, I'll create an empty stub which can be
363 befilled with something meaningful.
364
365 Besides the creation of these two files, I also add the files to the
366 project artifacts and the makefile, so they are captured by the build
367 system.
368
369 """
370 templatefile_prefix = os.path.dirname( os.path.realpath(__file__) ) + "/"
371 templatefile_prefix += self.__class__.__name__
372
373 if(subdirectory):
374 subdirectory += "/"
375
376 dictionary = {
377 "SOLVER_INCLUDES": "",
378 "MIN_H": self.min_hmin_h,
379 "MAX_H": self.max_hmax_h,
380 "CELL_LHS_MATRIX": self.cell_lhs_matrix,
381 "CELL_RHS_MATRIX": self.cell_rhs_matrix,
383 "CELL_LHS_MATRIX_SCALING": self.cell_lhs_matrix_scaling,
384 "CELL_RHS_MATRIX_SCALING": self.cell_rhs_matrix_scaling,
385 "SOLVER_NAME": self.typename(),
386 }
387
388 generated_abstract_header_file = peano4.output.Jinja2TemplatedHeaderImplementationFilePair(
389 templatefile_prefix + ".Abstract.template.h",
390 templatefile_prefix + ".Abstract.template.cpp",
391 "Abstract" + self.typename(),
392 namespace,
393 subdirectory + ".",
394 dictionary,
395 True,
396 True)
398 templatefile_prefix + ".template.h",
399 templatefile_prefix + ".template.cpp",
400 self.typename(),
401 namespace,
402 subdirectory + ".",
403 dictionary,
404 False,
405 True)
406
407 output.add( generated_abstract_header_file )
408 output.add( generated_solver_files )
409 output.makefile.add_h_file( subdirectory + "Abstract" + self._name + ".h", generated=True )
410 output.makefile.add_h_file( subdirectory + self._name + ".h", generated=True )
411 output.makefile.add_cpp_file( subdirectory + "Abstract" + self._name + ".cpp", generated=True )
412 output.makefile.add_cpp_file( subdirectory + self._name + ".cpp", generated=True )
413
414
415
416 @property
418 """!
419
420 Nothing is associated with the vertex. There's nothing to be done here.
421
422 """
423 return self._unknowns
424
425 @property
427 """!
428
429 In the DG scheme, we have a projection from the left and we have a
430 projection from the right. These values are proper projections, i.e.
431 they do not carry any semantics. Then we have to solve the Riemann
432 problem, and need one more unknown to store the outcome of that one.
433
434 """
435 return 0
436
437 @property
439 """!
440
441 This is the actual unknowns per cell
442
443 """
444 return 0
445
Trigger assembly of PETSc matrix and project rhs values from mesh onto rhs vector entries.
user_should_modify_template(self)
The action set that Peano will generate that corresponds to this class should not be modified by user...
get_body_of_operation(self, operation_name)
Provide C++ code snippet for peano4.solversteps.ActionSet.OPERATION_TOUCH_VERTEX_FIRST_TIME Provide...
__init__(self, solver)
Initialise vertex-associated degrees of freedom.
get_attributes(self)
Return attributes as copied and pasted into the generated class.
add_use_statements(self, observer)
Register the index numbers to be used in each and every mesh traversal.
add_to_plot(self, observer)
Tell the project's observer how to plot the data.
number_of_matrix_entries_per_face(self)
In the DG scheme, we have a projection from the left and we have a projection from the right.
add_to_create_grid(self, observer)
Add whatever action set you want to use to observer.
add_to_assemble(self, observer)
Add whatever action set you want to use to observer.
add_implementation_files_to_project(self, namespace, output, subdirectory="")
The solver creates two classes: An abstract base class and its implementations.
add_to_map_solution_onto_mesh(self, observer)
Add whatever action set you want to use to observer.
__init__(self, name, unknowns, dimensions, min_h, max_h, cell_lhs_matrix, cell_rhs_matrix, cell_lhs_matrix_scaling, cell_rhs_matrix_scaling)
Collocated low-order (d-linear) Finite Elements.
Abstract base class for all PETSc solvers.
Definition Solver.py:12
number_of_matrix_entries_per_vertex(self)
Definition Solver.py:243
Specialisation of array using Peano's tarch.
Default superclass for any data model in Peano which is stored within the grid.
Definition DaStGen2.py:157
Action set (reactions to events)
Definition ActionSet.py:6