6from PDE
import max_eigenvalue, flux, source_term
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",
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.
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.
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);
43 Q[Shortcuts::rho] = GROUND_DENSITY * exponentialDistribution;
44 Q[Shortcuts::rhoU + 0] = 0.0;
45 Q[Shortcuts::rhoU + 1] = 0.0;
47 Q[Shortcuts::rhoU + 2] = 0.0;
50 const auto pressure = Q[Shortcuts::rho] * GAS_CONSTANT * TEMPERATURE / MEAN_MOLECULAR_MASS;
51 Q[Shortcuts::rhoE] = pressure / (GAMMA - 1.0);
54boundary_conditions =
"""
55 // normal == 0 => left
56 // normal == 1 => bottom
57 // normal == 2 => right
60 if (normal == 1 || normal == 3) {
61 // positive dy upwards, negative dy downwards
62 const auto dy = (normal == 3 ? h(1) : -h(1));
64 const auto scaleHeight = GAS_CONSTANT * TEMPERATURE / GRAVITY / MEAN_MOLECULAR_MASS;
65 const auto exponentialDistribution = tarch::la::pow(tarch::la::E, -dy / scaleHeight);
67 const auto densityOutside = Qinside[Shortcuts::rho] * exponentialDistribution;
68 const auto pressureOutside = densityOutside * GAS_CONSTANT * TEMPERATURE / MEAN_MOLECULAR_MASS;
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];
74 const auto w = Qinside[Shortcuts::rhoU + 2] / Qinside[Shortcuts::rho];
75 const auto uSq = u * u + v * v + w * w;
77 const auto uSq = u * u + v * v;
80 Qoutside[Shortcuts::rhoE] = pressureOutside / (GAMMA - 1) + 0.5 * densityOutside * uSq;
82 Qoutside[Shortcuts::rhoU + 0] = u * densityOutside;
84 Qoutside[Shortcuts::rhoU + 2] = w * densityOutside;
88 // Reflective at bottom to keep atmosphere
89 // from escaping the domain through the Earth's surface
90 Qoutside[Shortcuts::rhoU + 1] = -v * densityOutside;
93 Qoutside[Shortcuts::rhoU + 1] = v * densityOutside;
96 // outflow in other directions than stratified
97 for (int unknown = 0; unknown < NumberOfUnknowns; ++unknown) {
98 Qoutside[unknown] = Qinside[unknown];
104 auto computeUSq = [] (
105 const {{COMPUTE_PRECISION}}* const NOALIAS Q,
106 {{COMPUTE_PRECISION}}& uSq
108 const int ShortcutRho = 0;
109 const int ShortcutRhoU = 1;
111 const auto u = Q[ShortcutRhoU + 0] / Q[ShortcutRho];
112 const auto v = Q[ShortcutRhoU + 1] / Q[ShortcutRho];
114 const auto w = Q[ShortcutRhoU + 2] / Q[ShortcutRho];
115 uSq = u * u + v * v + w * w;
122calculate_pressure = (
124 auto calculatePressure = [] (
125 const {{COMPUTE_PRECISION}}* const NOALIAS Q,
126 {{COMPUTE_PRECISION}}& pressure,
127 {{COMPUTE_PRECISION}}& uSq
129 const int ShortcutRho = 0;
130 const int ShortcutRhoE = DIMENSIONS + 1;
137 pressure = (GAMMA - 1.0) * (Q[ShortcutRhoE] - 0.5 * Q[ShortcutRho] * uSq);
142calculate_pressure_perturbations = (
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,
150 {{COMPUTE_PRECISION}}& pressurePerturbationLeft,
151 {{COMPUTE_PRECISION}}& pressurePerturbationRight
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
156 const auto densityLeft = QL[0];
157 const auto density = Q[0];
158 const auto densityRight = QR[0];
164 {{COMPUTE_PRECISION}} pressure;
165 {{COMPUTE_PRECISION}} uSq;
166 calculatePressure(Q, pressure, uSq);
168 {{COMPUTE_PRECISION}} pressureLeft;
169 {{COMPUTE_PRECISION}} uSqLeft;
170 calculatePressure(QL, pressureLeft, uSqLeft);
172 {{COMPUTE_PRECISION}} pressureRight;
173 {{COMPUTE_PRECISION}} uSqRight;
174 calculatePressure(QR, pressureRight, uSqRight);
177 const auto reconstructedPressureLeft = pressure + 0.5 * (densityLeft + density) * -GRAVITY * h(normal);
178 const auto reconstructedPressureRight = pressure - 0.5 * (densityRight + density) * -GRAVITY * h(normal);
181 pressurePerturbationLeft = pressureLeft - reconstructedPressureLeft;
182 pressurePerturbationRight = pressureRight - reconstructedPressureRight;
188 calculate_pressure_perturbations
190 {{COMPUTE_PRECISION}} a;
191 {{COMPUTE_PRECISION}} b;
192 if (unknown == 3 && normal == 1) {
193 pressurePerturbations(QL, Q, QR, h, normal, b, a);
195 a = QR[unknown] - Q[unknown];
196 b = Q[unknown] - QL[unknown];
203 if (tarch::la::smaller(tarch::la::abs(a), tarch::la::abs(b))) {
213Required in MUSCL-Hancock before we solve the Riemann Problem.
214-> Not the same as a simple postprocessing step
218 auto reconstructEnergyAtBoundary = [] (
219 const {{COMPUTE_PRECISION}} * const NOALIAS Q,
220 const tarch::la::Vector<DIMENSIONS, double> h,
222 const {{COMPUTE_PRECISION}} slope,
223 {{COMPUTE_PRECISION}}* const NOALIAS extrapolatedBoundaryLeft,
224 {{COMPUTE_PRECISION}}* const NOALIAS extrapolatedBoundaryRight
231 {{COMPUTE_PRECISION}} pressure;
232 {{COMPUTE_PRECISION}} uSq;
233 calculatePressure(Q, pressure, uSq);
235 const int ShortcutRho = 0;
236 const int ShortcutE = DIMENSIONS + 1;
238 const {{COMPUTE_PRECISION}} density = Q[ShortcutRho];
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);
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;
250 // compute boundary uSqs
251 {{COMPUTE_PRECISION}} uSqLeftBoundary;
252 {{COMPUTE_PRECISION}} uSqRightBoundary;
258 computeUSq(extrapolatedBoundaryLeft, uSqLeftBoundary);
259 computeUSq(extrapolatedBoundaryRight, uSqRightBoundary);
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;
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);
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];
281parser = exahype2.ArgumentParser(
"ExaHyPE 2 - Finite Volumes Testing Script")
284 degrees_of_freedom=16,
286args = parser.parse_args()
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)
292fv_solver = exahype2.solvers.fv.musclhancock.GlobalAdaptiveTimeStep(
294 patch_size=args.degrees_of_freedom,
295 unknowns={
"rho": 1,
"rhoU": args.dimensions,
"rhoE": 1},
296 auxiliary_variables=0,
299 time_step_relaxation=0.5,
300 use_enclave_tasking=args.enclave_tasking,
301 number_of_enclave_tasks=args.ntasks,
302 reconstruction=reconstruction,
305fv_solver.set_implementation(
306 initial_conditions=initial_conditions,
307 boundary_conditions=boundary_conditions,
308 max_eigenvalue=max_eigenvalue,
311 riemann_solver=fWave,
320fv_solver.set_implementation(
321 source_term=source_term,
324project = exahype2.Project(
325 namespace=[
"tests",
"exahype2",
"fv"],
326 project_name=
"IsothermalStratification",
328 executable=
"ExaHyPE",
330project.add_solver(fv_solver)
332if args.number_of_snapshots <= 0:
333 time_in_between_plots = 0.0
335 time_in_between_plots = args.end_time / args.number_of_snapshots
336 project.set_output_path(args.output)
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,
347 args.periodic_boundary_conditions_x,
348 args.periodic_boundary_conditions_y,
349 args.periodic_boundary_conditions_z,
353project.set_load_balancer(
354 f
"new ::exahype2::LoadBalancingConfiguration({args.load_balancing_quality}, 1, {args.trees})"
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)