ryujin 2.1.1 revision ee5cbcbf2346c1299c942d0e1f13b46449973c18
Loading...
Searching...
No Matches
wave_speed_estimator.h
Go to the documentation of this file.
1//
2// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3// Copyright (C) 2020 - 2025 by the ryujin authors
4//
5
6#pragma once
7
8#include <compile_time_options.h>
9
10#include "hyperbolic_system.h"
11
12#include <gpu.h>
13#include <newton.h>
14#include <observer_pointer.h>
15#include <simd.h>
16
17#include <deal.II/base/point.h>
18#include <deal.II/base/tensor.h>
19
20// #define DEBUG_WAVE_SPEED_ESTIMATOR
21
22namespace ryujin
23{
24 namespace Euler
25 {
26 template <int dim,
27 typename Number = double,
28 typename MemorySpace = dealii::MemorySpace::Host>
29 class WaveSpeedEstimatorView;
30
40 template <typename ScalarNumber = double>
41 class WaveSpeedEstimator : public dealii::ParameterAcceptor
42 {
43 public:
48
53 struct Parameters {
56 };
57
63 template <int dim,
64 typename Number = double,
65 typename MemorySpace = dealii::MemorySpace::Host>
67
69
73
77 WaveSpeedEstimator(const HyperbolicSystem &hyperbolic_system,
78 const std::string &subsection = "/WaveSpeedEstimator")
79 : ParameterAcceptor(subsection)
80 , parameters_("euler_wave_speed_estimator_parameters",
82 , hyperbolic_system_(&hyperbolic_system)
83 {
84 /*
85 * Note: We bind the parameters directly to the storage held by the
86 * Mirrored object. The corresponding memory is allocated once in
87 * the constructor and never reallocated, and the
88 * implicit_transfers_host_resident policy guarantees that the host
89 * storage is never deallocated: the addresses thus remain valid
90 * for the lifetime of this object.
91 */
92 auto &parameters = *parameters_.view();
93
94 if constexpr (std::is_same<ScalarNumber, double>::value)
95 parameters.newton_tolerance = 1.e-10;
96 else
97 parameters.newton_tolerance = 1.e-4;
98 add_parameter("newton tolerance",
99 parameters.newton_tolerance,
100 "Tolerance for the quadratic newton stopping criterion");
101
102 parameters.newton_max_iterations = 0;
103 add_parameter("newton max iterations",
104 parameters.newton_max_iterations,
105 "Maximal number of quadratic newton iterations performed "
106 "during limiting");
107
108 /*
109 * A parameter file read writes directly through the addresses
110 * bound above and bypasses the view() mechanism. Request a
111 * writable view on the host memory space to invalidate the (now
112 * stale) mirror of the parameters in the default memory space:
113 */
114 ParameterAcceptor::parse_parameters_call_back.connect(
115 [this] { parameters_.view(); });
116 }
117
125 template <int dim,
126 typename Number,
127 typename MemorySpace = dealii::MemorySpace::Host>
128 auto view() const
129 {
131 hyperbolic_system_->template view<dim, Number, MemorySpace>(),
132 *this};
133 }
134
135 private:
137
141
142 Mirrored<Parameters> parameters_;
143
145
149
150 dealii::ObserverPointer<const HyperbolicSystem> hyperbolic_system_;
151
153
154 template <int, typename, typename>
156 };
157
158
167 template <int dim, typename Number, typename MemorySpace>
169 {
170 public:
171 static_assert(
172 std::is_same_v<MemorySpace, dealii::MemorySpace::Host> ||
173 std::is_same_v<MemorySpace, dealii::MemorySpace::Default>,
174 "Unexpected memory space");
175
180
182
184
186
187 using state_type = typename View::state_type;
188
193 static constexpr unsigned int riemann_data_size = 4;
194
199 using primitive_type = std::array<Number, riemann_data_size>;
200
202
204
206
210
216 const View &view,
217 const WaveSpeedEstimator<ScalarNumber> &wave_speed_estimator)
218 : view_(view)
219 , parameters_(
220 wave_speed_estimator.parameters_.template view<MemorySpace>())
221 {
222 }
223
227 DEAL_II_HOST_DEVICE_ALWAYS_INLINE ScalarNumber newton_tolerance() const
228 {
229 return ScalarNumber(parameters_->newton_tolerance);
230 }
231
235 DEAL_II_HOST_DEVICE_ALWAYS_INLINE unsigned int
237 {
238 return parameters_->newton_max_iterations;
239 }
240
246 DEAL_II_HOST_DEVICE Number
247 compute(const primitive_type &riemann_data_i,
248 const primitive_type &riemann_data_j) const;
249
257 DEAL_II_HOST_DEVICE Number
259 const state_type &U_i,
260 const state_type &U_j,
261 const unsigned int i,
262 const unsigned int *js,
263 const dealii::Tensor<1, dim, Number> &n_ij) const;
264
266
267 protected:
272
278 DEAL_II_HOST_DEVICE Number f(const primitive_type &riemann_data,
279 const Number p_star) const;
280
281
287 DEAL_II_HOST_DEVICE Number df(const primitive_type &riemann_data,
288 const Number &p_star) const;
289
290
296 DEAL_II_HOST_DEVICE Number phi(const primitive_type &riemann_data_i,
297 const primitive_type &riemann_data_j,
298 const Number p_in) const;
299
300
306 DEAL_II_HOST_DEVICE Number dphi(const primitive_type &riemann_data_i,
307 const primitive_type &riemann_data_j,
308 const Number &p) const;
309
310
325 DEAL_II_HOST_DEVICE Number
326 phi_of_p_max(const primitive_type &riemann_data_i,
327 const primitive_type &riemann_data_j) const;
328
329
335 DEAL_II_HOST_DEVICE Number lambda1_minus(
336 const primitive_type &riemann_data, const Number p_star) const;
337
338
344 DEAL_II_HOST_DEVICE Number lambda3_plus(
345 const primitive_type &primitive_state, const Number p_star) const;
346
347
358 DEAL_II_HOST_DEVICE std::array<Number, 2>
359 compute_gap(const primitive_type &riemann_data_i,
360 const primitive_type &riemann_data_j,
361 const Number p_1,
362 const Number p_2) const;
363
364
374 DEAL_II_HOST_DEVICE Number
375 compute_lambda(const primitive_type &riemann_data_i,
376 const primitive_type &riemann_data_j,
377 const Number p_star) const;
378
379
388 DEAL_II_HOST_DEVICE Number
389 p_star_two_rarefaction(const primitive_type &riemann_data_i,
390 const primitive_type &riemann_data_j) const;
391
400 DEAL_II_HOST_DEVICE Number
401 p_star_failsafe(const primitive_type &riemann_data_i,
402 const primitive_type &riemann_data_j) const;
403
404
412 DEAL_II_HOST_DEVICE primitive_type
414 const dealii::Tensor<1, dim, Number> &n_ij) const;
415
416 private:
418
422
423 const View view_;
424 const WaveSpeedEstimator<ScalarNumber>::Parameters *const parameters_;
425
427 };
428
429
430 /*
431 * -------------------------------------------------------------------------
432 * Inline definitions
433 * -------------------------------------------------------------------------
434 */
435
436
437 template <int dim, typename Number, typename MemorySpace>
438 DEAL_II_HOST_DEVICE Number
440 const primitive_type &riemann_data_i,
441 const primitive_type &riemann_data_j) const
442 {
443 /*
444 * For exactly solving the Riemann problem we need to start with a
445 * good upper and lower bound, p_1 <= p_star <= p_2, for finding
446 * phi(p_star) == 0. This implies that we have to ensure that
447 * phi(p_2) >= 0 and phi(p_1) <= 0.
448 *
449 * Instead of solving the Riemann problem exactly, however we will
450 * simply use the upper bound p_2 (with p_2 >= p_star) to compute
451 * lambda_max and return the estimate.
452 *
453 * We will use three candidates, p_min, p_max and the two rarefaction
454 * approximation p_star_tilde. We have (up to round-off errors) that
455 * phi(p_star_tilde) >= 0. So this is a safe upper bound, it might
456 * just be too large.
457 *
458 * Depending on the sign of phi(p_max) we select the following ranges:
459 *
460 * phi(p_max) < 0:
461 * p_1 <- p_max and p_2 <- p_star_tilde
462 *
463 * phi(p_max) >= 0:
464 * p_1 <- p_min and p_2 <- min(p_max, p_star_tilde)
465 *
466 * Nota bene:
467 *
468 * - The special case phi(p_max) == 0 as discussed in [1] is already
469 * contained in the second condition.
470 *
471 * - In principle, we would have to treat the case phi(p_min) > 0 as
472 * well. This corresponds to two expansion waves and a good
473 * estimate for the wavespeed is obtained by simply computing
474 * lambda_max with p_2 = 0.
475 *
476 * However, it turns out that numerically in this case we will
477 * have
478 *
479 * 0 < p_star <= p_star_tilde <= p_min <= p_max.
480 *
481 * So it is sufficient to end up with p_2 = p_star_tilde (!!) to
482 * compute the exact same wave speed as for p_2 = 0.
483 *
484 * Note: If for some reason p_star should be computed exactly,
485 * then p_1 has to be set to zero. This can be done efficiently by
486 * simply checking for p_2 < p_1 and setting p_1 <- 0 if
487 * necessary.
488 */
489
490 const auto &[rho_i, u_i, p_i, a_i] = riemann_data_i;
491 const auto &[rho_j, u_j, p_j, a_j] = riemann_data_j;
492
493#ifdef DEBUG_WAVE_SPEED_ESTIMATOR
494 std::cout << "rho_left: " << rho_i << std::endl;
495 std::cout << "u_left: " << u_i << std::endl;
496 std::cout << "p_left: " << p_i << std::endl;
497 std::cout << "a_left: " << a_i << std::endl;
498 std::cout << "rho_right: " << rho_j << std::endl;
499 std::cout << "u_right: " << u_j << std::endl;
500 std::cout << "p_right: " << p_j << std::endl;
501 std::cout << "a_right: " << a_j << std::endl;
502#endif
503
504 const Number p_max = std::max(p_i, p_j);
505
506 const Number rarefaction =
507 p_star_two_rarefaction(riemann_data_i, riemann_data_j);
508 const Number failsafe = p_star_failsafe(riemann_data_i, riemann_data_j);
509 const Number p_star_tilde = std::min(rarefaction, failsafe);
510
511 const Number phi_p_max = phi_of_p_max(riemann_data_i, riemann_data_j);
512
513 Number p_2 =
514 ryujin::compare_and_apply_mask<dealii::SIMDComparison::less_than>(
515 phi_p_max,
516 Number(0.),
517 p_star_tilde,
518 std::min(p_max, p_star_tilde));
519
520#ifdef DEBUG_WAVE_SPEED_ESTIMATOR
521 std::cout << " p^*_tilde = " << p_2 << "\n";
522 std::cout << " phi(p_*_t) = "
523 << phi(riemann_data_i, riemann_data_j, p_2) << std::endl;
524#endif
525
526 /*
527 * If we do no Newton iteration, cut it short:
528 */
529
530 if (newton_max_iterations() == 0) {
531 const auto lambda_max =
532 compute_lambda(riemann_data_i, riemann_data_j, p_2);
533
534#ifdef DEBUG_WAVE_SPEED_ESTIMATOR
535 std::cout << "-> lambda_max = " << lambda_max << std::endl;
536#endif
537 return lambda_max;
538 }
539
540 /*
541 * Compute p_1 and ensure that p_1 < p_2. If we hit a case with two
542 * expansions we might indeed have that p_star_tilde < p_1. Set p_1 =
543 * p_2 in this case.
544 */
545
546 const Number p_min = std::min(riemann_data_i[2], riemann_data_j[2]);
547
548 Number p_1 =
549 ryujin::compare_and_apply_mask<dealii::SIMDComparison::less_than>(
550 phi_p_max, Number(0.), p_max, p_min);
551
553 dealii::SIMDComparison::less_than_or_equal>(p_1, p_2, p_1, p_2);
554
555 /*
556 * Step 2: Perform quadratic Newton iteration.
557 *
558 * See [1], p. 915f (4.8) and (4.9)
559 */
560
561 auto [gap, lambda_max] =
562 compute_gap(riemann_data_i, riemann_data_j, p_1, p_2);
563
564#ifdef DEBUG_WAVE_SPEED_ESTIMATOR
565 std::cout << std::fixed << std::setprecision(16);
566 std::cout << "p_1: (start) " << p_1 << std::endl;
567 std::cout << "p_2: (start) " << p_2 << std::endl;
568 std::cout << "gap: (start) " << gap << std::endl;
569 std::cout << "l_m: (start) " << lambda_max << std::endl;
570#endif
571
572 for (unsigned int i = 0; i < newton_max_iterations(); ++i) {
573
574 /* We accept our current guess if we reach the tolerance... */
575 const Number tolerance(newton_tolerance());
576 if (std::max(Number(0.), gap - tolerance) == Number(0.)) {
577#ifdef DEBUG_WAVE_SPEED_ESTIMATOR
578 std::cout << "converged after " << i << " iterations." << std::endl;
579#endif
580 break;
581 }
582
583 // FIXME: Fuse these computations:
584 const Number phi_p_1 = phi(riemann_data_i, riemann_data_j, p_1);
585 const Number phi_p_2 = phi(riemann_data_i, riemann_data_j, p_2);
586 const Number dphi_p_1 = dphi(riemann_data_i, riemann_data_j, p_1);
587 const Number dphi_p_2 = dphi(riemann_data_i, riemann_data_j, p_2);
588
589 quadratic_newton_step(p_1, p_2, phi_p_1, phi_p_2, dphi_p_1, dphi_p_2);
590
591 /* Update lambda_max and gap: */
592 auto [gap_new, lambda_max_new] =
593 compute_gap(riemann_data_i, riemann_data_j, p_1, p_2);
594 gap = gap_new;
595 lambda_max = lambda_max_new;
596
597#ifdef DEBUG_WAVE_SPEED_ESTIMATOR
598 std::cout << "phi_p_1: " << phi_p_1 << std::endl;
599 std::cout << "phi_p_2: " << phi_p_2 << std::endl;
600 std::cout << "dphi_p_1: " << dphi_p_1 << std::endl;
601 std::cout << "dphi_p_2: " << dphi_p_2 << std::endl;
602 std::cout << "p_1: ( " << i << " ) " << p_1 << std::endl;
603 std::cout << "p_2: ( " << i << " ) " << p_2 << std::endl;
604 std::cout << "gap: " << gap << std::endl;
605 std::cout << "l_m: " << lambda_max << std::endl;
606#endif
607 }
608
609#ifdef DEBUG_WAVE_SPEED_ESTIMATOR
610 std::cout << "-> lambda_max = " << lambda_max << std::endl;
611#endif
612
613 return lambda_max;
614 }
615
616
617 template <int dim, typename Number, typename MemorySpace>
618 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number
620 const PrecomputedVectorView & /*pv*/,
621 const state_type &U_i,
622 const state_type &U_j,
623 const unsigned int /*i*/,
624 const unsigned int * /*js*/,
625 const dealii::Tensor<1, dim, Number> &n_ij) const
626 {
627 const auto riemann_data_i = riemann_data_from_state(U_i, n_ij);
628 const auto riemann_data_j = riemann_data_from_state(U_j, n_ij);
629
630 return compute(riemann_data_i, riemann_data_j);
631 }
632
633
634 template <int dim, typename Number, typename MemorySpace>
635 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number
637 const primitive_type &riemann_data, const Number p_star) const
638 {
639 const auto &gamma = view_.gamma();
640
641 const auto &[rho, u, p, a] = riemann_data;
642
643 const Number Az = ScalarNumber(2.) / (rho * (gamma + Number(1.)));
644 const Number Bz =
645 (gamma - ScalarNumber(1.)) / (gamma + ScalarNumber(1.)) * p;
646 const Number radicand = Az / (p_star + Bz);
647 const Number true_value = (p_star - p) * std::sqrt(radicand);
648
649 const auto exponent =
650 ScalarNumber(0.5) * (gamma - ScalarNumber(1.)) / gamma;
651 const Number factor = ryujin::pow(p_star / p, exponent) - Number(1.);
652 const auto false_value =
653 ScalarNumber(2.) * a * factor / (gamma - ScalarNumber(1.));
654
656 dealii::SIMDComparison::greater_than_or_equal>(
657 p_star, p, true_value, false_value);
658 }
659
660
661 template <int dim, typename Number, typename MemorySpace>
662 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number
664 const primitive_type &riemann_data, const Number &p_star) const
665 {
667 const auto &gamma = view_.gamma();
668 const auto &gamma_inverse = view_.gamma_inverse();
669 const auto &gamma_minus_one_inverse = view_.gamma_minus_one_inverse();
670 const auto &gamma_plus_one_inverse = view_.gamma_plus_one_inverse();
671
672 const auto &[rho, u, p, a] = riemann_data;
673
674 const Number radicand_inverse = ScalarNumber(0.5) * rho *
675 ((gamma + ScalarNumber(1.)) * p_star +
676 (gamma - ScalarNumber(1.)) * p);
677 const Number denominator =
678 (p_star + (gamma - ScalarNumber(1.)) * gamma_plus_one_inverse * p);
679 const Number true_value =
680 (denominator - ScalarNumber(0.5) * (p_star - p)) /
681 (denominator * std::sqrt(radicand_inverse));
682
683 const auto exponent =
684 (ScalarNumber(-1.) - gamma) * ScalarNumber(0.5) * gamma_inverse;
685 const Number factor = (gamma - ScalarNumber(1.)) * ScalarNumber(0.5) *
686 gamma_inverse * ryujin::pow(p_star / p, exponent) /
687 p;
688 const auto false_value =
689 factor * ScalarNumber(2.) * a * gamma_minus_one_inverse;
690
692 dealii::SIMDComparison::greater_than_or_equal>(
693 p_star, p, true_value, false_value);
694 }
695
696
697 template <int dim, typename Number, typename MemorySpace>
698 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number
700 const primitive_type &riemann_data_i,
701 const primitive_type &riemann_data_j,
702 const Number p_in) const
703 {
704 const Number &u_i = riemann_data_i[1];
705 const Number &u_j = riemann_data_j[1];
706
707 return f(riemann_data_i, p_in) + f(riemann_data_j, p_in) + u_j - u_i;
708 }
709
710
711 template <int dim, typename Number, typename MemorySpace>
712 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number
714 const primitive_type &riemann_data_i,
715 const primitive_type &riemann_data_j,
716 const Number &p) const
717 {
718 return df(riemann_data_i, p) + df(riemann_data_j, p);
719 }
720
721
722 /*
723 * The approximate Riemann solver is based on a function phi(p) that is
724 * montone increasing in p, concave down and whose (weak) third
725 * derivative is non-negative and locally bounded [1, p. 912]. Because we
726 * actually do not perform any iteration for computing our wavespeed
727 * estimate we can get away by only implementing a specialized variant of
728 * the phi function that computes phi(p_max). It inlines the
729 * implementation of the "f" function and eliminates all unnecessary
730 * branches in "f".
731 *
732 * Cost: 0x pow, 2x division, 2x sqrt
733 */
734 template <int dim, typename Number, typename MemorySpace>
735 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number
737 const primitive_type &riemann_data_i,
738 const primitive_type &riemann_data_j) const
739 {
740 const auto &gamma = view_.gamma();
741
742 const auto &[rho_i, u_i, p_i, a_i] = riemann_data_i;
743 const auto &[rho_j, u_j, p_j, a_j] = riemann_data_j;
744
745 const Number p_max = std::max(p_i, p_j);
746
747 const Number radicand_inverse_i = ScalarNumber(0.5) * rho_i *
748 ((gamma + ScalarNumber(1.)) * p_max +
749 (gamma - ScalarNumber(1.)) * p_i);
750
751 const Number value_i = (p_max - p_i) / std::sqrt(radicand_inverse_i);
752
753 const Number radicand_inverse_j = ScalarNumber(0.5) * rho_j *
754 ((gamma + ScalarNumber(1.)) * p_max +
755 (gamma - ScalarNumber(1.)) * p_j);
756
757 const Number value_j = (p_max - p_j) / std::sqrt(radicand_inverse_j);
758
759 return value_i + value_j + u_j - u_i;
760 }
761
762
763 /*
764 * Next we construct approximations for the two extreme wave speeds of
765 * the Riemann fan [1, p. 912, (3.7) + (3.8)] and compute an upper bound
766 * lambda_max of the maximal wave speed:
767 */
768
769
770 /*
771 * see [1], page 912, (3.7)
772 *
773 * Cost: 0x pow, 1x division, 1x sqrt
774 */
775 template <int dim, typename Number, typename MemorySpace>
776 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number
778 const primitive_type &riemann_data, const Number p_star) const
779 {
780 const auto &gamma = view_.gamma();
781 const auto &gamma_inverse = view_.gamma_inverse();
782 const auto factor =
783 (gamma + ScalarNumber(1.0)) * ScalarNumber(0.5) * gamma_inverse;
784
785 const auto &[rho, u, p, a] = riemann_data;
786 const auto inv_p = ScalarNumber(1.0) / p;
787
788 const Number tmp = positive_part((p_star - p) * inv_p);
789
790 return u - a * std::sqrt(ScalarNumber(1.0) + factor * tmp);
791 }
792
793
794 /*
795 * see [1], page 912, (3.8)
796 *
797 * Cost: 0x pow, 1x division, 1x sqrt
798 */
799 template <int dim, typename Number, typename MemorySpace>
800 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number
802 const primitive_type &primitive_state, const Number p_star) const
803 {
804 const auto &gamma = view_.gamma();
805 const auto &gamma_inverse = view_.gamma_inverse();
806 const Number factor =
807 (gamma + ScalarNumber(1.0)) * ScalarNumber(0.5) * gamma_inverse;
808
809 const auto &[rho, u, p, a] = primitive_state;
810 const auto inv_p = ScalarNumber(1.0) / p;
811
812 const Number tmp = positive_part((p_star - p) * inv_p);
813 return u + a * std::sqrt(Number(1.0) + factor * tmp);
814 }
815
816
826 template <int dim, typename Number, typename MemorySpace>
827 DEAL_II_HOST_DEVICE_ALWAYS_INLINE std::array<Number, 2>
829 const std::array<Number, 4> &riemann_data_i,
830 const std::array<Number, 4> &riemann_data_j,
831 const Number p_1,
832 const Number p_2) const
833 {
834 const Number nu_11 = lambda1_minus(riemann_data_i, p_2 /*SIC!*/);
835 const Number nu_12 = lambda1_minus(riemann_data_i, p_1 /*SIC!*/);
836
837 const Number nu_31 = lambda3_plus(riemann_data_j, p_1);
838 const Number nu_32 = lambda3_plus(riemann_data_j, p_2);
839
840 const Number lambda_max =
841 std::max(positive_part(nu_32), negative_part(nu_11));
842
843 const Number gap =
844 std::max(std::abs(nu_32 - nu_31), std::abs(nu_12 - nu_11));
845
846 return {{gap, lambda_max}};
847 }
848
849
850 /*
851 * For two given primitive states <code>riemann_data_i</code> and
852 * <code>riemann_data_j</code>, and a guess p_2, compute an upper bound
853 * for lambda.
854 *
855 * This is the same lambda_max as computed by compute_gap. The function
856 * simply avoids a number of unnecessary computations (in case we do
857 * not need to know the gap).
858 *
859 * Cost: 0x pow, 2x division, 2x sqrt
860 */
861 template <int dim, typename Number, typename MemorySpace>
862 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number
864 const primitive_type &riemann_data_i,
865 const primitive_type &riemann_data_j,
866 const Number p_star) const
867 {
868 const Number nu_11 = lambda1_minus(riemann_data_i, p_star);
869 const Number nu_32 = lambda3_plus(riemann_data_j, p_star);
870
871 return std::max(positive_part(nu_32), negative_part(nu_11));
872 }
873
874
875 /*
876 * Two-rarefaction approximation to p_star computed for two primitive
877 * states <code>riemann_data_i</code> and <code>riemann_data_j</code>.
878 *
879 * See [1], page 914, (4.3)
880 *
881 * Cost: 2x pow, 2x division, 0x sqrt
882 */
883 template <int dim, typename Number, typename MemorySpace>
884 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number
886 const primitive_type &riemann_data_i,
887 const primitive_type &riemann_data_j) const
888 {
889 const auto &gamma = view_.gamma();
890 const auto &gamma_inverse = view_.gamma_inverse();
891 const auto &gamma_minus_one_inverse = view_.gamma_minus_one_inverse();
892
893 const auto &[rho_i, u_i, p_i, a_i] = riemann_data_i;
894 const auto &[rho_j, u_j, p_j, a_j] = riemann_data_j;
895 const auto inv_p_j = ScalarNumber(1.) / p_j;
896
897 /*
898 * Nota bene (cf. [1, (4.3)]):
899 * a_Z^0 * sqrt(1 - b * rho_Z) = a_Z * (1 - b * rho_Z)
900 * We have computed a_Z already, so we are simply going to use this
901 * identity below:
902 */
903
904 const auto factor = (gamma - ScalarNumber(1.)) * ScalarNumber(0.5);
905
906 /*
907 * Nota bene (cf. [1, (3.6)]: The condition "numerator > 0" is the
908 * well-known non-vacuum condition. In case we encounter numerator <= 0
909 * then p_star = 0 is the correct pressure to compute the wave speed.
910 * Therefore, all we have to do is to take the positive part of the
911 * expression:
912 */
913
914 const Number numerator = positive_part(a_i + a_j - factor * (u_j - u_i));
915 const Number denominator =
916 a_i * ryujin::pow(p_i * inv_p_j, -factor * gamma_inverse) + a_j;
917
918 const auto exponent = ScalarNumber(2.0) * gamma * gamma_minus_one_inverse;
919
920 const auto p_1_tilde =
921 p_j * ryujin::pow(numerator / denominator, exponent);
922
923#ifdef DEBUG_WAVE_SPEED_ESTIMATOR
924 std::cout << "p_star_two_rarefaction = " << p_1_tilde << std::endl;
925#endif
926 return p_1_tilde;
927 }
928
929
930 /*
931 * Failsafe approximation to p_star computed for two primitive
932 * states <code>riemann_data_i</code> and <code>riemann_data_j</code>.
933 *
934 * See [1], page 914, (4.3)
935 *
936 * Cost: 2x pow, 2x division, 0x sqrt
937 */
938 template <int dim, typename Number, typename MemorySpace>
939 DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number
941 const primitive_type &riemann_data_i,
942 const primitive_type &riemann_data_j) const
943 {
944 const auto &gamma = view_.gamma();
945
946 const auto &[rho_i, u_i, p_i, a_i] = riemann_data_i;
947 const auto &[rho_j, u_j, p_j, a_j] = riemann_data_j;
948
949 /*
950 * Compute (5.11) formula for \tilde p_2^\ast:
951 *
952 * Cost: 0x pow, 3x division, 3x sqrt
953 */
954
955 const Number p_max = std::max(p_i, p_j);
956
957 Number radicand_i = ScalarNumber(2.) * p_max;
958 radicand_i /=
959 rho_i * ((gamma + Number(1.)) * p_max + (gamma - Number(1.)) * p_i);
960
961 const Number x_i = std::sqrt(radicand_i);
962
963 Number radicand_j = ScalarNumber(2.) * p_max;
964 radicand_j /=
965 rho_j * ((gamma + Number(1.)) * p_max + (gamma - Number(1.)) * p_j);
966
967 const Number x_j = std::sqrt(radicand_j);
968
969 const Number a = x_i + x_j;
970 const Number b = u_j - u_i;
971 const Number c = -p_i * x_i - p_j * x_j;
972
973 const Number base = (-b + std::sqrt(b * b - ScalarNumber(4.) * a * c)) /
974 (ScalarNumber(2.) * a);
975 const Number p_2_tilde = base * base;
976
977#ifdef DEBUG_WAVE_SPEED_ESTIMATOR
978 std::cout << "p_star_failsafe = " << p_2_tilde << std::endl;
979#endif
980 return p_2_tilde;
981 }
982
983
984 template <int dim, typename Number, typename MemorySpace>
985 DEAL_II_HOST_DEVICE_ALWAYS_INLINE auto
987 const state_type &U, const dealii::Tensor<1, dim, Number> &n_ij) const
989 {
990 const auto rho = view_.density(U);
991 const auto rho_inverse = Number(1.0) / rho;
992
993 const auto m = view_.momentum(U);
994 const auto proj_m = n_ij * m;
995 const auto perp = m - proj_m * n_ij;
996
997 const auto E = view_.total_energy(U) -
998 Number(0.5) * perp.norm_square() * rho_inverse;
999
1000 /*
1001 * Compute the pressure and speed of sound of the projected
1002 * one-dimensional state [rho, proj_m, E]:
1003 */
1004 const auto gamma = view_.gamma();
1005 const auto internal_energy =
1006 E - ScalarNumber(0.5) * (proj_m * proj_m) * rho_inverse;
1007 const auto p = (gamma - ScalarNumber(1.)) * internal_energy;
1008 const auto a = std::sqrt(gamma * p * rho_inverse);
1009
1010 return {{rho, proj_m * rho_inverse, p, a}};
1011 }
1012 } // namespace Euler
1013} // namespace ryujin
Vectors::MultiComponentVectorView< ScalarNumber, n_precomputed_values, dealii::VectorizedArray< ScalarNumber >::size(), MemorySpace, false > PrecomputedVectorView
dealii::Tensor< 1, problem_dimension, Number > state_type
std::array< Number, n_precomputed_values > precomputed_type
static constexpr unsigned int problem_dimension
typename get_value_type< Number >::type ScalarNumber
HyperbolicSystemView< dim, Number, MemorySpace > View
DEAL_II_HOST_DEVICE Number compute_lambda(const primitive_type &riemann_data_i, const primitive_type &riemann_data_j, const Number p_star) const
typename View::PrecomputedVectorView PrecomputedVectorView
std::array< Number, riemann_data_size > primitive_type
DEAL_II_HOST_DEVICE Number compute(const primitive_type &riemann_data_i, const primitive_type &riemann_data_j) const
DEAL_II_HOST_DEVICE Number phi(const primitive_type &riemann_data_i, const primitive_type &riemann_data_j, const Number p_in) const
DEAL_II_HOST_DEVICE primitive_type riemann_data_from_state(const state_type &U, const dealii::Tensor< 1, dim, Number > &n_ij) const
typename View::precomputed_type precomputed_type
DEAL_II_HOST_DEVICE Number p_star_failsafe(const primitive_type &riemann_data_i, const primitive_type &riemann_data_j) const
DEAL_II_HOST_DEVICE_ALWAYS_INLINE ScalarNumber newton_tolerance() const
DEAL_II_HOST_DEVICE Number lambda1_minus(const primitive_type &riemann_data, const Number p_star) const
DEAL_II_HOST_DEVICE std::array< Number, 2 > compute_gap(const primitive_type &riemann_data_i, const primitive_type &riemann_data_j, const Number p_1, const Number p_2) const
DEAL_II_HOST_DEVICE Number lambda3_plus(const primitive_type &primitive_state, const Number p_star) const
DEAL_II_HOST_DEVICE Number p_star_two_rarefaction(const primitive_type &riemann_data_i, const primitive_type &riemann_data_j) const
static constexpr unsigned int riemann_data_size
WaveSpeedEstimatorView(const View &view, const WaveSpeedEstimator< ScalarNumber > &wave_speed_estimator)
DEAL_II_HOST_DEVICE Number dphi(const primitive_type &riemann_data_i, const primitive_type &riemann_data_j, const Number &p) const
DEAL_II_HOST_DEVICE Number phi_of_p_max(const primitive_type &riemann_data_i, const primitive_type &riemann_data_j) const
DEAL_II_HOST_DEVICE_ALWAYS_INLINE unsigned int newton_max_iterations() const
DEAL_II_HOST_DEVICE Number df(const primitive_type &riemann_data, const Number &p_star) const
DEAL_II_HOST_DEVICE Number f(const primitive_type &riemann_data, const Number p_star) const
WaveSpeedEstimator(const HyperbolicSystem &hyperbolic_system, const std::string &subsection="/WaveSpeedEstimator")
TransferPolicy
Definition gpu.h:88
DEAL_II_HOST_DEVICE_ALWAYS_INLINE void quadratic_newton_step(Number &p_1, Number &p_2, const Number phi_p_1, const Number phi_p_2, const Number dphi_p_1, const Number dphi_p_2, const Number sign=Number(1.0))
Definition newton.h:39
DEAL_II_HOST_DEVICE T pow(const T x, const T b)
DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number positive_part(const Number number)
Definition simd.h:149
DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number negative_part(const Number number)
Definition simd.h:161
DEAL_II_HOST_DEVICE_ALWAYS_INLINE Number compare_and_apply_mask(const Number &left, const Number &right, const Number &true_value, const Number &false_value)
Definition simd.h:176