Line data Source code
1 1 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : /// \file
5 : /// Defines function lift_flux.
6 :
7 : #pragma once
8 :
9 : #include <cstddef>
10 : #include <utility>
11 :
12 : #include "DataStructures/DataBox/PrefixHelpers.hpp"
13 : #include "DataStructures/DataVector.hpp"
14 : #include "DataStructures/Tensor/Tensor.hpp"
15 : #include "DataStructures/Variables.hpp"
16 : #include "NumericalAlgorithms/Spectral/Basis.hpp"
17 : #include "Utilities/Gsl.hpp"
18 : #include "Utilities/TMPL.hpp"
19 :
20 : namespace dg {
21 : /// @{
22 : /// \ingroup DiscontinuousGalerkinGroup
23 : /// \brief Lifts the flux contribution from an interface to the volume.
24 : ///
25 : /// The lifting operation takes the (d-1)-dimensional flux term at the
26 : /// interface and computes the corresponding d-dimensional term in the
27 : /// volume. SpECTRE implements an efficient DG method in which each
28 : /// interface grid point contributes only to that same grid point of the
29 : /// volume.
30 : ///
31 : /// \details
32 : /// SpECTRE implements a DG method with a diagonalized mass matrix (also
33 : /// known as a mass-lumping scheme). This choice gives a large
34 : /// reduction in the computational cost of the lifting operation, however,
35 : /// the scheme is slightly less accurate, especially when the grid is
36 : /// deformed by non-trivial Jacobians. For more details on the
37 : /// diagonalization of the mass matrix and its implications,
38 : /// \cite Teukolsky2015ega, especially Section 3.
39 : ///
40 : /// This function is implemented to handle Legendre, Chebyshev, and Zernike
41 : /// bases.
42 : ///
43 : /// \note The result is still provided only on the boundary grid. The
44 : /// values away from the boundary are zero and are not stored.
45 : template <typename... BoundaryCorrectionTags>
46 1 : void lift_flux(
47 : const gsl::not_null<Variables<tmpl::list<BoundaryCorrectionTags...>>*>
48 : boundary_correction_terms,
49 : const size_t extent_perpendicular_to_boundary,
50 : const Scalar<DataVector>& magnitude_of_face_normal,
51 : const Spectral::Basis basis = Spectral::Basis::Legendre) {
52 : if (basis == Spectral::Basis::Legendre) {
53 : // For an Nth degree basis (i.e., one with N+1 basis functions), the LGL
54 : // weights are:
55 : // w_i = 2 / ((N + 1) * N * (P_{N}(xi_i))^2)
56 : // and so at the end points (xi = +/- 1) we get:
57 : // w_0 = 2 / ((N + 1) * N)
58 : // w_N = 2 / ((N + 1) * N)
59 : // The negative sign comes from bringing the boundary correction over to the
60 : // RHS of the equal sign (e.g. `du/dt=Source - div Flux - boundary corr`),
61 : // while the magnitude of the normal vector above accounts for the ratios of
62 : // spatial metrics and Jacobians.
63 : *boundary_correction_terms *=
64 : -0.5 *
65 : static_cast<double>((extent_perpendicular_to_boundary *
66 : (extent_perpendicular_to_boundary - 1))) *
67 : get(magnitude_of_face_normal);
68 : } else if (basis == Spectral::Basis::Chebyshev) {
69 : // Chebyshev with GaussLobotto, with N grid points, has the weight at
70 : // the upper and lower sides
71 : // w_0 = 1 / ((N - 1)^2 - a)
72 : // w_N = 1 / ((N - 1)^2 - a)
73 : // where a = 0 if N is even, else it is 1
74 : // The negative sign follows from the Legendre discussion
75 : *boundary_correction_terms *=
76 : -static_cast<double>((extent_perpendicular_to_boundary - 1) *
77 : (extent_perpendicular_to_boundary - 1) -
78 : extent_perpendicular_to_boundary % 2) *
79 : get(magnitude_of_face_normal);
80 : } else if (basis == Spectral::Basis::ZernikeB1) {
81 : // ZernikeB1 with GaussRadauUpper, with N grid points, has the weight at
82 : // the upper side
83 : // w_N = 1 / (N * (2 * N - 1))
84 : // The negative sign follows from the Legendre discussion
85 : *boundary_correction_terms *=
86 : -1.0 *
87 : static_cast<double>((extent_perpendicular_to_boundary *
88 : (2 * extent_perpendicular_to_boundary - 1))) *
89 : get(magnitude_of_face_normal);
90 : } else if (basis == Spectral::Basis::ZernikeB2) {
91 : // ZernikeB2 with GaussRadauUpper, with N grid points, has the weight at
92 : // the upper side
93 : // w_N = 1 / (2 * N^2)
94 : // The negative sign follows from the Legendre discussion
95 : *boundary_correction_terms *=
96 : -1.0 *
97 : static_cast<double>((2 * extent_perpendicular_to_boundary *
98 : extent_perpendicular_to_boundary)) *
99 : get(magnitude_of_face_normal);
100 : } else if (basis == Spectral::Basis::ZernikeB3) {
101 : // ZernikeB3 with GaussRadauUpper, with N grid points, has the weight at
102 : // the upper side
103 : // w_N = 1 / (N * (2 * N + 1))
104 : // The negative sign follows from the Legendre discussion
105 : *boundary_correction_terms *=
106 : -1.0 *
107 : static_cast<double>((extent_perpendicular_to_boundary *
108 : (2 * extent_perpendicular_to_boundary + 1))) *
109 : get(magnitude_of_face_normal);
110 : } else {
111 : ERROR("Got unexpected basis " << basis);
112 : }
113 : }
114 :
115 : template <typename... FluxTags>
116 1 : auto lift_flux(Variables<tmpl::list<FluxTags...>> flux,
117 : const size_t extent_perpendicular_to_boundary,
118 : const Scalar<DataVector>& magnitude_of_face_normal,
119 : const Spectral::Basis basis = Spectral::Basis::Legendre)
120 : -> Variables<tmpl::list<db::remove_tag_prefix<FluxTags>...>> {
121 : Variables<tmpl::list<db::remove_tag_prefix<FluxTags>...>> lifted_data(
122 : std::move(flux));
123 : lift_flux(make_not_null(&lifted_data), extent_perpendicular_to_boundary,
124 : magnitude_of_face_normal, basis);
125 : return lifted_data;
126 : }
127 : /// @}
128 : } // namespace dg
|