Peano
Loading...
Searching...
No Matches
fv-fd-coupling.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
6parser = exahype2.ArgumentParser(
7 "ExaHyPE 2 - Finite Volumes/Finite Differences Coupling Testing Script"
8)
9
10parser.set_defaults(
11 min_depth=4,
12 degrees_of_freedom=16,
13 end_time=5000,
14 periodic_boundary_conditions_x=False,
15 periodic_boundary_conditions_y=False,
16 periodic_boundary_conditions_z=False,
17)
18args = parser.parse_args()
19
20size = [1e6, 1e6, 1e6]
21max_h = 1.1 * min(size) / 3.0**args.min_depth
22min_h = max_h * 3.0 ** (-args.amr_levels)
23
24
27
28euler_unknowns = {
29 "rho": 1,
30 "rhoU": 1,
31 "rhoV": 1,
32}
33if args.dimensions == 3:
34 euler_unknowns["rhoW"] = 1
35euler_unknowns["rhoE"] = 1
36
37euler_initial_conditions = f"""
38 Q[Shortcuts::rho] = 0.1;
39 Q[Shortcuts::rhoU] = 0.0;
40 Q[Shortcuts::rhoV] = 0.0;
41#if DIMENSIONS == 3
42 Q[Shortcuts::rhoW] = 0.0;
43#endif
44 Q[Shortcuts::rhoE] = ((x(0) < {size[0] / 2}) ? (1.01) : (1.0));
45"""
46
47euler_boundary_conditions = """
48 // Reflective boundary conditions
49 Qoutside[Shortcuts::rho] = Qinside[Shortcuts::rho];
50 Qoutside[Shortcuts::rhoU] = -Qinside[Shortcuts::rhoU];
51 Qoutside[Shortcuts::rhoV] = -Qinside[Shortcuts::rhoV];
52#if DIMENSIONS == 3
53 Qoutside[Shortcuts::rhoW] = -Qinside[Shortcuts::rhoW];
54#endif
55 Qoutside[Shortcuts::rhoE] = Qinside[Shortcuts::rhoE];
56"""
57
58compute_primitive_variables = r"""
59 const auto irho = 1.0 / Q[Shortcuts::rho];
60 const auto u0 = Q[Shortcuts::rhoU + 0] * irho;
61 const auto u1 = Q[Shortcuts::rhoU + 1] * irho;
62#if DIMENSIONS == 3
63 const auto u2 = Q[Shortcuts::rhoU + 2] * irho;
64#endif
65
66#if DIMENSIONS == 3
67 const auto uSq = u0 * u0 + u1 * u1 + u2 * u2;
68 const auto u_n = (normal == 0) ? u0 : (normal == 1) ? u1 : u2;
69#else
70 const auto uSq = u0 * u0 + u1 * u1;
71 const auto u_n = (normal == 0) ? u0 : u1;
72#endif
73
74 const auto internalE = Q[Shortcuts::rhoE] - 0.5 * Q[Shortcuts::rho] * uSq;
75 const auto p = (GAMMA - 1.0) * internalE;
76"""
77
78euler_max_eigenvalue = r"""
79 {compute_primitive_variables}
80 const auto speedOfSound = std::sqrt(GAMMA * p * irho);
81 auto result = std::fmax(0.0, std::fabs(u_n - speedOfSound));
82 result = std::fmax(result, std::fabs(u_n + speedOfSound));
83 return result;
84""".format(
85 compute_primitive_variables=compute_primitive_variables
86)
87
88euler_flux = r"""
89 {compute_primitive_variables}
90
91 F[Shortcuts::rho] = Q[Shortcuts::rhoU + normal];
92
93 F[Shortcuts::rhoU + 0] = Q[Shortcuts::rhoU + 0] * u_n;
94 F[Shortcuts::rhoU + 1] = Q[Shortcuts::rhoU + 1] * u_n;
95#if DIMENSIONS == 3
96 F[Shortcuts::rhoU + 2] = Q[Shortcuts::rhoU + 2] * u_n;
97#endif
98
99 F[Shortcuts::rhoU + normal] += p;
100
101 F[Shortcuts::rhoE] = (Q[Shortcuts::rhoE] + p) * u_n;
102""".format(
103 compute_primitive_variables=compute_primitive_variables
104)
105
106euler_solver = exahype2.solvers.fv.godunov.GlobalAdaptiveTimeStep(
107 name="EulerFVSolver",
108 patch_size=args.degrees_of_freedom,
109 unknowns=euler_unknowns,
110 auxiliary_variables=0,
111 min_volume_h=min_h,
112 max_volume_h=max_h,
113 time_step_relaxation=0.5,
114)
115
116euler_solver.set_implementation(
117 initial_conditions=euler_initial_conditions,
118 boundary_conditions=euler_boundary_conditions,
119 flux=euler_flux,
120 max_eigenvalue=euler_max_eigenvalue,
121)
122
123
126
127diffusion_unknowns = {
128 "rhoU": 1,
129 "rhoV": 1,
130}
131if args.dimensions == 3:
132 diffusion_unknowns["rhoW"] = 1
133diffusion_unknowns["rhoE"] = 1
134diffusion_auxiliary_variables = {
135 "rho": 1,
136}
137
138diffusion_initial_conditions = f"""
139 Q[Shortcuts::rhoU] = 0.0;
140 Q[Shortcuts::rhoV] = 0.0;
141#if DIMENSIONS == 3
142 Q[Shortcuts::rhoW] = 0.0;
143#endif
144 Q[Shortcuts::rhoE] = ((x(0) < {size[0] / 2}) ? (1.01) : (1.0));
145 Q[Shortcuts::rho] = 0.1;
146"""
147
148diffusion_boundary_conditions = """
149 // Reflective boundary conditions
150 Qoutside0[Shortcuts::rhoU] = -Qinside[Shortcuts::rhoU];
151 Qoutside0[Shortcuts::rhoV] = -Qinside[Shortcuts::rhoV];
152#if DIMENSIONS == 3
153 Qoutside0[Shortcuts::rhoW] = -Qinside[Shortcuts::rhoW];
154#endif
155 Qoutside0[Shortcuts::rhoE] = Qinside[Shortcuts::rhoE];
156 Qoutside0[Shortcuts::rho] = Qinside[Shortcuts::rho];
157"""
158
159diffusion_stencil = """
160 {{COMPUTE_PRECISION}} velocityLaplacianU = 0.0;
161 {{COMPUTE_PRECISION}} velocityLaplacianV = 0.0;
162#if DIMENSIONS == 3
163 {{COMPUTE_PRECISION}} velocityLaplacianW = 0.0;
164#endif
165
166 assertion(Q[Shortcuts::rho] > 0.0);
167 assertion(Q[Shortcuts::rho] == Q[Shortcuts::rho]);
168 const auto irho = 1.0 / Q[Shortcuts::rho];
169 const auto uSquared = (Q[Shortcuts::rhoU] * irho) * (Q[Shortcuts::rhoU] * irho);
170 const auto vSquared = (Q[Shortcuts::rhoV] * irho) * (Q[Shortcuts::rhoV] * irho);
171#if DIMENSIONS == 3
172 const auto wSquared = (Q[Shortcuts::rhoW] * irho) * (Q[Shortcuts::rhoW] * irho);
173 const auto squaredVelocities = uSquared + vSquared + wSquared;
174#else
175 const auto squaredVelocities = uSquared + vSquared;
176#endif
177
178 // x-direction
179 velocityLaplacianU += ((QRight0[Shortcuts::rhoU] / QRight0[Shortcuts::rho]) - 2 * (Q[Shortcuts::rhoU] * irho) + (QLeft0[Shortcuts::rhoU] / QLeft0[Shortcuts::rho])) / (h(0) * h(0));
180 velocityLaplacianV += ((QRight0[Shortcuts::rhoV] / QRight0[Shortcuts::rho]) - 2 * (Q[Shortcuts::rhoV] * irho) + (QLeft0[Shortcuts::rhoV] / QLeft0[Shortcuts::rho])) / (h(0) * h(0));
181#if DIMENSIONS == 3
182 velocityLaplacianW += ((QRight0[Shortcuts::rhoW] / QRight0[Shortcuts::rho]) - 2 * (Q[Shortcuts::rhoW] * irho) + (QLeft0[Shortcuts::rhoW] / QLeft0[Shortcuts::rho])) / (h(0) * h(0));
183#endif
184
185 // y-direction
186 velocityLaplacianU += ((QTop0[Shortcuts::rhoU] / QTop0[Shortcuts::rho]) - 2 * (Q[Shortcuts::rhoU] * irho) + (QDown0[Shortcuts::rhoU] / QDown0[Shortcuts::rho])) / (h(1) * h(1));
187 velocityLaplacianV += ((QTop0[Shortcuts::rhoV] / QTop0[Shortcuts::rho]) - 2 * (Q[Shortcuts::rhoV] * irho) + (QDown0[Shortcuts::rhoV] / QDown0[Shortcuts::rho])) / (h(1) * h(1));
188#if DIMENSIONS == 3
189 velocityLaplacianW += ((QTop0[Shortcuts::rhoW] / QTop0[Shortcuts::rho]) - 2 * (Q[Shortcuts::rhoW] * irho) + (QDown0[Shortcuts::rhoW] / QDown0[Shortcuts::rho])) / (h(1) * h(1));
190#endif
191
192#if DIMENSIONS == 3
193 // z-direction
194 velocityLaplacianU += ((QFront0[Shortcuts::rhoU] / QFront0[Shortcuts::rho]) - 2 * (Q[Shortcuts::rhoU] * irho) + (QBack0[Shortcuts::rhoU] / QBack0[Shortcuts::rho])) / (h(2) * h(2));
195 velocityLaplacianV += ((QFront0[Shortcuts::rhoV] / QFront0[Shortcuts::rho]) - 2 * (Q[Shortcuts::rhoV] * irho) + (QBack0[Shortcuts::rhoV] / QBack0[Shortcuts::rho])) / (h(2) * h(2));
196 velocityLaplacianW += ((QFront0[Shortcuts::rhoW] / QFront0[Shortcuts::rho]) - 2 * (Q[Shortcuts::rhoW] * irho) + (QBack0[Shortcuts::rhoW] / QBack0[Shortcuts::rho])) / (h(2) * h(2));
197#endif
198
199 QNew[Shortcuts::rhoU] = Q[Shortcuts::rhoU] + dt * Q[Shortcuts::rho] * KINEMATIC_VISCOSITY * velocityLaplacianU;
200 QNew[Shortcuts::rhoV] = Q[Shortcuts::rhoV] + dt * Q[Shortcuts::rho] * KINEMATIC_VISCOSITY * velocityLaplacianV;
201#if DIMENSIONS == 3
202 QNew[Shortcuts::rhoW] = Q[Shortcuts::rhoW] + dt * Q[Shortcuts::rho] * KINEMATIC_VISCOSITY * velocityLaplacianW;
203#endif
204
205 auto temperature = [](const double* Q) {
206 const auto irho = 1 / Q[Shortcuts::rho];
207 // Calculate temperature based on energy density
208 const auto uSquared = (Q[Shortcuts::rhoU] * irho) * (Q[Shortcuts::rhoU] * irho);
209 const auto vSquared = (Q[Shortcuts::rhoV] * irho) * (Q[Shortcuts::rhoV] * irho);
210#if DIMENSIONS == 3
211 const auto wSquared = (Q[Shortcuts::rhoW] * irho) * (Q[Shortcuts::rhoW] * irho);
212 const auto squaredVelocities = uSquared + vSquared + wSquared;
213#else
214 const auto squaredVelocities = uSquared + vSquared;
215#endif
216 const auto internalEnergy = Q[Shortcuts::rhoE] - 0.5 * (Q[Shortcuts::rho] * squaredVelocities);
217 const auto temperature = (internalEnergy * (GAMMA - 1.0)) / (Q[Shortcuts::rho] * UNIVERSAL_GAS_CONSTANT);
218 return temperature;
219};
220
221 {{COMPUTE_PRECISION}} temperatureLaplacian = 0.0;
222 const auto temperatureQ = temperature(Q);
223 const auto doubleTemperatureQ = 2 * temperatureQ;
224
225 // x-direction
226 temperatureLaplacian += (temperature(QRight0) - doubleTemperatureQ + temperature(QLeft0)) / (h(0) * h(0));
227 // y-direction
228 temperatureLaplacian += (temperature(QTop0) - doubleTemperatureQ + temperature(QDown0)) / (h(1) * h(1));
229#if DIMENSIONS == 3
230 // z-direction
231 temperatureLaplacian += (temperature(QFront0) - doubleTemperatureQ + temperature(QBack0)) / (h(2) * h(2));
232#endif
233
234 const auto newPressure = Q[Shortcuts::rho] * UNIVERSAL_GAS_CONSTANT * (temperatureQ + dt * THERMAL_DIFFUSIVITY * temperatureLaplacian);
235 QNew[Shortcuts::rhoE] = newPressure / (GAMMA - 1.0) + 0.5 * Q[Shortcuts::rho] * squaredVelocities;
236 QNew[Shortcuts::rho] = Q[Shortcuts::rho];
237"""
238
239diffusion_solver = exahype2.solvers.fd.GlobalAdaptiveTimeStep(
240 name="DiffusionSolver",
241 patch_size=args.degrees_of_freedom,
242 unknowns=diffusion_unknowns,
243 auxiliary_variables=diffusion_auxiliary_variables,
244 min_h=min_h,
245 max_h=max_h,
246 time_step_relaxation=0.5,
247 solvers_before_in_coupling=[euler_solver],
248 order=1,
249)
250
251diffusion_solver.set_implementation(
252 initial_conditions=diffusion_initial_conditions,
253 boundary_conditions=diffusion_boundary_conditions,
254 solver=diffusion_stencil,
255 max_eigenvalue="""
256 return 1.0;
257""",
258)
259
260
263
264diffusion_solver._compute_time_step_size = (
265 """
266 const double timeStepSize = repositories::"""
267 + euler_solver.get_name_of_global_instance()
268 + """.getAdmissibleTimeStepSize();
269"""
270)
271
272number_of_variables_euler = euler_solver.auxiliary_variables + euler_solver.unknowns
273number_of_variables_diffusion = (
274 diffusion_solver.auxiliary_variables + diffusion_solver.unknowns
275)
276
277# EULER to DIFFUSION
278
279euler_solver.postprocess_updated_patch += f"""// Copy density, energy, and momenta
280 ::toolbox::blockstructured::copyUnknown(
281 {euler_solver.patch_size}, // no of unknowns per dimension
282 fineGridCell{euler_solver.name}Q.data(), // source
283 {euler_solver.variable_names.index("rho")}, // density in source
284 {number_of_variables_euler}, // unknowns in source (incl material parameters)
285 0, // no overlap/halo here
286 fineGridCell{diffusion_solver.name}Q.data(), // dest
287 {diffusion_solver.variable_names.index("rho")}, // density in dest
288 {number_of_variables_diffusion}, // unknowns in dest (incl material parameters)
289 0 // no overlap/halo here
290 );
291
292 ::toolbox::blockstructured::copyUnknown(
293 {euler_solver.patch_size}, // no of unknowns per dimension
294 fineGridCell{euler_solver.name}Q.data(), // source
295 {euler_solver.variable_names.index("rhoU")}, // x-momentum in source
296 {number_of_variables_euler}, // unknowns in source (incl material parameters)
297 0, // no overlap/halo here
298 fineGridCell{diffusion_solver.name}Q.data(), // dest
299 {diffusion_solver.variable_names.index("rhoU")}, // x-momentum in dest
300 {number_of_variables_diffusion}, // unknowns in dest (incl material parameters)
301 0 // no overlap/halo here
302 );
303
304 ::toolbox::blockstructured::copyUnknown(
305 {euler_solver.patch_size}, // no of unknowns per dimension
306 fineGridCell{euler_solver.name}Q.data(), // source
307 {euler_solver.variable_names.index("rhoV")}, // y-momentum in source
308 {number_of_variables_euler}, // unknowns in source (incl material parameters)
309 0, // no overlap/halo here
310 fineGridCell{diffusion_solver.name}Q.data(), // dest
311 {diffusion_solver.variable_names.index("rhoV")}, // y-momentum in dest
312 {number_of_variables_diffusion}, // unknowns in dest (incl material parameters)
313 0 // no overlap/halo here
314 );
315
316 ::toolbox::blockstructured::copyUnknown(
317 {euler_solver.patch_size}, // no of unknowns per dimension
318 fineGridCell{euler_solver.name}Q.data(), // source
319 {euler_solver.variable_names.index("rhoE")}, // energy in source
320 {number_of_variables_euler}, // unknowns in source (incl material parameters)
321 0, // no overlap/halo here
322 fineGridCell{diffusion_solver.name}Q.data(), // dest
323 {diffusion_solver.variable_names.index("rhoE")}, // energy in dest
324 {number_of_variables_diffusion}, // unknowns in dest (incl material parameters)
325 0 // no overlap/halo here
326 );
327"""
328
329if args.dimensions == 3:
330 euler_solver.postprocess_updated_patch += f"""
331#if DIMENSIONS == 3
332 ::toolbox::blockstructured::copyUnknown(
333 {euler_solver.patch_size}, // no of unknowns per dimension
334 fineGridCell{euler_solver.name}Q.data(), // source
335 {euler_solver.variable_names.index("rhoW")}, // z-momentum in source
336 {number_of_variables_euler}, // unknowns in source (incl material parameters)
337 0, // no overlap/halo here
338 fineGridCell{diffusion_solver.name}Q.data(), // dest
339 {diffusion_solver.variable_names.index("rhoW")}, // z-momentum in dest
340 {number_of_variables_diffusion}, // unknowns in dest (incl material parameters)
341 0 // no overlap/halo here
342 );
343#endif
344"""
345
346euler_solver.add_user_action_set_includes('#include "toolbox/blockstructured/Copy.h"\n')
347
348# DIFFUSION to EULER
349
350diffusion_solver.postprocess_updated_patch += f"""// Copy energy and momenta
351 ::toolbox::blockstructured::copyUnknown(
352 {diffusion_solver.patch_size}, // no of unknowns per dimension
353 fineGridCell{diffusion_solver.name}Q.data(), // source
354 {diffusion_solver.variable_names.index("rhoU")}, // x-momentum in source
355 {number_of_variables_diffusion}, // unknowns in source (incl material parameters)
356 0, // no overlap/halo here
357 fineGridCell{euler_solver.name}Q.data(), // dest
358 {euler_solver.variable_names.index("rhoU")}, // x-momentum in dest
359 {number_of_variables_euler}, // unknowns in dest (incl material parameters)
360 0 // no overlap/halo here
361 );
362
363 ::toolbox::blockstructured::copyUnknown(
364 {diffusion_solver.patch_size}, // no of unknowns per dimension
365 fineGridCell{diffusion_solver.name}Q.data(), // source
366 {diffusion_solver.variable_names.index("rhoV")}, // y-momentum in source
367 {number_of_variables_diffusion}, // unknowns in source (incl material parameters)
368 0, // no overlap/halo here
369 fineGridCell{euler_solver.name}Q.data(), // dest
370 {euler_solver.variable_names.index("rhoV")}, // y-momentum in dest
371 {number_of_variables_euler}, // unknowns in dest (incl material parameters)
372 0 // no overlap/halo here
373 );
374
375 ::toolbox::blockstructured::copyUnknown(
376 {diffusion_solver.patch_size}, // no of unknowns per dimension
377 fineGridCell{diffusion_solver.name}Q.data(), // source
378 {diffusion_solver.variable_names.index("rhoE")}, // energy in source
379 {number_of_variables_diffusion}, // unknowns in source (incl material parameters)
380 0, // no overlap/halo here
381 fineGridCell{euler_solver.name}Q.data(), // dest
382 {euler_solver.variable_names.index("rhoE")}, // energy in dest
383 {number_of_variables_euler}, // unknowns in dest (incl material parameters)
384 0 // no overlap/halo here
385 );
386"""
387
388if args.dimensions == 3:
389 diffusion_solver.postprocess_updated_patch += f"""
390#if DIMENSIONS == 3
391 ::toolbox::blockstructured::copyUnknown(
392 {diffusion_solver.patch_size}, // no of unknowns per dimension
393 fineGridCell{diffusion_solver.name}Q.data(), // source
394 {diffusion_solver.variable_names.index("rhoW")}, // z-momentum in source
395 {number_of_variables_diffusion}, // unknowns in source (incl material parameters)
396 0, // no overlap/halo here
397 fineGridCell{euler_solver.name}Q.data(), // dest
398 {euler_solver.variable_names.index("rhoW")}, // z-momentum in dest
399 {number_of_variables_euler}, // unknowns in dest (incl material parameters)
400 0 // no overlap/halo here
401 );
402#endif
403"""
404
405diffusion_solver.add_user_action_set_includes(
406 '#include "toolbox/blockstructured/Copy.h"\n'
407)
408
409euler_solver.last_solver = diffusion_solver
410
411
414
415project = exahype2.Project(
416 namespace=["tests", "exahype2", "fvfd"],
417 project_name=".",
418 directory=".",
419 executable="ExaHyPE",
420)
421
422project.add_solver(euler_solver)
423project.add_solver(diffusion_solver)
424
425if args.number_of_snapshots <= 0:
426 time_in_between_plots = 0.0
427else:
428 time_in_between_plots = args.end_time / args.number_of_snapshots
429 project.set_output_path(args.output)
430
431project.set_global_simulation_parameters(
432 dimensions=args.dimensions,
433 size=size[0 : args.dimensions],
434 offset=[0.0, 0.0, 0.0][0 : args.dimensions],
435 min_end_time=args.end_time,
436 max_end_time=args.end_time,
437 first_plot_time_stamp=0.0,
438 time_in_between_plots=time_in_between_plots,
439 periodic_BC=[
440 args.periodic_boundary_conditions_x,
441 args.periodic_boundary_conditions_y,
442 args.periodic_boundary_conditions_z,
443 ],
444)
445
446project.set_load_balancer("new ::exahype2::LoadBalancingConfiguration")
447project.set_build_mode(mode=peano4.output.string_to_mode(args.build_mode))
448project = project.generate_Peano4_project(verbose=False)
449project.output.makefile.add_CXX_flag("-DGAMMA=1.4")
450project.output.makefile.add_CXX_flag("-DUNIVERSAL_GAS_CONSTANT=8.31")
451project.output.makefile.add_CXX_flag("-DKINEMATIC_VISCOSITY=100")
452project.output.makefile.add_CXX_flag("-DTHERMAL_DIFFUSIVITY=150")
453project.build(make=True, make_clean_first=True, throw_away_data_after_build=True)