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
219class CollocatedLowOrderDiscretisation(Solver):
220 """!
221
222 \page petsc_collocated_solver Collocated Solver with PETSc
223
224 This is one of the simplest solvers that you can think of. It places
225 one degree of freedom (can be scalar or a vector) on each vertex. As
226 we support solely element-wise assembly, this means you can implement
227 things like a 9-point (2d) or 27-point (3d) stencil, but not really
228 anything more sophisticated.
229
230 cell_lhs_matrix: [double] or []
231 Pass in [] if you prefer to assemble the matrix per cell yourself.
232
233 cell_rhs_matrix: [double] or []
234 Pass in [] if you prefer to assemble the matrix per cell yourself.
235
236 cell_lhs_matrix_scaling: Positive integer
237 The lhs matrix is scaled with @f$ h^x @f$ where the x is defined by this
238 parameter. If you don't specify an lhs matrix, i.e. you prefer to inject
239 this matrix manually, you can leave this parameter None, as it is not
240 used.
241
242 cell_rhs_matrix_scaling: Positive integer
243 The rhs matrix is scaled with @f$ h^x @f$ where the x is defined by this
244 parameter. If you don't specify a rhs matrix, i.e. you prefer to inject
245 this matrix manually, you can leave this parameter None, as it is not
246 used.
247
248 """
249 def __init__(self,
250 name,
251 unknowns,
252 dimensions,
253 min_h,
254 max_h,
255 cell_lhs_matrix,
256 cell_rhs_matrix,
257 cell_lhs_matrix_scaling,
258 cell_rhs_matrix_scaling,
259 ):
260 """!
261
262 Collocated low-order (d-linear) Finite Elements
263
264 """
265 super( CollocatedLowOrderDiscretisation, self ).__init__( name,
266 min_h,
267 max_h
268 )
269 self._unknowns = unknowns
270 self._vertex_pde_data = peano4.datamodel.DaStGen2( name )
271 self._vertex_pde_data.data.add_attribute( peano4.dastgen2.Peano4DoubleArray( "value", str(unknowns) ) )
272 self._vertex_pde_data.data.add_attribute( peano4.dastgen2.Peano4DoubleArray( "rhs", str(unknowns) ) )
273
274 self.cell_lhs_matrix = cell_lhs_matrix
275 self.cell_rhs_matrix = cell_rhs_matrix
276 self.cell_lhs_matrix_scaling = cell_lhs_matrix_scaling
277 self.cell_rhs_matrix_scaling = cell_rhs_matrix_scaling
278 pass
279
280
281 def add_to_Peano4_datamodel( self, datamodel, verbose ):
282 super( CollocatedLowOrderDiscretisation, self ).add_to_Peano4_datamodel(datamodel,verbose)
283 datamodel.add_vertex(self._vertex_pde_data)
284
285
286 def add_use_statements(self, observer):
287 super( CollocatedLowOrderDiscretisation, self ).add_use_statements(observer)
288 observer.use_vertex( self._vertex_pde_data )
289
290
291 def add_to_plot(self, observer):
292 """!
293
294 Tell the project's observer how to plot the data
295
296 Nothing fancy here. We add plotters from Peano's toolbox to visualise
297 solution and right-hand side.
298
299 """
301 filename = "solution-" + self._name,
302 vertex_unknown = self._vertex_pde_data,
303 getter = "getValue().data()",
304 description = "u",
305 time_stamp_evaluation = peano4.toolbox.PlotVertexDataInPeanoBlockFormat.CountTimeSteps,
306 number_of_unknows_per_vertex = self._unknowns,
307 guard_predicate = "true",
308 ))
310 filename = "rhs-" + self._name,
311 vertex_unknown = self._vertex_pde_data,
312 getter = "getRhs().data()",
313 description = "u",
314 time_stamp_evaluation = peano4.toolbox.PlotVertexDataInPeanoBlockFormat.CountTimeSteps,
315 number_of_unknows_per_vertex = self._unknowns,
316 guard_predicate = "true",
317 ))
318 pass
319
320
321 def add_to_create_grid(self, observer):
322 observer.add_action_set(peano4.toolbox.CreateRegularGrid(self.max_h))
323 pass
324
325
326 def add_to_enumerate_and_init_solution(self, observer):
327 """!
328
329 Solution initialisation
330
331 Close to trivial: Just add the action set petsc.actionsets.InitVertexDoFs
332 to the observer.
333
334 """
335 observer.add_action_set( EnumerateDoFs(self, True, False) )
336 observer.add_action_set( InitVertexDoFs(self) )
337 pass
338
339
340 def add_to_assemble(self, observer):
341 observer.add_action_set( AssemblePetscMatrix(self) )
342 pass
343
344
345 def add_to_init_petsc(self, observer):
346 pass
347
348
349 def add_to_map_solution_onto_mesh(self, observer):
350 observer.add_action_set( ProjectPETScSolutionOnVerticesBackOntoMesh(self) )
351 pass
352
353
354 def add_implementation_files_to_project(self, namespace, output, subdirectory=""):
355 """
356
357 The solver creates two classes: An abstract base class and its
358 implementations. The abstract base class will be overwritten if
359 there is one in the directory. I pipe all the Python constants
360 into this base class, so they are available in the C++ code.
361
362 The implementation file will not be overwritten, as I assume that
363 the users will write their own domain-specific stuff in there. If
364 it does not exist yet, I'll create an empty stub which can be
365 befilled with something meaningful.
366
367 Besides the creation of these two files, I also add the files to the
368 project artifacts and the makefile, so they are captured by the build
369 system.
370
371 """
372 templatefile_prefix = os.path.dirname( os.path.realpath(__file__) ) + "/"
373 templatefile_prefix += self.__class__.__name__
374
375 if(subdirectory):
376 subdirectory += "/"
377
378 dictionary = {
379 "SOLVER_INCLUDES": "",
380 "MIN_H": self.min_h,
381 "MAX_H": self.max_h,
382 "CELL_LHS_MATRIX": self.cell_lhs_matrix,
383 "CELL_RHS_MATRIX": self.cell_rhs_matrix,
384 "VERTEX_CARDINALITY": self.number_of_matrix_entries_per_vertex,
385 "CELL_LHS_MATRIX_SCALING": self.cell_lhs_matrix_scaling,
386 "CELL_RHS_MATRIX_SCALING": self.cell_rhs_matrix_scaling,
387 "SOLVER_NAME": self.typename(),
388 }
389
390 generated_abstract_header_file = peano4.output.Jinja2TemplatedHeaderImplementationFilePair(
391 templatefile_prefix + ".Abstract.template.h",
392 templatefile_prefix + ".Abstract.template.cpp",
393 "Abstract" + self.typename(),
394 namespace,
395 subdirectory + ".",
396 dictionary,
397 True,
398 True)
400 templatefile_prefix + ".template.h",
401 templatefile_prefix + ".template.cpp",
402 self.typename(),
403 namespace,
404 subdirectory + ".",
405 dictionary,
406 False,
407 True)
408
409 output.add( generated_abstract_header_file )
410 output.add( generated_solver_files )
411 output.makefile.add_h_file( subdirectory + "Abstract" + self._name + ".h", generated=True )
412 output.makefile.add_h_file( subdirectory + self._name + ".h", generated=True )
413 output.makefile.add_cpp_file( subdirectory + "Abstract" + self._name + ".cpp", generated=True )
414 output.makefile.add_cpp_file( subdirectory + self._name + ".cpp", generated=True )
415
416
417
418 @property
419 def number_of_matrix_entries_per_vertex(self):
420 """!
421
422 Nothing is associated with the vertex. There's nothing to be done here.
423
424 """
425 return self._unknowns
426
427 @property
428 def number_of_matrix_entries_per_face(self):
429 """!
430
431 In the DG scheme, we have a projection from the left and we have a
432 projection from the right. These values are proper projections, i.e.
433 they do not carry any semantics. Then we have to solve the Riemann
434 problem, and need one more unknown to store the outcome of that one.
435
436 """
437 return 0
438
439 @property
440 def number_of_matrix_entries_per_cell(self):
441 """!
442
443 This is the actual unknowns per cell
444
445 """
446 return 0
447
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.
Abstract base class for all PETSc solvers.
Definition Solver.py:12
Specialisation of dastgen2.attributes.DoubleArray which relies on Peano's tarch.
Default superclass for any data model in Peano which is stored within the grid.
Definition DaStGen2.py:47
Action set (reactions to events)
Definition ActionSet.py:6