Peano
Loading...
Searching...
No Matches
isothermal-stratification.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 exahype2
5
6from PDE import max_eigenvalue, flux, source_term
7from FWave import fWave
8
9"""
10@article{WellBalancedness,
11 author = {{Käppeli, R.} and {Mishra, S.}},
12 title = {A well-balanced finite volume scheme for the Euler equations with gravitation - The exact preservation of hydrostatic equilibrium with arbitrary entropy stratification},
13 DOI= "10.1051/0004-6361/201527815",
14 url= "https://doi.org/10.1051/0004-6361/201527815",
15 journal = {A&A},
16 year = 2016,
17 volume = 587,
18 pages = "A94",
19 month = "",
20}
21
22This artificial scenario describes a stratified atmosphere by ensuring that an exponentially decreasing density profile
23is exactly balanced with an exponentially decreasing pressure gradient.
24Therefore, no updates should occur over the duration of the simulation and nothing should change in the density
25and pressure profiles. This scenario can be used to test the well-balancedness of the underlying numerical scheme
26such that it accurately captures the initial steady state. This is particularly difficult for higher order methods,
27such as the MUSCL-Hancock scheme.
28
29The reconstruction proposed by Käppeli and Mishra aims at retaining an exponentially decaying pressure gradient
30within the MUSCL-Hancock boundary extrapolation phase. To this end, they detail first- and second-order
31pressure reconstruction techniques, which they find to capture the initial stratified steady state up to
32machine precision in a Runge-Kutta scheme. While the first-order accurate pressure reconstruction is found to work
33in this scenario, the necessary second-order reconstruction, which limits pressure perturbations after the initial
34time step t = 0 to t != 0, results in unphysical updates.
35In general, any sort of custom reconstruction of the volume boundary values using the limited slopes and original
36values can be implemented with the newly introduced reconstruction argument of the MUSCL-Hancock solver.
37"""
38
39initial_conditions = """
40 const auto scaleHeight = (GAS_CONSTANT * TEMPERATURE) / GRAVITY / MEAN_MOLECULAR_MASS;
41 const auto exponentialDistribution = tarch::la::pow(tarch::la::E, -x(1) / scaleHeight);
42
43 Q[Shortcuts::rho] = GROUND_DENSITY * exponentialDistribution;
44 Q[Shortcuts::rhoU + 0] = 0.0;
45 Q[Shortcuts::rhoU + 1] = 0.0;
46#if DIMENSIONS == 3
47 Q[Shortcuts::rhoU + 2] = 0.0;
48#endif
49
50 const auto pressure = Q[Shortcuts::rho] * GAS_CONSTANT * TEMPERATURE / MEAN_MOLECULAR_MASS;
51 Q[Shortcuts::rhoE] = pressure / (GAMMA - 1.0);
52"""
53
54boundary_conditions = """
55 // normal == 0 => left
56 // normal == 1 => bottom
57 // normal == 2 => right
58 // normal == 3 => top
59
60 if (normal == 1 || normal == 3) {
61 // positive dy upwards, negative dy downwards
62 const auto dy = (normal == 3 ? h(1) : -h(1));
63
64 const auto scaleHeight = GAS_CONSTANT * TEMPERATURE / GRAVITY / MEAN_MOLECULAR_MASS;
65 const auto exponentialDistribution = tarch::la::pow(tarch::la::E, -dy / scaleHeight);
66
67 const auto densityOutside = Qinside[Shortcuts::rho] * exponentialDistribution;
68 const auto pressureOutside = densityOutside * GAS_CONSTANT * TEMPERATURE / MEAN_MOLECULAR_MASS;
69
70 Qoutside[Shortcuts::rho] = densityOutside;
71 const auto u = Qinside[Shortcuts::rhoU + 0] / Qinside[Shortcuts::rho];
72 const auto v = Qinside[Shortcuts::rhoU + 1] / Qinside[Shortcuts::rho];
73#if DIMENSIONS == 3
74 const auto w = Qinside[Shortcuts::rhoU + 2] / Qinside[Shortcuts::rho];
75 const auto uSq = u * u + v * v + w * w;
76#else
77 const auto uSq = u * u + v * v;
78#endif
79
80 Qoutside[Shortcuts::rhoE] = pressureOutside / (GAMMA - 1) + 0.5 * densityOutside * uSq;
81
82 Qoutside[Shortcuts::rhoU + 0] = u * densityOutside;
83#if DIMENSIONS == 3
84 Qoutside[Shortcuts::rhoU + 2] = w * densityOutside;
85#endif
86
87 if (normal == 1) {
88 // Reflective at bottom to keep atmosphere
89 // from escaping the domain through the Earth's surface
90 Qoutside[Shortcuts::rhoU + 1] = -v * densityOutside;
91 } else {
92 // Outflow at top
93 Qoutside[Shortcuts::rhoU + 1] = v * densityOutside;
94 }
95 } else {
96 // outflow in other directions than stratified
97 for (int unknown = 0; unknown < NumberOfUnknowns; ++unknown) {
98 Qoutside[unknown] = Qinside[unknown];
99 }
100 }
101"""
102
103compute_usq = """
104 auto computeUSq = [] (
105 const {{COMPUTE_PRECISION}}* const NOALIAS Q,
106 {{COMPUTE_PRECISION}}& uSq
107 ) {
108 const int ShortcutRho = 0;
109 const int ShortcutRhoU = 1;
110
111 const auto u = Q[ShortcutRhoU + 0] / Q[ShortcutRho];
112 const auto v = Q[ShortcutRhoU + 1] / Q[ShortcutRho];
113 #if DIMENSIONS == 3
114 const auto w = Q[ShortcutRhoU + 2] / Q[ShortcutRho];
115 uSq = u * u + v * v + w * w;
116 #else
117 uSq = u * u + v * v;
118 #endif
119 };
120"""
121
122calculate_pressure = (
123 """
124 auto calculatePressure = [] (
125 const {{COMPUTE_PRECISION}}* const NOALIAS Q,
126 {{COMPUTE_PRECISION}}& pressure,
127 {{COMPUTE_PRECISION}}& uSq
128 ) {
129 const int ShortcutRho = 0;
130 const int ShortcutRhoE = DIMENSIONS + 1;
131
132 """
133 + compute_usq
134 + """
135
136 computeUSq(Q, uSq);
137 pressure = (GAMMA - 1.0) * (Q[ShortcutRhoE] - 0.5 * Q[ShortcutRho] * uSq);
138 };
139"""
140)
141
142calculate_pressure_perturbations = (
143 """
144 auto pressurePerturbations = [] (
145 const {{COMPUTE_PRECISION}}* const NOALIAS QL,
146 const {{COMPUTE_PRECISION}}* const NOALIAS Q,
147 const {{COMPUTE_PRECISION}}* const NOALIAS QR,
148 const tarch::la::Vector<DIMENSIONS, double> h,
149 const int normal,
150 {{COMPUTE_PRECISION}}& pressurePerturbationLeft,
151 {{COMPUTE_PRECISION}}& pressurePerturbationRight
152 ) {
153 /**
154 * Following "A well-balanced finite volume scheme for the Euler equations with gravitation", R. Käppeli and S. Mishra, https://doi.org/10.1051/0004-6361/201527815
155 */
156 const auto densityLeft = QL[0];
157 const auto density = Q[0];
158 const auto densityRight = QR[0];
159
160 """
161 + calculate_pressure
162 + """
163
164 {{COMPUTE_PRECISION}} pressure;
165 {{COMPUTE_PRECISION}} uSq;
166 calculatePressure(Q, pressure, uSq);
167
168 {{COMPUTE_PRECISION}} pressureLeft;
169 {{COMPUTE_PRECISION}} uSqLeft;
170 calculatePressure(QL, pressureLeft, uSqLeft);
171
172 {{COMPUTE_PRECISION}} pressureRight;
173 {{COMPUTE_PRECISION}} uSqRight;
174 calculatePressure(QR, pressureRight, uSqRight);
175
176 // Eq. 26
177 const auto reconstructedPressureLeft = pressure + 0.5 * (densityLeft + density) * -GRAVITY * h(normal);
178 const auto reconstructedPressureRight = pressure - 0.5 * (densityRight + density) * -GRAVITY * h(normal);
179
180 // Eq. 25
181 pressurePerturbationLeft = pressureLeft - reconstructedPressureLeft;
182 pressurePerturbationRight = pressureRight - reconstructedPressureRight;
183 };
184"""
185)
186
187limiter = (
188 calculate_pressure_perturbations
189 + """
190 {{COMPUTE_PRECISION}} a;
191 {{COMPUTE_PRECISION}} b;
192 if (unknown == 3 && normal == 1) {
193 pressurePerturbations(QL, Q, QR, h, normal, b, a);
194 } else {
195 a = QR[unknown] - Q[unknown];
196 b = Q[unknown] - QL[unknown];
197 }
198
199 // Minmod
200 if (a * b <= 0.0) {
201 return 0.0;
202 } else {
203 if (tarch::la::smaller(tarch::la::abs(a), tarch::la::abs(b))) {
204 return a;
205 } else {
206 return b;
207 }
208 }
209"""
210)
211
212"""
213Required in MUSCL-Hancock before we solve the Riemann Problem.
214-> Not the same as a simple postprocessing step
215"""
216reconstruction = (
217 """
218 auto reconstructEnergyAtBoundary = [] (
219 const {{COMPUTE_PRECISION}} * const NOALIAS Q,
220 const tarch::la::Vector<DIMENSIONS, double> h,
221 const int normal,
222 const {{COMPUTE_PRECISION}} slope,
223 {{COMPUTE_PRECISION}}* const NOALIAS extrapolatedBoundaryLeft,
224 {{COMPUTE_PRECISION}}* const NOALIAS extrapolatedBoundaryRight
225 ) {
226
227 """
228 + calculate_pressure
229 + """
230
231 {{COMPUTE_PRECISION}} pressure;
232 {{COMPUTE_PRECISION}} uSq;
233 calculatePressure(Q, pressure, uSq);
234
235 const int ShortcutRho = 0;
236 const int ShortcutE = DIMENSIONS + 1;
237
238 const {{COMPUTE_PRECISION}} density = Q[ShortcutRho];
239
240 // Eq. 16 Käppeli and Mishra
241 const auto reconstructedPressureLeftBoundary = pressure + 0.5 * density * -GRAVITY * h(normal);
242 const auto reconstructedPressureRightBoundary = pressure - 0.5 * density * -GRAVITY * h(normal);
243
244 // Eq. 27 Käppeli and Mishra
245 // Following p_{1,i}(x_i) = p_i - p_{0,i}(x_i) = 0, all pressure perturbations are zero,
246 // so we just add the perturbation slopes to the reconstructed pressure
247 const auto pressureLeftBoundary = reconstructedPressureLeftBoundary - 0.5 * h(normal) * slope;
248 const auto pressureRightBoundary = reconstructedPressureRightBoundary + 0.5 * h(normal) * slope;
249
250 // compute boundary uSqs
251 {{COMPUTE_PRECISION}} uSqLeftBoundary;
252 {{COMPUTE_PRECISION}} uSqRightBoundary;
253
254 """
255 + compute_usq
256 + """
257
258 computeUSq(extrapolatedBoundaryLeft, uSqLeftBoundary);
259 computeUSq(extrapolatedBoundaryRight, uSqRightBoundary);
260
261 // convert pressure back to energy
262 extrapolatedBoundaryLeft[ShortcutE] = pressureLeftBoundary / (GAMMA - 1.0) + 0.5 * extrapolatedBoundaryLeft[ShortcutRho] * uSqLeftBoundary;
263 extrapolatedBoundaryRight[ShortcutE] = pressureRightBoundary / (GAMMA - 1.0) + 0.5 * extrapolatedBoundaryRight[ShortcutRho] * uSqRightBoundary;
264 };
265
266 if (unknown == 3 && normal == 1) {
267 reconstructEnergyAtBoundary(QInGathered, volumeSize, normal, slopeGathered[normal][unknown], extrapolatedBoundaryLeft, extrapolatedBoundaryRight);
268 reconstructEnergyAtBoundary(QInGatheredLeft, volumeSize, normal, slopeGatheredLeft[normal][unknown], extrapolatedBoundaryLeftNeighborLeftBoundary, extrapolatedBoundaryLeftNeighborRightBoundary);
269 reconstructEnergyAtBoundary(QInGatheredRight, volumeSize, normal, slopeGatheredRight[normal][unknown], extrapolatedBoundaryRightNeighborLeftBoundary, extrapolatedBoundaryRightNeighborRightBoundary);
270 } else {
271 extrapolatedBoundaryLeft[unknown] = QInGathered[unknown] - 0.5 * volumeSize(normal) * slopeGathered[normal][unknown];
272 extrapolatedBoundaryRight[unknown] = QInGathered[unknown] + 0.5 * volumeSize(normal) * slopeGathered[normal][unknown];
273 extrapolatedBoundaryLeftNeighborLeftBoundary[unknown] = QInGatheredLeft[unknown] - 0.5 * volumeSize(normal) * slopeGatheredLeft[normal][unknown];
274 extrapolatedBoundaryLeftNeighborRightBoundary[unknown] = QInGatheredLeft[unknown] + 0.5 * volumeSize(normal) * slopeGatheredLeft[normal][unknown];
275 extrapolatedBoundaryRightNeighborLeftBoundary[unknown] = QInGatheredRight[unknown] - 0.5 * volumeSize(normal) * slopeGatheredRight[normal][unknown];
276 extrapolatedBoundaryRightNeighborRightBoundary[unknown] = QInGatheredRight[unknown] + 0.5 * volumeSize(normal) * slopeGatheredRight[normal][unknown];
277 }
278"""
279)
280
281parser = exahype2.ArgumentParser("ExaHyPE 2 - Finite Volumes Testing Script")
282parser.set_defaults(
283 min_depth=6,
284 degrees_of_freedom=16,
285)
286args = parser.parse_args()
287
288size = [40_000, 40_000, 40_000]
289max_h = 1.1 * min(size) / (3.0**args.min_depth)
290min_h = max_h * 3.0 ** (-args.amr_levels)
291
292fv_solver = exahype2.solvers.fv.musclhancock.GlobalAdaptiveTimeStep(
293 name="FVSolver",
294 patch_size=args.degrees_of_freedom,
295 unknowns={"rho": 1, "rhoU": args.dimensions, "rhoE": 1},
296 auxiliary_variables=0,
297 min_volume_h=min_h,
298 max_volume_h=max_h,
299 time_step_relaxation=0.5,
300 use_enclave_tasking=args.enclave_tasking,
301 number_of_enclave_tasks=args.ntasks,
302 reconstruction=reconstruction, # Specific to MUSCL-Hancock
303)
304
305fv_solver.set_implementation(
306 initial_conditions=initial_conditions,
307 boundary_conditions=boundary_conditions,
308 max_eigenvalue=max_eigenvalue,
309 flux=flux,
310 limiter=limiter,
311 riemann_solver=fWave,
312)
313
314
320fv_solver.set_implementation(
321 source_term=source_term,
322)
323
324project = exahype2.Project(
325 namespace=["tests", "exahype2", "fv"],
326 project_name="IsothermalStratification",
327 directory=".",
328 executable="ExaHyPE",
329)
330project.add_solver(fv_solver)
331
332if args.number_of_snapshots <= 0:
333 time_in_between_plots = 0.0
334else:
335 time_in_between_plots = args.end_time / args.number_of_snapshots
336 project.set_output_path(args.output)
337
338project.set_global_simulation_parameters(
339 dimensions=args.dimensions,
340 size=size[0 : args.dimensions],
341 offset=[0.0, 0.0, 0.0][0 : args.dimensions],
342 min_end_time=args.end_time,
343 max_end_time=args.end_time,
344 first_plot_time_stamp=90.0,
345 time_in_between_plots=time_in_between_plots,
346 periodic_BC=[
347 args.periodic_boundary_conditions_x,
348 args.periodic_boundary_conditions_y,
349 args.periodic_boundary_conditions_z,
350 ],
351)
352
353project.set_load_balancer(
354 f"new ::exahype2::LoadBalancingConfiguration({args.load_balancing_quality}, 1, {args.trees})"
355)
356project.set_build_mode(mode=peano4.output.string_to_mode(args.build_mode))
357project = project.generate_Peano4_project(verbose=False)
358project.output.makefile.set_target_device(args.target_device)
359project.output.makefile.add_CXX_flag("-DGAMMA=1.4")
360project.output.makefile.add_CXX_flag("-DGRAVITY=9.81")
361project.output.makefile.add_CXX_flag("-DGAS_CONSTANT=8.31")
362project.output.makefile.add_CXX_flag("-DTEMPERATURE=299")
363project.output.makefile.add_CXX_flag("-DGROUND_DENSITY=1.166")
364project.output.makefile.add_CXX_flag("-DMEAN_MOLECULAR_MASS=0.0289647")
365project.build(make=True, make_clean_first=True, throw_away_data_after_build=True)