Cholla 3.0.1-dev
Cholla - Massively parallel hydro on GPUs
Loading...
Searching...
No Matches
reconstruction_internals.h
1
8#pragma once
9
10// External Includes
11
12// Local Includes
13#include "../global/global.h"
14#include "../global/global_cuda.h"
15#include "../utils/cuda_utilities.h"
16#include "../utils/gpu.hpp"
17#include "../utils/hydro_utilities.h"
18#include "../utils/mhd_utilities.h"
19// #include "../reconstruction/pcm_cuda.h"
20
25namespace reconstruction
26{
27// =====================================================================================================================
36enum Kind {
37 pcm,
38 plmp,
39 plmc,
40 ppmp,
41 ppmc,
42
43#if defined(PCM)
44 chosen = pcm
45#elif defined(PLMP)
46 chosen = plmp
47#elif defined(PLMC)
48 chosen = plmc
49#elif defined(PPMP)
50 chosen = ppmp
51#elif defined(PPMC)
52 chosen = ppmc
53#else
54 #error "no reconstruction selected"
55#endif
56};
57// =====================================================================================================================
58
59// =====================================================================================================================
60struct EigenVecs {
61 Real sound_speed;
62#ifdef MHD
63 Real magnetosonic_speed_fast, magnetosonic_speed_slow, magnetosonic_speed_fast_squared,
64 magnetosonic_speed_slow_squared;
65 Real alpha_fast, alpha_slow;
66 Real beta_y, beta_z;
67 Real n_fs, sign;
69 Real q_fast, q_slow;
70 Real a_fast, a_slow;
72 Real q_prime_fast, q_prime_slow;
73 Real a_prime_fast, a_prime_slow;
74#endif // MHD
75};
76// =====================================================================================================================
77
78// =====================================================================================================================
85 // Hydro variables
86 Real a0, a1, a2, a3, a4;
87
88#ifdef MHD
89 Real a5, a6;
90#endif // MHD
91};
92// =====================================================================================================================
93
94// =====================================================================================================================
108template <int order>
109bool __device__ __host__ __inline__ Thread_Guard(int const &nx, int const &ny, int const &nz, int const &xid,
110 int const &yid, int const &zid)
111{
112 // These checks all make sure that the xid is such that the thread won't try to load any memory that is out of bounds
113
114 // X check
115 bool out_of_bounds_thread = xid < order - 1 or xid >= nx - order;
116
117 // Y check, only used for 2D and 3D
118 if (ny > 1) {
119 out_of_bounds_thread = yid < order - 1 or yid >= ny - order or out_of_bounds_thread;
120 }
121
122 // z check, only used for 3D
123 if (nz > 1) {
124 out_of_bounds_thread = zid < order - 1 or zid >= nz - order or out_of_bounds_thread;
125 }
126 // This is needed in the case that nz == 1 to avoid overrun
127 else {
128 out_of_bounds_thread = zid >= nz or out_of_bounds_thread;
129 }
130
131 return out_of_bounds_thread;
132}
133// =====================================================================================================================
134
135// =====================================================================================================================
152hydro_utilities::Primitive __device__ __host__ __inline__ Load_Data(
153 Real const *dev_conserved, size_t const &xid, size_t const &yid, size_t const &zid, size_t const &nx,
154 size_t const &ny, size_t const &n_cells, size_t const &o1, size_t const &o2, size_t const &o3, Real const &gamma)
155{ // Compute index
156 size_t const id = cuda_utilities::compute1DIndex(xid, yid, zid, nx, ny);
157
158 // Declare the variable we will return
159 hydro_utilities::Primitive loaded_data;
160
161 // Load hydro variables except pressure
162 loaded_data.density = dev_conserved[grid_enum::density * n_cells + id];
163 loaded_data.velocity.x() = dev_conserved[o1 * n_cells + id] / loaded_data.density;
164 loaded_data.velocity.y() = dev_conserved[o2 * n_cells + id] / loaded_data.density;
165 loaded_data.velocity.z() = dev_conserved[o3 * n_cells + id] / loaded_data.density;
166
167 // Load MHD variables. Note that I only need the centered values for the transverse fields except for the initial
168 // computation of the primitive variables
169#ifdef MHD
170 auto magnetic_centered = mhd::utils::cellCenteredMagneticFields(dev_conserved, id, xid, yid, zid, n_cells, nx, ny);
171 switch (o1) {
172 case grid_enum::momentum_x:
173 loaded_data.magnetic.x() = magnetic_centered.x();
174 loaded_data.magnetic.y() = magnetic_centered.y();
175 loaded_data.magnetic.z() = magnetic_centered.z();
176 break;
177 case grid_enum::momentum_y:
178 loaded_data.magnetic.x() = magnetic_centered.y();
179 loaded_data.magnetic.y() = magnetic_centered.z();
180 loaded_data.magnetic.z() = magnetic_centered.x();
181 break;
182 case grid_enum::momentum_z:
183 loaded_data.magnetic.x() = magnetic_centered.z();
184 loaded_data.magnetic.y() = magnetic_centered.x();
185 loaded_data.magnetic.z() = magnetic_centered.y();
186 break;
187 }
188#endif // MHD
189
190// Load pressure accounting for dual energy if enabled
191#ifdef DE // DE
192 Real const energy = dev_conserved[grid_enum::Energy * n_cells + id];
193 Real const gas_energy = dev_conserved[grid_enum::GasEnergy * n_cells + id];
194
196 loaded_data.density, loaded_data.velocity.x(), loaded_data.velocity.y(), loaded_data.velocity.z());
197
198 #ifdef MHD
199 energy_non_thermal +=
200 mhd::utils::computeMagneticEnergy(magnetic_centered.x(), magnetic_centered.y(), magnetic_centered.z());
201 #endif // MHD
202
203 loaded_data.pressure = hydro_utilities::Get_Pressure_From_DE(energy, energy - energy_non_thermal, gas_energy, gamma);
204 loaded_data.gas_energy = gas_energy / loaded_data.density;
205#else // not DE
206 #ifdef MHD
207 loaded_data.pressure = hydro_utilities::Calc_Pressure_Primitive(
208 dev_conserved[grid_enum::Energy * n_cells + id], loaded_data.density, loaded_data.velocity.x(),
209 loaded_data.velocity.y(), loaded_data.velocity.z(), gamma, loaded_data.magnetic.x(), loaded_data.magnetic.y(),
210 loaded_data.magnetic.z());
211 #else // not MHD
212 loaded_data.pressure = hydro_utilities::Calc_Pressure_Primitive(
213 dev_conserved[grid_enum::Energy * n_cells + id], loaded_data.density, loaded_data.velocity.x(),
214 loaded_data.velocity.y(), loaded_data.velocity.z(), gamma);
215 #endif // MHD
216#endif // DE
217
218#ifdef SCALAR
219 for (size_t i = 0; i < grid_enum::nscalars; i++) {
220 loaded_data.scalar[i] = dev_conserved[(grid_enum::scalar + i) * n_cells + id] / loaded_data.density;
221 }
222#endif // SCALAR
223
224 return loaded_data;
225}
226// =====================================================================================================================
227
228// =====================================================================================================================
242template <int reconstruction>
243bool __device__ __host__ __inline__ Riemann_Thread_Guard(size_t const nx, size_t const ny, size_t const nz,
244 size_t const xid, size_t const yid, size_t const zid)
245{
246 int order;
247 if constexpr (reconstruction == reconstruction::Kind::pcm) {
248 order = 1;
249 } else if constexpr (reconstruction == reconstruction::Kind::plmc or reconstruction == reconstruction::Kind::plmp) {
250 order = 3;
251 } else if constexpr (reconstruction == reconstruction::Kind::ppmc or reconstruction == reconstruction::Kind::ppmp) {
252 order = 4;
253 }
254
255 bool out_of_bounds_thread = false;
256 // X check
257 if (nx > 1) {
258 out_of_bounds_thread = xid < order - 1 or xid >= nx - order or out_of_bounds_thread;
259 }
260 // Y check, only used for 2D and 3D
261 if (ny > 1) {
262 out_of_bounds_thread = yid < order - 1 or yid >= ny - order or out_of_bounds_thread;
263 }
264
265 // z check, only used for 3D
266 if (nz > 1) {
267 out_of_bounds_thread = zid < order - 1 or zid >= nz - order or out_of_bounds_thread;
268 }
269 // This is needed in the case that nz == 1 to avoid overrun
270 else {
271 out_of_bounds_thread = zid >= nz or out_of_bounds_thread;
272 }
273
274 return out_of_bounds_thread;
275}
276// =====================================================================================================================
277
278// =====================================================================================================================
287hydro_utilities::Primitive __device__ __host__ __inline__ Compute_Slope(hydro_utilities::Primitive const &left,
288 hydro_utilities::Primitive const &right,
289 Real const &coef = 1.0)
290{
292
293 slopes.density = coef * (right.density - left.density);
294 slopes.velocity.x() = coef * (right.velocity.x() - left.velocity.x());
295 slopes.velocity.y() = coef * (right.velocity.y() - left.velocity.y());
296 slopes.velocity.z() = coef * (right.velocity.z() - left.velocity.z());
297 slopes.pressure = coef * (right.pressure - left.pressure);
298
299#ifdef MHD
300 slopes.magnetic.y() = coef * (right.magnetic.y() - left.magnetic.y());
301 slopes.magnetic.z() = coef * (right.magnetic.z() - left.magnetic.z());
302#endif // MHD
303
304#ifdef DE
305 slopes.gas_energy = coef * (right.gas_energy - left.gas_energy);
306#endif // DE
307
308#ifdef SCALAR
309 for (size_t i = 0; i < grid_enum::nscalars; i++) {
310 slopes.scalar[i] = coef * (right.scalar[i] - left.scalar[i]);
311 }
312#endif // SCALAR
313
314 return slopes;
315}
316// =====================================================================================================================
317
318// =====================================================================================================================
327 hydro_utilities::Primitive const &left_slope, hydro_utilities::Primitive const &right_slope)
328{
330
331 auto Calc_Vl_Slope = [](Real const &left, Real const &right) -> Real {
332 if (left * right > 0.0) {
333 return 2.0 * left * right / (left + right);
334 } else {
335 return 0.0;
336 }
337 };
338
339 vl_slopes.density = Calc_Vl_Slope(left_slope.density, right_slope.density);
340 vl_slopes.velocity.x() = Calc_Vl_Slope(left_slope.velocity.x(), right_slope.velocity.x());
341 vl_slopes.velocity.y() = Calc_Vl_Slope(left_slope.velocity.y(), right_slope.velocity.y());
342 vl_slopes.velocity.z() = Calc_Vl_Slope(left_slope.velocity.z(), right_slope.velocity.z());
343 vl_slopes.pressure = Calc_Vl_Slope(left_slope.pressure, right_slope.pressure);
344
345#ifdef MHD
346 vl_slopes.magnetic.y() = Calc_Vl_Slope(left_slope.magnetic.y(), right_slope.magnetic.y());
347 vl_slopes.magnetic.z() = Calc_Vl_Slope(left_slope.magnetic.z(), right_slope.magnetic.z());
348#endif // MHD
349
350#ifdef DE
351 vl_slopes.gas_energy = Calc_Vl_Slope(left_slope.gas_energy, right_slope.gas_energy);
352#endif // DE
353
354#ifdef SCALAR
355 for (size_t i = 0; i < grid_enum::nscalars; i++) {
356 vl_slopes.scalar[i] = Calc_Vl_Slope(left_slope.scalar[i], right_slope.scalar[i]);
357 }
358#endif // SCALAR
359
360 return vl_slopes;
361}
362// =====================================================================================================================
363
364// =====================================================================================================================
372EigenVecs __device__ __inline__ Compute_Eigenvectors(hydro_utilities::Primitive const &primitive, Real const &gamma)
373{
374 EigenVecs output;
375
376 output.sound_speed = hydro_utilities::Calc_Sound_Speed(primitive.pressure, primitive.density, gamma);
377
378#ifdef MHD
379 // This is taken from Stone et al. 2008, appendix A. Equation numbers will be quoted as relevant
380
381 // Compute wave speeds and their squares
382 output.magnetosonic_speed_fast =
383 mhd::utils::fastMagnetosonicSpeed(primitive.density, primitive.pressure, primitive.magnetic.x(),
384 primitive.magnetic.y(), primitive.magnetic.z(), gamma);
385 output.magnetosonic_speed_slow =
386 mhd::utils::slowMagnetosonicSpeed(primitive.density, primitive.pressure, primitive.magnetic.x(),
387 primitive.magnetic.y(), primitive.magnetic.z(), gamma);
388
389 output.magnetosonic_speed_fast_squared = output.magnetosonic_speed_fast * output.magnetosonic_speed_fast;
390 output.magnetosonic_speed_slow_squared = output.magnetosonic_speed_slow * output.magnetosonic_speed_slow;
391
392 Real const sound_speed_squared = output.sound_speed * output.sound_speed;
393
394 // Compute Alphas (equation A16)
395 if (Real const denom = (output.magnetosonic_speed_fast_squared - output.magnetosonic_speed_slow_squared),
396 numerator_2 = (output.magnetosonic_speed_fast_squared - sound_speed_squared);
397 denom <= 0.0 or numerator_2 <= 0.0) {
398 output.alpha_fast = 1.0;
399 output.alpha_slow = 0.0;
400 } else if (Real const numerator_1 = (sound_speed_squared - output.magnetosonic_speed_slow_squared);
401 numerator_1 <= 0.0) {
402 output.alpha_fast = 0.0;
403 output.alpha_slow = 1.0;
404 } else {
405 output.alpha_fast = sqrt(numerator_1 / denom);
406 output.alpha_slow = sqrt(numerator_2 / denom);
407 }
408
409 // Compute Betas (equation A17). Note that rhypot can return an inf if By and Bz are both zero, the isfinite check
410 // handles that case
411 Real const beta_denom = rhypot(primitive.magnetic.y(), primitive.magnetic.z());
412 output.beta_y = (isfinite(beta_denom)) ? primitive.magnetic.y() * beta_denom : 1.0;
413 output.beta_z = (isfinite(beta_denom)) ? primitive.magnetic.z() * beta_denom : 0.0;
414
415 // Compute Q(s) (equation A14)
416 output.sign = copysign(1.0, primitive.magnetic.x());
417 output.n_fs = 0.5 / sound_speed_squared; // equation A19
418 output.q_prime_fast = output.sign * output.n_fs * output.alpha_fast * output.magnetosonic_speed_fast;
419 output.q_prime_slow = output.sign * output.n_fs * output.alpha_slow * output.magnetosonic_speed_slow;
420 output.q_fast = output.sign * output.alpha_fast * output.magnetosonic_speed_fast;
421 output.q_slow = output.sign * output.alpha_slow * output.magnetosonic_speed_slow;
422
423 // Compute A(s) (equation A15)
424 output.a_fast = output.alpha_fast * output.sound_speed * sqrt(primitive.density);
425 output.a_slow = output.alpha_slow * output.sound_speed * sqrt(primitive.density);
426 output.a_prime_fast = 0.5 * output.alpha_fast / (output.sound_speed * sqrt(primitive.density));
427 output.a_prime_slow = 0.5 * output.alpha_slow / (output.sound_speed * sqrt(primitive.density));
428#endif // MHD
429
430 return output;
431}
432// =====================================================================================================================
433
434// =====================================================================================================================
446 hydro_utilities::Primitive const &primitive_slope,
447 EigenVecs const &eigen, Real const &gamma)
448{
449 Characteristic output;
450
451#ifdef MHD
452 // Multiply the slopes by the left eigenvector matrix given in equation 18
453 Real const inverse_sqrt_density = rsqrt(primitive.density);
454 output.a0 =
455 eigen.n_fs * eigen.alpha_fast *
456 (primitive_slope.pressure / primitive.density -
457 eigen.magnetosonic_speed_fast * primitive_slope.velocity.x()) +
458 eigen.q_prime_slow * (eigen.beta_y * primitive_slope.velocity.y() + eigen.beta_z * primitive_slope.velocity.z()) +
459 eigen.a_prime_slow * (eigen.beta_y * primitive_slope.magnetic.y() + eigen.beta_z * primitive_slope.magnetic.z());
460
461 output.a1 =
462 0.5 * (eigen.beta_y *
463 (primitive_slope.magnetic.z() * eigen.sign * inverse_sqrt_density + primitive_slope.velocity.z()) -
464 eigen.beta_z *
465 (primitive_slope.magnetic.y() * eigen.sign * inverse_sqrt_density + primitive_slope.velocity.y()));
466
467 output.a2 =
468 eigen.n_fs * eigen.alpha_slow *
469 (primitive_slope.pressure / primitive.density -
470 eigen.magnetosonic_speed_slow * primitive_slope.velocity.x()) -
471 eigen.q_prime_fast * (eigen.beta_y * primitive_slope.velocity.y() + eigen.beta_z * primitive_slope.velocity.z()) -
472 eigen.a_prime_fast * (eigen.beta_y * primitive_slope.magnetic.y() + eigen.beta_z * primitive_slope.magnetic.z());
473
474 output.a3 = primitive_slope.density - primitive_slope.pressure / (eigen.sound_speed * eigen.sound_speed);
475
476 output.a4 =
477 eigen.n_fs * eigen.alpha_slow *
478 (primitive_slope.pressure / primitive.density +
479 eigen.magnetosonic_speed_slow * primitive_slope.velocity.x()) +
480 eigen.q_prime_fast * (eigen.beta_y * primitive_slope.velocity.y() + eigen.beta_z * primitive_slope.velocity.z()) -
481 eigen.a_prime_fast * (eigen.beta_y * primitive_slope.magnetic.y() + eigen.beta_z * primitive_slope.magnetic.z());
482 output.a5 =
483 0.5 * (eigen.beta_y *
484 (primitive_slope.magnetic.z() * eigen.sign * inverse_sqrt_density - primitive_slope.velocity.z()) -
485 eigen.beta_z *
486 (primitive_slope.magnetic.y() * eigen.sign * inverse_sqrt_density - primitive_slope.velocity.y()));
487
488 output.a6 =
489 eigen.n_fs * eigen.alpha_fast *
490 (primitive_slope.pressure / primitive.density +
491 eigen.magnetosonic_speed_fast * primitive_slope.velocity.x()) -
492 eigen.q_prime_slow * (eigen.beta_y * primitive_slope.velocity.y() + eigen.beta_z * primitive_slope.velocity.z()) +
493 eigen.a_prime_slow * (eigen.beta_y * primitive_slope.magnetic.y() + eigen.beta_z * primitive_slope.magnetic.z());
494
495#else // not MHD
496 output.a0 = -primitive.density * primitive_slope.velocity.x() / (2.0 * eigen.sound_speed) +
497 primitive_slope.pressure / (2.0 * (eigen.sound_speed * eigen.sound_speed));
498 output.a1 = primitive_slope.density - primitive_slope.pressure / ((eigen.sound_speed * eigen.sound_speed));
499 output.a2 = primitive_slope.velocity.y();
500 output.a3 = primitive_slope.velocity.z();
501 output.a4 = primitive.density * primitive_slope.velocity.x() / (2.0 * eigen.sound_speed) +
502 primitive_slope.pressure / (2.0 * (eigen.sound_speed * eigen.sound_speed));
503#endif // MHD
504
505 return output;
506}
507// =====================================================================================================================
508
509// =====================================================================================================================
521 hydro_utilities::Primitive const &primitive, Characteristic const &characteristic_slope, EigenVecs const &eigen,
522 Real const &gamma)
523{
525#ifdef MHD
526 // Multiply the slopes by the right eigenvector matrix given in equation 12
527 output.density = primitive.density * (eigen.alpha_fast * (characteristic_slope.a0 + characteristic_slope.a6) +
528 eigen.alpha_slow * (characteristic_slope.a2 + characteristic_slope.a4)) +
529 characteristic_slope.a3;
530 output.velocity.x() =
531 eigen.magnetosonic_speed_fast * eigen.alpha_fast * (characteristic_slope.a6 - characteristic_slope.a0) +
532 eigen.magnetosonic_speed_slow * eigen.alpha_slow * (characteristic_slope.a4 - characteristic_slope.a2);
533 output.velocity.y() = eigen.beta_y * (eigen.q_slow * (characteristic_slope.a0 - characteristic_slope.a6) +
534 eigen.q_fast * (characteristic_slope.a4 - characteristic_slope.a2)) +
535 eigen.beta_z * (characteristic_slope.a5 - characteristic_slope.a1);
536 output.velocity.z() = eigen.beta_z * (eigen.q_slow * (characteristic_slope.a0 - characteristic_slope.a6) +
537 eigen.q_fast * (characteristic_slope.a4 - characteristic_slope.a2)) +
538 eigen.beta_y * (characteristic_slope.a1 - characteristic_slope.a5);
539 output.pressure = primitive.density * (eigen.sound_speed * eigen.sound_speed) *
540 (eigen.alpha_fast * (characteristic_slope.a0 + characteristic_slope.a6) +
541 eigen.alpha_slow * (characteristic_slope.a2 + characteristic_slope.a4));
542 output.magnetic.y() =
543 eigen.beta_y * (eigen.a_slow * (characteristic_slope.a0 + characteristic_slope.a6) -
544 eigen.a_fast * (characteristic_slope.a2 + characteristic_slope.a4)) -
545 eigen.beta_z * eigen.sign * sqrt(primitive.density) * (characteristic_slope.a5 + characteristic_slope.a1);
546 output.magnetic.z() =
547 eigen.beta_z * (eigen.a_slow * (characteristic_slope.a0 + characteristic_slope.a6) -
548 eigen.a_fast * (characteristic_slope.a2 + characteristic_slope.a4)) +
549 eigen.beta_y * eigen.sign * sqrt(primitive.density) * (characteristic_slope.a5 + characteristic_slope.a1);
550
551#else // not MHD
552 output.density = characteristic_slope.a0 + characteristic_slope.a1 + characteristic_slope.a4;
553 output.velocity.x() = eigen.sound_speed / primitive.density * (characteristic_slope.a4 - characteristic_slope.a0);
554 output.velocity.y() = characteristic_slope.a2;
555 output.velocity.z() = characteristic_slope.a3;
556 output.pressure = (eigen.sound_speed * eigen.sound_speed) * (characteristic_slope.a0 + characteristic_slope.a4);
557#endif // MHD
558
559 return output;
560}
561// =====================================================================================================================
562
563// =====================================================================================================================
573Real __device__ __host__ __inline__ Van_Leer_Limiter(Real const &left, Real const &right, Real const &centered,
574 Real const &van_leer)
575{
576 if (left * right > 0.0) {
577 Real const lim_slope_a = 2.0 * fmin(fabs(left), fabs(right));
578 Real const lim_slope_b = fmin(fabs(centered), fabs(van_leer));
579 return copysign(fmin(lim_slope_a, lim_slope_b), centered);
580 } else {
581 return 0.0;
582 }
583};
584// =====================================================================================================================
585
586// =====================================================================================================================
597Characteristic __device__ __host__ __inline__ Van_Leer_Limiter(Characteristic const &del_a_L,
598 Characteristic const &del_a_R,
599 Characteristic const &del_a_C,
600 Characteristic const &del_a_G)
601{
602 // the monotonized difference in the characteristic variables
603 Characteristic del_a_m;
604
605 // Monotonize the slopes
606 del_a_m.a0 = Van_Leer_Limiter(del_a_L.a0, del_a_R.a0, del_a_C.a0, del_a_G.a0);
607 del_a_m.a1 = Van_Leer_Limiter(del_a_L.a1, del_a_R.a1, del_a_C.a1, del_a_G.a1);
608 del_a_m.a2 = Van_Leer_Limiter(del_a_L.a2, del_a_R.a2, del_a_C.a2, del_a_G.a2);
609 del_a_m.a3 = Van_Leer_Limiter(del_a_L.a3, del_a_R.a3, del_a_C.a3, del_a_G.a3);
610 del_a_m.a4 = Van_Leer_Limiter(del_a_L.a4, del_a_R.a4, del_a_C.a4, del_a_G.a4);
611
612#ifdef MHD
613 del_a_m.a5 = Van_Leer_Limiter(del_a_L.a5, del_a_R.a5, del_a_C.a5, del_a_G.a5);
614 del_a_m.a6 = Van_Leer_Limiter(del_a_L.a6, del_a_R.a6, del_a_C.a6, del_a_G.a6);
615#endif // MHD
616
617 return del_a_m;
618}
619// =====================================================================================================================
620
621// =====================================================================================================================
633 hydro_utilities::Primitive const &del_R,
634 hydro_utilities::Primitive const &del_C,
635 hydro_utilities::Primitive const &del_G)
636{
637 // the monotonized difference in the primitive variables
639
640 // Monotonize the slopes
641 del_m.density = Van_Leer_Limiter(del_L.density, del_R.density, del_C.density, del_G.density);
642 del_m.velocity.x() = Van_Leer_Limiter(del_L.velocity.x(), del_R.velocity.x(), del_C.velocity.x(), del_G.velocity.x());
643 del_m.velocity.y() = Van_Leer_Limiter(del_L.velocity.y(), del_R.velocity.y(), del_C.velocity.y(), del_G.velocity.y());
644 del_m.velocity.z() = Van_Leer_Limiter(del_L.velocity.z(), del_R.velocity.z(), del_C.velocity.z(), del_G.velocity.z());
645 del_m.pressure = Van_Leer_Limiter(del_L.pressure, del_R.pressure, del_C.pressure, del_G.pressure);
646
647#ifdef MHD
648 del_m.magnetic.y() = Van_Leer_Limiter(del_L.magnetic.y(), del_R.magnetic.y(), del_C.magnetic.y(), del_G.magnetic.y());
649 del_m.magnetic.z() = Van_Leer_Limiter(del_L.magnetic.z(), del_R.magnetic.z(), del_C.magnetic.z(), del_G.magnetic.z());
650#endif // MHD
651
652#ifdef DE
653 del_m.gas_energy = Van_Leer_Limiter(del_L.gas_energy, del_R.gas_energy, del_C.gas_energy, del_G.gas_energy);
654#endif // DE
655#ifdef SCALAR
656 for (int i = 0; i < NSCALARS; i++) {
657 del_m.scalar[i] = Van_Leer_Limiter(del_L.scalar[i], del_R.scalar[i], del_C.scalar[i], del_G.scalar[i]);
658 }
659#endif // SCALAR
660
661 return del_m;
662}
663// =====================================================================================================================
664
665// =====================================================================================================================
676void __device__ __host__ __inline__ Monotonize_Parabolic_Interface(hydro_utilities::Primitive const &cell_i,
677 hydro_utilities::Primitive const &cell_im1,
678 hydro_utilities::Primitive const &cell_ip1,
679 hydro_utilities::Primitive &interface_L_iph,
680 hydro_utilities::Primitive &interface_R_imh)
681{
682 // The function that will actually do the monotozation. Note the return by refernce of the interface state
683 auto Monotonize = [](Real const &state_i, Real const &state_im1, Real const &state_ip1, Real &interface_L,
684 Real &interface_R) {
685 // Some terms we need for the comparisons
686 Real const term_1 = 6.0 * (interface_L - interface_R) * (state_i - 0.5 * (interface_R + interface_L));
687 Real const term_2 = pow(interface_L - interface_R, 2.0);
688
689 // First monotonicity constraint. Equations 47-49 in Stone et al. 2008
690 if ((interface_L - state_i) * (state_i - interface_R) <= 0.0) {
691 interface_L = state_i;
692 interface_R = state_i;
693 }
694 // Second monotonicity constraint. Equations 50 & 51 in Stone et al. 2008
695 else if (term_1 > term_2) {
696 interface_R = 3.0 * state_i - 2.0 * interface_L;
697 }
698 // Third monotonicity constraint. Equations 52 & 53 in Stone et al. 2008
699 else if (term_1 < -term_2) {
700 interface_L = 3.0 * state_i - 2.0 * interface_R;
701 }
702
703 // Bound the interface to lie between adjacent cell centered values
704 interface_R = fmax(fmin(state_i, state_im1), interface_R);
705 interface_R = fmin(fmax(state_i, state_im1), interface_R);
706 interface_L = fmax(fmin(state_i, state_ip1), interface_L);
707 interface_L = fmin(fmax(state_i, state_ip1), interface_L);
708 };
709
710 // Monotonize each interface state
711 Monotonize(cell_i.density, cell_im1.density, cell_ip1.density, interface_L_iph.density, interface_R_imh.density);
712 Monotonize(cell_i.velocity.x(), cell_im1.velocity.x(), cell_ip1.velocity.x(), interface_L_iph.velocity.x(),
713 interface_R_imh.velocity.x());
714 Monotonize(cell_i.velocity.y(), cell_im1.velocity.y(), cell_ip1.velocity.y(), interface_L_iph.velocity.y(),
715 interface_R_imh.velocity.y());
716 Monotonize(cell_i.velocity.z(), cell_im1.velocity.z(), cell_ip1.velocity.z(), interface_L_iph.velocity.z(),
717 interface_R_imh.velocity.z());
718 Monotonize(cell_i.pressure, cell_im1.pressure, cell_ip1.pressure, interface_L_iph.pressure, interface_R_imh.pressure);
719
720#ifdef MHD
721 Monotonize(cell_i.magnetic.y(), cell_im1.magnetic.y(), cell_ip1.magnetic.y(), interface_L_iph.magnetic.y(),
722 interface_R_imh.magnetic.y());
723 Monotonize(cell_i.magnetic.z(), cell_im1.magnetic.z(), cell_ip1.magnetic.z(), interface_L_iph.magnetic.z(),
724 interface_R_imh.magnetic.z());
725#endif // MHD
726
727#ifdef DE
728 Monotonize(cell_i.gas_energy, cell_im1.gas_energy, cell_ip1.gas_energy, interface_L_iph.gas_energy,
729 interface_R_imh.gas_energy);
730#endif // DE
731#ifdef SCALAR
732 for (int i = 0; i < NSCALARS; i++) {
733 Monotonize(cell_i.scalar[i], cell_im1.scalar[i], cell_ip1.scalar[i], interface_L_iph.scalar[i],
734 interface_R_imh.scalar[i]);
735 }
736#endif // SCALAR
737}
738// =====================================================================================================================
739
740// =====================================================================================================================
750 hydro_utilities::Primitive const &primitive, hydro_utilities::Primitive const &slopes, Real const &sign)
751{
753
754 auto interface = [&sign](Real const &state, Real const &slope) -> Real { return state + sign * 0.5 * slope; };
755
756 output.density = interface(primitive.density, slopes.density);
757 output.velocity.x() = interface(primitive.velocity.x(), slopes.velocity.x());
758 output.velocity.y() = interface(primitive.velocity.y(), slopes.velocity.y());
759 output.velocity.z() = interface(primitive.velocity.z(), slopes.velocity.z());
760 output.pressure = interface(primitive.pressure, slopes.pressure);
761
762#ifdef MHD
763 output.magnetic.y() = interface(primitive.magnetic.y(), slopes.magnetic.y());
764 output.magnetic.z() = interface(primitive.magnetic.z(), slopes.magnetic.z());
765#endif // MHD
766
767#ifdef DE
768 output.gas_energy = interface(primitive.gas_energy, slopes.gas_energy);
769#endif // DE
770#ifdef SCALAR
771 for (int i = 0; i < NSCALARS; i++) {
772 output.scalar[i] = interface(primitive.scalar[i], slopes.scalar[i]);
773 }
774#endif // SCALAR
775
776 return output;
777}
778// =====================================================================================================================
779
780// =====================================================================================================================
792 hydro_utilities::Primitive const &cell_i, hydro_utilities::Primitive const &cell_im1,
793 hydro_utilities::Primitive const &slopes_i, hydro_utilities::Primitive const &slopes_im1)
794{
796
797 auto interface = [](Real const &state_i, Real const &state_im1, Real const &slope_i, Real const &slope_im1) -> Real {
798 return 0.5 * (state_i + state_im1) - (slope_i - slope_im1) / 6.0;
799 };
800
801 output.density = interface(cell_i.density, cell_im1.density, slopes_i.density, slopes_im1.density);
802 output.velocity.x() =
803 interface(cell_i.velocity.x(), cell_im1.velocity.x(), slopes_i.velocity.x(), slopes_im1.velocity.x());
804 output.velocity.y() =
805 interface(cell_i.velocity.y(), cell_im1.velocity.y(), slopes_i.velocity.y(), slopes_im1.velocity.y());
806 output.velocity.z() =
807 interface(cell_i.velocity.z(), cell_im1.velocity.z(), slopes_i.velocity.z(), slopes_im1.velocity.z());
808 output.pressure = interface(cell_i.pressure, cell_im1.pressure, slopes_i.pressure, slopes_im1.pressure);
809
810#ifdef MHD
811 output.magnetic.y() =
812 interface(cell_i.magnetic.y(), cell_im1.magnetic.y(), slopes_i.magnetic.y(), slopes_im1.magnetic.y());
813 output.magnetic.z() =
814 interface(cell_i.magnetic.z(), cell_im1.magnetic.z(), slopes_i.magnetic.z(), slopes_im1.magnetic.z());
815#endif // MHD
816
817#ifdef DE
818 output.gas_energy = interface(cell_i.gas_energy, cell_im1.gas_energy, slopes_i.gas_energy, slopes_im1.gas_energy);
819#endif // DE
820#ifdef SCALAR
821 for (int i = 0; i < NSCALARS; i++) {
822 output.scalar[i] = interface(cell_i.scalar[i], cell_im1.scalar[i], slopes_i.scalar[i], slopes_im1.scalar[i]);
823 }
824#endif // SCALAR
825
826 return output;
827}
828// =====================================================================================================================
829
830// =====================================================================================================================
853void __device__ __host__ __inline__ PPM_Single_Variable(Real const &cell_im2, Real const &cell_im1, Real const &cell_i,
854 Real const &cell_ip1, Real const &cell_ip2,
855 Real &interface_L_iph, Real &interface_R_imh)
856{
857 // Let's start by setting up some things that we'll need later
858
859 // Colella & Sekora 2008 constant used in second derivative limiter
860 Real const C2 = 1.25;
861
862 // This lambda function is used for limiting the interfaces
863 auto limit_interface = [&C2](Real const &cell_i, Real const &cell_im1, Real const &interface, Real const &slope_2nd_i,
864 Real const &slope_2nd_im1) -> Real {
865 // Colella et al. 2011 eq. 85b.
866 // 85a is slope_2nd_im1 and 85c is slope_2nd_i
867 Real slope_2nd_centered = 3.0 * (cell_im1 + cell_i - 2.0 * interface);
868
869 Real limited_slope = 0.0;
870 if (SIGN(slope_2nd_centered) == SIGN(slope_2nd_im1) and SIGN(slope_2nd_centered) == SIGN(slope_2nd_i)) {
871 limited_slope = SIGN(slope_2nd_centered) *
872 fmin(C2 * abs(slope_2nd_im1), fmin(C2 * abs(slope_2nd_i), abs(slope_2nd_centered)));
873 }
874
875 // Collela et al. 2011 eq. 84a & 84b
876 Real const diff_left = interface - cell_im1;
877 Real const diff_right = cell_i - interface;
878 if (diff_left * diff_right < 0.0) {
879 // Local extrema detected at the interface
880 return 0.5 * (cell_im1 + cell_i) - limited_slope / 6.0;
881 } else {
882 return interface;
883 }
884 };
885
886 // Now that the setup is done we can start computing the interface states
887
888 // Compute average slopes
889 Real const slope_left = (cell_i - cell_im1);
890 Real const slope_right = (cell_ip1 - cell_i);
891 Real const slope_avg_im1 = 0.5 * slope_left + 0.5 * (cell_im1 - cell_im2);
892 Real const slope_avg_i = 0.5 * slope_right + 0.5 * slope_left;
893 Real const slope_avg_ip1 = 0.5 * (cell_ip2 - cell_ip1) + 0.5 * slope_right;
894
895 // Approximate interface average at i-1/2 and i+1/2 using PPM
896 // P. Colella & P. Woodward 1984 eq. 1.6
897 interface_R_imh = 0.5 * (cell_im1 + cell_i) + (slope_avg_im1 - slope_avg_i) / 6.0;
898 interface_L_iph = 0.5 * (cell_i + cell_ip1) + (slope_avg_i - slope_avg_ip1) / 6.0;
899
900 // Limit interpolated interface states (Colella et al. 2011 section 4.3.1)
901
902 // Approximate second derivative at interfaces for smooth extrema preservation
903 // Colella et al. 2011 eq 85a
904 Real const slope_2nd_im1 = cell_im2 + cell_i - 2.0 * cell_im1;
905 Real const slope_2nd_i = cell_im1 + cell_ip1 - 2.0 * cell_i;
906 Real const slope_2nd_ip1 = cell_i + cell_ip2 - 2.0 * cell_ip1;
907
908 interface_R_imh = limit_interface(cell_i, cell_im1, interface_R_imh, slope_2nd_i, slope_2nd_im1);
909 interface_L_iph = limit_interface(cell_ip1, cell_i, interface_L_iph, slope_2nd_ip1, slope_2nd_i);
910
911 // Compute cell-centered difference stencils (McCorquodale & Colella 2011 section 2.4.1)
912
913 // Apply Colella & Sekora limiters to parabolic interpolant
914 Real slope_2nd_face = 6.0 * (interface_R_imh + interface_L_iph - 2.0 * cell_i);
915
916 Real slope_2nd_limited = 0.0;
917 if (SIGN(slope_2nd_im1) == SIGN(slope_2nd_i) and SIGN(slope_2nd_im1) == SIGN(slope_2nd_ip1) and
918 SIGN(slope_2nd_im1) == SIGN(slope_2nd_face)) {
919 // Extrema is smooth
920 // Colella & Sekora eq. 22
921 slope_2nd_limited = SIGN(slope_2nd_face) * fmin(fmin(C2 * abs(slope_2nd_im1), C2 * abs(slope_2nd_i)),
922 fmin(C2 * abs(slope_2nd_ip1), abs(slope_2nd_face)));
923 }
924
925 // Check if 2nd derivative is close to roundoff error
926 Real cell_max = fmax(abs(cell_im2), abs(cell_im1));
927 cell_max = fmax(cell_max, abs(cell_i));
928 cell_max = fmax(cell_max, abs(cell_ip1));
929 cell_max = fmax(cell_max, abs(cell_ip2));
930
931 // If this condition is true then the limiter is not sensitive to roundoff and we use the limited ratio
932 // McCorquodale & Colella 2011 eq. 27
933 Real const rho = (abs(slope_2nd_face) > (1.0e-12) * cell_max) ? slope_2nd_limited / slope_2nd_face : 0.0;
934
935 // Colella & Sekora eq. 25
936 Real slope_face_left = cell_i - interface_R_imh;
937 Real slope_face_right = interface_L_iph - cell_i;
938
939 // Check for local extrema
940 if ((slope_face_left * slope_face_right) <= 0.0 or ((cell_ip1 - cell_i) * (cell_i - cell_im1)) <= 0.0) {
941 // Extrema detected
942 // Check if relative change in limited 2nd deriv is > roundoff
943 if (rho <= (1.0 - (1.0e-12))) {
944 // Limit smooth extrema
945 // Colella & Sekora eq. 23
946 interface_R_imh = cell_i - rho * slope_face_left;
947 interface_L_iph = cell_i + rho * slope_face_right;
948 }
949 } else {
950 // No extrema detected
951 // Overshoot i-1/2,R / i,(-) state
952 if (abs(slope_face_left) >= 2.0 * abs(slope_face_right)) {
953 interface_R_imh = cell_i - 2.0 * slope_face_right;
954 }
955 // Overshoot i+1/2,L / i,(+) state
956 if (abs(slope_face_right) >= 2.0 * abs(slope_face_left)) {
957 interface_L_iph = cell_i + 2.0 * slope_face_left;
958 }
959 }
960}
961// =====================================================================================================================
962
963// =====================================================================================================================
977void __device__ __host__ __inline__ Write_Data(hydro_utilities::Primitive const &interface_state, Real *dev_interface,
978 Real const *dev_conserved, size_t const &id, size_t const &n_cells,
979 size_t const &o1, size_t const &o2, size_t const &o3, Real const &gamma)
980{
981 // Write out density and momentum
982 dev_interface[grid_enum::density * n_cells + id] = interface_state.density;
983 dev_interface[o1 * n_cells + id] = interface_state.density * interface_state.velocity.x();
984 dev_interface[o2 * n_cells + id] = interface_state.density * interface_state.velocity.y();
985 dev_interface[o3 * n_cells + id] = interface_state.density * interface_state.velocity.z();
986
987#ifdef MHD
988 // Write the Y and Z interface states and load the X magnetic face needed to compute the energy
989 Real magnetic_x;
990 switch (o1) {
991 case grid_enum::momentum_x:
992 dev_interface[grid_enum::Q_x_magnetic_y * n_cells + id] = interface_state.magnetic.y();
993 dev_interface[grid_enum::Q_x_magnetic_z * n_cells + id] = interface_state.magnetic.z();
994 magnetic_x = dev_conserved[grid_enum::magnetic_x * n_cells + id];
995 break;
996 case grid_enum::momentum_y:
997 dev_interface[grid_enum::Q_y_magnetic_z * n_cells + id] = interface_state.magnetic.y();
998 dev_interface[grid_enum::Q_y_magnetic_x * n_cells + id] = interface_state.magnetic.z();
999 magnetic_x = dev_conserved[grid_enum::magnetic_y * n_cells + id];
1000 break;
1001 case grid_enum::momentum_z:
1002 dev_interface[grid_enum::Q_z_magnetic_x * n_cells + id] = interface_state.magnetic.y();
1003 dev_interface[grid_enum::Q_z_magnetic_y * n_cells + id] = interface_state.magnetic.z();
1004 magnetic_x = dev_conserved[grid_enum::magnetic_z * n_cells + id];
1005 break;
1006 }
1007
1008 // Compute the MHD energy
1009 dev_interface[grid_enum::Energy * n_cells + id] = hydro_utilities::Calc_Energy_Primitive(
1010 interface_state.pressure, interface_state.density, interface_state.velocity.x(), interface_state.velocity.y(),
1011 interface_state.velocity.z(), gamma, magnetic_x, interface_state.magnetic.y(), interface_state.magnetic.z());
1012#else // not MHD
1013 // Compute the hydro energy
1014 dev_interface[grid_enum::Energy * n_cells + id] = hydro_utilities::Calc_Energy_Primitive(
1015 interface_state.pressure, interface_state.density, interface_state.velocity.x(), interface_state.velocity.y(),
1016 interface_state.velocity.z(), gamma);
1017#endif // MHD
1018
1019#ifdef DE
1020 dev_interface[grid_enum::GasEnergy * n_cells + id] = interface_state.density * interface_state.gas_energy;
1021#endif // DE
1022#ifdef SCALAR
1023 for (int i = 0; i < NSCALARS; i++) {
1024 dev_interface[(grid_enum::scalar + i) * n_cells + id] = interface_state.density * interface_state.scalar[i];
1025 }
1026#endif // SCALAR
1027}
1028// =====================================================================================================================
1029} // namespace reconstruction
__host__ __device__ Real Calc_Sound_Speed(Real const &E, Real const &d, Real const &mx, Real const &my, Real const &mz, Real const &gamma)
Compute the sound speed in the cell from conserved variables.
Definition hydro_utilities.h:194
__host__ __device__ Real Calc_Kinetic_Energy_From_Velocity(Real const &d, Real const &vx, Real const &vy, Real const &vz)
Compute the kinetic energy from the density and velocities.
Definition hydro_utilities.h:162
Namespace to contain various utilities for the interface reconstruction kernels.
Definition pcm_cuda.h:23
hydro_utilities::Primitive __device__ __host__ Load_Data(Real const *dev_conserved, size_t const &xid, size_t const &yid, size_t const &zid, size_t const &nx, size_t const &ny, size_t const &n_cells, size_t const &o1, size_t const &o2, size_t const &o3, Real const &gamma)
Load the data for reconstruction.
Definition reconstruction_internals.h:152
hydro_utilities::Primitive __device__ __host__ Compute_Van_Leer_Slope(hydro_utilities::Primitive const &left_slope, hydro_utilities::Primitive const &right_slope)
Compute the Van Lear slope from the left and right slopes.
Definition reconstruction_internals.h:326
void __device__ __host__ Write_Data(hydro_utilities::Primitive const &interface_state, Real *dev_interface, Real const *dev_conserved, size_t const &id, size_t const &n_cells, size_t const &o1, size_t const &o2, size_t const &o3, Real const &gamma)
Write the interface data to the appropriate arrays.
Definition reconstruction_internals.h:977
bool __device__ __host__ Thread_Guard(int const &nx, int const &ny, int const &nz, int const &xid, int const &yid, int const &zid)
Determine if a thread is within the allowed range.
Definition reconstruction_internals.h:109
Kind
This enum is used to select which reconstructor to use. The idea is that either one of its implicitly...
Definition reconstruction_internals.h:36
bool __device__ __host__ Riemann_Thread_Guard(size_t const nx, size_t const ny, size_t const nz, size_t const xid, size_t const yid, size_t const zid)
Determine if a thread is within the allowed range.
Definition reconstruction_internals.h:243
hydro_utilities::Primitive __device__ __host__ Compute_Slope(hydro_utilities::Primitive const &left, hydro_utilities::Primitive const &right, Real const &coef=1.0)
Compute a simple slope. Equation is coef * (right - left).
Definition reconstruction_internals.h:287
Characteristic __device__ Primitive_To_Characteristic(hydro_utilities::Primitive const &primitive, hydro_utilities::Primitive const &primitive_slope, EigenVecs const &eigen, Real const &gamma)
Project from the primitive variables slopes to the characteristic variables slopes....
Definition reconstruction_internals.h:445
hydro_utilities::Primitive __device__ __host__ Calc_Interface_Linear(hydro_utilities::Primitive const &primitive, hydro_utilities::Primitive const &slopes, Real const &sign)
Compute the interface state from the slope and cell centered state using linear interpolation.
Definition reconstruction_internals.h:749
Real __device__ __host__ Van_Leer_Limiter(Real const &left, Real const &right, Real const &centered, Real const &van_leer)
Compute the limited slope using the Van Leer limiter.
Definition reconstruction_internals.h:573
hydro_utilities::Primitive __device__ __host__ Calc_Interface_Parabolic(hydro_utilities::Primitive const &cell_i, hydro_utilities::Primitive const &cell_im1, hydro_utilities::Primitive const &slopes_i, hydro_utilities::Primitive const &slopes_im1)
Compute the interface state for the CTU version fo the reconstructor from the slope and cell centered...
Definition reconstruction_internals.h:791
EigenVecs __device__ Compute_Eigenvectors(hydro_utilities::Primitive const &primitive, Real const &gamma)
Compute the eigenvectors in the given cell.
Definition reconstruction_internals.h:372
void __device__ __host__ Monotonize_Parabolic_Interface(hydro_utilities::Primitive const &cell_i, hydro_utilities::Primitive const &cell_im1, hydro_utilities::Primitive const &cell_ip1, hydro_utilities::Primitive &interface_L_iph, hydro_utilities::Primitive &interface_R_imh)
Monotonize the parabolic interface states.
Definition reconstruction_internals.h:676
hydro_utilities::Primitive __device__ __host__ Characteristic_To_Primitive(hydro_utilities::Primitive const &primitive, Characteristic const &characteristic_slope, EigenVecs const &eigen, Real const &gamma)
Project from the characteristic variables slopes to the primitive variables slopes....
Definition reconstruction_internals.h:520
void __device__ __host__ PPM_Single_Variable(Real const &cell_im2, Real const &cell_im1, Real const &cell_i, Real const &cell_ip1, Real const &cell_ip2, Real &interface_L_iph, Real &interface_R_imh)
Compute the PPM interface state for a given field/stencil.
Definition reconstruction_internals.h:853
A data only struct for the primitive variables.
Definition basic_structs.h:124
__device__ __host__ T & x() noexcept
Directly access the x, y, and z elements. Const version is needed if the object instance is declared ...
Definition basic_structs.h:68
A struct for the characteristic variables. We use the same notation as Stone et al....
Definition reconstruction_internals.h:84
Definition reconstruction_internals.h:60