Peano
Loading...
Searching...
No Matches
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#
16import os
17import peano4
18import peano4.datamodel
19import peano4.solversteps
20import peano4.output
21import peano4.visualisation
23
24
25#
26# Lets first clean up all plot files, so we don't get a mismatch
27#
28output_files = [ f for f in os.listdir(".") if f.endswith(".peano-patch-file") ]
29for 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#
37project = 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#
43patch_size = 13
44unknowns = 5
45patch = peano4.datamodel.Patch( (patch_size,patch_size,patch_size), unknowns, "Q" )
46project.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#
53patch_overlap = peano4.datamodel.Patch( (2,patch_size,patch_size), unknowns, "Q" )
54project.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#
62create_grid = peano4.solversteps.Step( "CreateGrid" )
63create_grid.use_face(patch_overlap)
64create_grid.use_cell(patch)
65#create_grid.add_action_set( peano4.toolbox.PlotGridInPeanoBlockFormat("grid-dump", patch) )
66create_grid.add_action_set( peano4.toolbox.blockstructured.ProjectPatchOntoFaces(patch,patch_overlap) )
67project.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
77print_solution = peano4.solversteps.Step( "PlotSolution" )
78print_solution.use_cell(patch)
79print_solution.use_face(patch_overlap)
80print_solution.remove_all_actions()
81plotter = peano4.toolbox.blockstructured.PlotPatchesInPeanoBlockFormat("solution",patch,"Q")
82print_solution.add_action_set( plotter )
83project.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#
91perform_time_step = peano4.solversteps.Step( "TimeStep" )
92perform_time_step.use_cell(patch)
93perform_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.
95functor = """
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
267perform_time_step.add_action_set( peano4.toolbox.blockstructured.ReconstructPatchAndApplyFunctor(patch,patch_overlap,functor,"","") )
268perform_time_step.add_action_set( peano4.toolbox.blockstructured.ProjectPatchOntoFaces(patch,patch_overlap) )
269# @todo raus
270perform_time_step.add_action_set( plotter )
271project.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#
282project.output.makefile.parse_configure_script_outcome( "../../.." )
283project.constants.export( "PatchSize", patch_size )
284project.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#
293project.generate(peano4.output.Overwrite.Default)
294project.build()
295success = 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
307if 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()