Peano
finite-volumes.py
Go to the documentation of this file.
1 """
2 
3  A very simple example which demonstrates how to configure a patch-based
4  Finite Volume solver in Peano4. The code relies on snippets from ExaHyPE2.
5  However, it relies only on ExaHyPE's C/FORTRAN compute kernels, i.e. the
6  sophisticated build environment of this H2020 project is not used. The
7  solver simulates the simple Euler equations.
8 
9 """
10 
11 
12 #
13 # We import Peano4 as project. If this step fails, ensure that your environment
14 # variable PYTHONPATH points to Peano4's python directory.
15 #
16 import os
17 import peano4
18 import peano4.datamodel
19 import peano4.solversteps
20 import peano4.output
21 import peano4.visualisation
23 
24 
25 #
26 # Lets first clean up all plot files, so we don't get a mismatch
27 #
28 output_files = [ f for f in os.listdir(".") if f.endswith(".peano-patch-file") ]
29 for f in output_files:
30  os.remove(f)
31 
32 
33 #
34 # Create a project and configure it to end up in a subnamespace (and thus
35 # subdirectory).
36 #
37 project = peano4.Project( ["examples", "finitevolumes"], "finitevolumes", "." )
38 
39 
40 #
41 # The solver will be patch-based, i.e. we will have one patch per cell.
42 #
43 patch_size = 13
44 unknowns = 5
45 patch = peano4.datamodel.Patch( (patch_size,patch_size,patch_size), unknowns, "Q" )
46 project.datamodel.add_cell(patch)
47 
48 
49 #
50 # Along the faces, we have the patch overlaps. As we use only a Rusanov flux,
51 # one cell of overlap between adjacent patches is sufficient.
52 #
53 patch_overlap = peano4.datamodel.Patch( (2,patch_size,patch_size), unknowns, "Q" )
54 project.datamodel.add_face(patch_overlap)
55 
56 
57 #
58 # For each step that we wanna do, we define one solver step. In the first step that
59 # we use, we add one grid level to the mesh per step. We also initialise the blocks
60 # with the initial values.
61 #
62 create_grid = peano4.solversteps.Step( "CreateGrid" )
63 create_grid.use_face(patch_overlap)
64 create_grid.use_cell(patch)
65 #create_grid.add_action_set( peano4.toolbox.PlotGridInPeanoBlockFormat("grid-dump", patch) )
66 create_grid.add_action_set( peano4.toolbox.blockstructured.ProjectPatchOntoFaces(patch,patch_overlap) )
67 project.solversteps.add_step(create_grid)
68 
69 
70 #
71 # Next, we want to dump the final solution once. Luckily, quite a lot of
72 # support for blockstructured grid is available within Peano's toolbox. So
73 # we use features from there. In the example above, we added code to the
74 # step manually (the grid setup). This time, we don't want to add any
75 # further code manually.
76 #that I can
77 print_solution = peano4.solversteps.Step( "PlotSolution" )
78 print_solution.use_cell(patch)
79 print_solution.use_face(patch_overlap)
80 print_solution.remove_all_actions()
81 plotter = peano4.toolbox.blockstructured.PlotPatchesInPeanoBlockFormat("solution",patch,"Q")
82 print_solution.add_action_set( plotter )
83 project.solversteps.add_step(print_solution)
84 
85 
86 #
87 # Haven't documented anything yet
88 #
89 # @todo Dsa ist schon gefused, weil wir ja die Reihenfolge umgedreht haben. Eigentlich muss das action set andersrum laufen
90 #
91 perform_time_step = peano4.solversteps.Step( "TimeStep" )
92 perform_time_step.use_cell(patch)
93 perform_time_step.use_face(patch_overlap)
94 # @todo Darf ich den Face gleich wieder ueberschreiben? Nachbar bekommt ja dann GS-meassig den neuen mit u.U.
95 functor = """
96  auto eigenvalues = [](double Q[5], const tarch::la::Vector<DIMENSIONS,double>& x, int normal, double lambda[5]) -> void {
97  constexpr double gamma = 1.4;
98  const double irho = 1./Q[0];
99  #if DIMENSIONS==3
100  const double p = (gamma-1) * (Q[4] - 0.5*irho*Q[1]*Q[1]+Q[2]*Q[2]+Q[3]*Q[3]);
101  #else
102  const double p = (gamma-1) * (Q[4] - 0.5*irho*Q[1]*Q[1]+Q[2]*Q[2]);
103  #endif
104 
105  const double u_n = Q[normal + 1] * irho;
106  assertion10( gamma * p * irho>=0.0, gamma, p, irho, x, normal, Q[0], Q[1], Q[2], Q[3], Q[4] );
107  const double c = std::sqrt(gamma * p * irho);
108 
109  lambda[0] = u_n;
110  lambda[1] = u_n;
111  lambda[2] = u_n;
112  lambda[3] = u_n + c;
113  lambda[4] = u_n - c;
114 
115  assertion4( lambda[0]==lambda[0], u_n, c, x, normal );
116  assertion4( lambda[1]==lambda[1], u_n, c, x, normal );
117  assertion4( lambda[2]==lambda[2], u_n, c, x, normal );
118  assertion4( lambda[3]==lambda[3], u_n, c, x, normal );
119  assertion4( lambda[4]==lambda[4], u_n, c, x, normal );
120  };
121 
122  auto flux = [](double Q[5], const tarch::la::Vector<DIMENSIONS,double>& x, int normal, double F[5]) -> void {
123  assertion5( Q[0]==Q[0], Q[0], Q[1], Q[2], Q[3], Q[4] );
124  assertion5( Q[1]==Q[1], Q[0], Q[1], Q[2], Q[3], Q[4] );
125  assertion5( Q[2]==Q[2], Q[0], Q[1], Q[2], Q[3], Q[4] );
126  assertion5( Q[3]==Q[3], Q[0], Q[1], Q[2], Q[3], Q[4] );
127  assertion5( Q[4]==Q[4], Q[0], Q[1], Q[2], Q[3], Q[4] );
128 
129  assertion5( Q[0]>1e-12, Q[0], Q[1], Q[2], Q[3], Q[4] );
130  constexpr double gamma = 1.4;
131  const double irho = 1./Q[0];
132  #if DIMENSIONS==3
133  const double p = (gamma-1) * (Q[4] - 0.5*irho*Q[1]*Q[1]+Q[2]*Q[2]+Q[3]*Q[3]);
134  #else
135  const double p = (gamma-1) * (Q[4] - 0.5*irho*Q[1]*Q[1]+Q[2]*Q[2]);
136  #endif
137 
138  switch (normal) {
139  case 0:
140  {
141  F[0] = Q[1];
142  F[1] = irho*Q[1]*Q[1] + p;
143  F[2] = irho*Q[2]*Q[1];
144  F[3] = (DIMENSIONS==3) ? irho*Q[3]*Q[1] : 0.0;
145  F[4] = irho*(Q[4]+p)*Q[1];
146  }
147  break;
148  case 1:
149  {
150  F[0] = Q[2];
151  F[1] = irho*Q[1]*Q[2];
152  F[2] = irho*Q[2]*Q[2] + p;
153  F[3] = (DIMENSIONS==3) ? irho*Q[3]*Q[2] : 0.0;
154  F[4] = irho*(Q[4]+p)*Q[2];
155  }
156  break;
157  case 2:
158  {
159  F[0] = Q[3];
160  F[1] = irho*Q[1]*Q[3];
161  F[2] = irho*Q[2]*Q[3];
162  F[3] = (DIMENSIONS==3) ? irho*Q[3]*Q[3] + p : 0.0;
163  F[4] = irho*(Q[4]+p)*Q[3];
164  }
165  break;
166  }
167 
168  assertion( F[0]==F[0] );
169  assertion( F[1]==F[1] );
170  assertion( F[2]==F[2] );
171  assertion( F[3]==F[3] );
172  assertion( F[4]==F[4] );
173  };
174 
175  auto splitRiemann1d = [&flux, &eigenvalues](double QL[5], double QR[5], const tarch::la::Vector<DIMENSIONS,double>& x, double dx, double dt, int normal, double F[5]) -> void {{
176  double averageQ[5];
177  for (int unknown=0; unknown<5; unknown++) {{
178  averageQ[unknown] = 0.5 * (QL[unknown] + QR[unknown]);
179  assertion4(averageQ[unknown]==averageQ[unknown], x, dx, dt, normal);
180  }}
181 
182  double averageF[5];
183  double lambdas[5];
184  flux(averageQ,x,normal,averageF);
185 
186  double lambdaMax = 0.0;
187  eigenvalues(averageQ,x,normal,lambdas);
188  for (int unknown=0; unknown<5; unknown++) {{
189  assertion(lambdas[unknown]==lambdas[unknown]);
190  lambdaMax = std::max(lambdaMax,lambdas[unknown]);
191  }}
192 
193  for (int unknown=0; unknown<5; unknown++) {{
194  assertion( QR[unknown]==QR[unknown] );
195  assertion( QL[unknown]==QL[unknown] );
196  F[unknown] = averageF[unknown] - 0.5 * lambdaMax * (QR[unknown] - QL[unknown]);
197  assertion9( F[unknown]==F[unknown], averageF[unknown], lambdas[unknown], QR[unknown], QL[unknown], unknown, x, dx, dt, normal );
198  }}
199  }};
200 
201  constexpr int PatchSize = 13;
202  constexpr int HaloSize = 1;
203  double dt = 0.0001;
204  double dx = marker.h()(0) / PatchSize;
205  assertion( dx>=tarch::la::NUMERICAL_ZERO_DIFFERENCE );
206  dfor(cell,PatchSize) {{ // DOFS_PER_AXIS
207  tarch::la::Vector<DIMENSIONS,double> voxelCentre = marker.x()
208  - static_cast<double>((PatchSize/2+HaloSize)) * tarch::la::Vector<DIMENSIONS,double>(dx)
209  + tarch::la::multiplyComponents(cell.convertScalar<double>(), tarch::la::Vector<DIMENSIONS,double>(dx));
210 
211  tarch::la::Vector<DIMENSIONS,int> currentVoxel = cell + tarch::la::Vector<DIMENSIONS,int>(HaloSize); // OVERLAP / Halo layer size
212  int currentVoxelSerialised = peano4::utils::dLinearised(currentVoxel,PatchSize + 2*HaloSize);
213 
214  double accumulatedNumericalFlux[] = { 0.0, 0.0, 0.0, 0.0, 0.0 };
215  double numericalFlux[5];
216  for (int d=0; d<DIMENSIONS; d++) {
217  tarch::la::Vector<DIMENSIONS,int> neighbourVoxel = currentVoxel;
218  tarch::la::Vector<DIMENSIONS,double> x = voxelCentre;
219 
220  neighbourVoxel(d) -= 1;
221  int neighbourVoxelSerialised = peano4::utils::dLinearised(neighbourVoxel,PatchSize + 2*HaloSize);
222  x(d) -= 0.5 * dx;
223  assertion(neighbourVoxel(d)>=0);
224 
225  splitRiemann1d(
226  oldQWithHalo + neighbourVoxelSerialised*5,
227  oldQWithHalo + currentVoxelSerialised*5,
228  x, dx, dt, d,
229  numericalFlux
230  );
231  for (int unknown=0; unknown<5; unknown++) {
232  //accumulatedNumericalFlux[unknown] -= numericalFlux[unknown];
233  accumulatedNumericalFlux[unknown] += numericalFlux[unknown];
234  assertion( numericalFlux[unknown]==numericalFlux[unknown] );
235  assertion( accumulatedNumericalFlux[unknown]==accumulatedNumericalFlux[unknown] );
236  }
237 
238  neighbourVoxel(d) += 2;
239  neighbourVoxelSerialised = peano4::utils::dLinearised(neighbourVoxel,PatchSize + 2*HaloSize);
240  x(d) += 1.0 * dx;
241 
242  splitRiemann1d(
243  oldQWithHalo + currentVoxelSerialised*5,
244  oldQWithHalo + neighbourVoxelSerialised*5,
245  x, dx, dt, d,
246  numericalFlux
247  );
248  for (int unknown=0; unknown<5; unknown++) {
249  //accumulatedNumericalFlux[unknown] += numericalFlux[unknown];
250  accumulatedNumericalFlux[unknown] -= numericalFlux[unknown];
251  assertion( numericalFlux[unknown]==numericalFlux[unknown] );
252  assertion( accumulatedNumericalFlux[unknown]==accumulatedNumericalFlux[unknown] );
253  }
254  }
255 
256  int destinationVoxelSerialised = peano4::utils::dLinearised(cell,PatchSize);
257 
258  for (int unknown=0; unknown<5; unknown++) {{
259  assertion( originalPatch[ destinationVoxelSerialised*5+unknown ]==originalPatch[ destinationVoxelSerialised*5+unknown ] );
260  assertion( accumulatedNumericalFlux[unknown]==accumulatedNumericalFlux[unknown] );
261  originalPatch[ destinationVoxelSerialised*5+unknown ] += dt / dx * accumulatedNumericalFlux[unknown];
262  }}
263  }}
264 """
265 
266 
267 perform_time_step.add_action_set( peano4.toolbox.blockstructured.ReconstructPatchAndApplyFunctor(patch,patch_overlap,functor,"","") )
268 perform_time_step.add_action_set( peano4.toolbox.blockstructured.ProjectPatchOntoFaces(patch,patch_overlap) )
269 # @todo raus
270 perform_time_step.add_action_set( plotter )
271 project.solversteps.add_step(perform_time_step)
272 
273 
274 #
275 # Peano's API does not know which settings to use on the present system. To
276 # make it copy/clone the settings identified by ./configure, we ask it to
277 # parse the generated configuration scripts. The makefile would also offer a
278 # routine to set the dimension. We take the default here.
279 #
280 # @todo Adopt to your particular system settings
281 #
282 project.output.makefile.parse_configure_script_outcome( "../../.." )
283 project.constants.export( "PatchSize", patch_size )
284 project.constants.export( "NumberOfUnknownsPerCell", unknowns )
285 
286 
287 #
288 # Standard triad of operations. You can skip the first two steps if you want as
289 # the script then will automatically invoke the previous steps. The other way
290 # round, it is always admissible to only generate stuff, e.g., but to build and
291 # run the project through a command line
292 #
293 project.generate(peano4.output.Overwrite.Default)
294 project.build()
295 success = project.run( ["myarguments"] )
296 
297 
298 #
299 # Dump grid into VTK
300 #
301 #convert = peano4.visualisation.Convert( "grid-dump" )
302 #convert.set_visualisation_tools_path( "/home/tobias/git/Peano/src/visualisation" )
303 #convert.extract_fine_grid()
304 #convert.convert_to_vtk()
305 
306 
307 if success:
308  convert = peano4.visualisation.Convert( "solution" )
309  convert.set_visualisation_tools_path( "/home/tobias/git/Peano/src/visualisation" )
310  convert.extract_fine_grid()
311  convert.convert_to_vtk()