Theseus
Compressible flow solver
Loading...
Searching...
No Matches
RoeFlux.hpp
Go to the documentation of this file.
1// Copyright (c) 2025-2026 Board of Trustees of the University of Illinois
2//
3// This file is part of Theseus.
4//
5// SPDX-License-Identifier: BSD-3-Clause
6#pragma once
7
8#include "mfem.hpp"
10#include "theseus_kernels.hpp"
11
12namespace Theseus
13{
14 namespace RoeFlux
15 {
16 // The split-form volume operator only needs a consistent two-point flux and
17 // a characteristic-speed estimate. Roe upwinding is applied at faces.
18 template<typename GasT>
19 MFEM_HOST_DEVICE inline static void
20 ComputeVolumeFluxKernel(const GasT &gas,
21 const mfem::real_t *q1,
22 const mfem::real_t *q2,
23 const mfem::real_t *met1,
24 const mfem::real_t *met2,
25 mfem::real_t *flux)
26 {
27 const int dim = gas.dim();
28 const int neq = gas.num_equations();
29 mfem::real_t met[Theseus::MAXDIM] = {0.0, 0.0, 0.0};
30 mfem::real_t f1[Theseus::MAXEQ][Theseus::MAXDIM];
31 mfem::real_t f2[Theseus::MAXEQ][Theseus::MAXDIM];
32 Kernels::ComputeMeanVec(met1, met2, met, dim);
33 NavierStokesFlux::ComputeInviscidFluxKernel(gas, q1, f1);
34 NavierStokesFlux::ComputeInviscidFluxKernel(gas, q2, f2);
35
36 for (int eq = 0; eq < neq; ++eq)
37 {
38 flux[eq] = 0.0;
39 for (int d = 0; d < dim; ++d)
40 {
41 flux[eq] += 0.5 * (f1[eq][d] + f2[eq][d]) * met[d];
42 }
43 }
44
45 }
46
47 // Classical Roe flux for a calorically perfect gas. The decomposition is
48 // written in normal/tangential-vector form so the same kernel works in 1-D,
49 // 2-D, and 3-D and for non-unit MFEM face normals.
50 template<typename GasModelT>
51 MFEM_HOST_DEVICE inline static void
52 ComputeFaceFluxKernel(const GasModelT &gas,
53 const mfem::real_t *qL,
54 const mfem::real_t *qR,
55 const mfem::real_t *nor,
56 mfem::real_t *flux)
57 {
58 const int dim = gas.dim();
59 const int neq = gas.num_equations();
60 const int eq_mass = gas.L.eq_mass;
61 const int eq_mom0 = gas.L.eq_mom0;
62 const int eq_energy = gas.L.eq_energy;
63 PointStateView SL{qL};
64 PointStateView SR{qR};
65
66 mfem::real_t fL[Theseus::MAXEQ][Theseus::MAXDIM];
67 mfem::real_t fR[Theseus::MAXEQ][Theseus::MAXDIM];
68 NavierStokesFlux::ComputeInviscidFluxKernel(gas, qL, fL);
69 NavierStokesFlux::ComputeInviscidFluxKernel(gas, qR, fR);
70
71 mfem::real_t nor_mag2 = 0.0;
72 for (int d = 0; d < dim; ++d) { nor_mag2 += nor[d] * nor[d]; }
73 const mfem::real_t nor_mag = Kernels::rsqrt(nor_mag2);
74 const mfem::real_t inv_nor_mag = 1.0 / nor_mag;
75
76 const mfem::real_t rhoL = gas.density(SL);
77 const mfem::real_t rhoR = gas.density(SR);
78 const mfem::real_t pL = gas.pressure(SL);
79 const mfem::real_t pR = gas.pressure(SR);
80 const mfem::real_t rootL = std::sqrt(rhoL);
81 const mfem::real_t rootR = std::sqrt(rhoR);
82 const mfem::real_t root_sum_inv = 1.0 / (rootL + rootR);
83 const mfem::real_t rho_roe = rootL * rootR;
84 const mfem::real_t HL = (gas.energy(SL) + pL) / rhoL;
85 const mfem::real_t HR = (gas.energy(SR) + pR) / rhoR;
86 const mfem::real_t H = (rootL * HL + rootR * HR) * root_sum_inv;
87
88 mfem::real_t u[Theseus::MAXDIM] = {0.0, 0.0, 0.0};
89 mfem::real_t du[Theseus::MAXDIM] = {0.0, 0.0, 0.0};
90 mfem::real_t du_t[Theseus::MAXDIM] = {0.0, 0.0, 0.0};
91 mfem::real_t nhat[Theseus::MAXDIM] = {0.0, 0.0, 0.0};
92 mfem::real_t u2 = 0.0;
93 mfem::real_t un = 0.0;
94 mfem::real_t dun = 0.0;
95 for (int d = 0; d < dim; ++d)
96 {
97 const mfem::real_t uL = gas.velocity(SL, d);
98 const mfem::real_t uR = gas.velocity(SR, d);
99 nhat[d] = nor[d] * inv_nor_mag;
100 u[d] = (rootL * uL + rootR * uR) * root_sum_inv;
101 du[d] = uR - uL;
102 u2 += u[d] * u[d];
103 un += u[d] * nhat[d];
104 dun += du[d] * nhat[d];
105 }
106
107 const mfem::real_t gamma = gas.gamma(SL);
108 const mfem::real_t a2 = (gamma - 1.0) * (H - 0.5 * u2);
109 const mfem::real_t a = std::sqrt(a2);
110 const mfem::real_t dp = pR - pL;
111 const mfem::real_t drho = rhoR - rhoL;
112 const mfem::real_t alpha_minus = 0.5 * (dp - rho_roe * a * dun) / a2;
113 const mfem::real_t alpha_plus = 0.5 * (dp + rho_roe * a * dun) / a2;
114 const mfem::real_t alpha_zero = drho - dp / a2;
115 const mfem::real_t lambda_minus = Kernels::rabs(un - a) * nor_mag;
116 const mfem::real_t lambda_zero = Kernels::rabs(un) * nor_mag;
117 const mfem::real_t lambda_plus = Kernels::rabs(un + a) * nor_mag;
118
119 for (int d = 0; d < dim; ++d) { du_t[d] = du[d] - dun * nhat[d]; }
120
121 const mfem::real_t dmass = lambda_minus * alpha_minus
122 + lambda_zero * alpha_zero
123 + lambda_plus * alpha_plus;
124 mfem::real_t diss[Theseus::MAXEQ] = {0.0};
125 diss[eq_mass] = dmass;
126 for (int d = 0; d < dim; ++d)
127 {
128 diss[eq_mom0 + d] =
129 lambda_minus * alpha_minus * (u[d] - a * nhat[d])
130 + lambda_zero * (alpha_zero * u[d] + rho_roe * du_t[d])
131 + lambda_plus * alpha_plus * (u[d] + a * nhat[d]);
132 }
133 mfem::real_t u_dot_du_t = 0.0;
134 for (int d = 0; d < dim; ++d) { u_dot_du_t += u[d] * du_t[d]; }
135 diss[eq_energy] =
136 lambda_minus * alpha_minus * (H - a * un)
137 + lambda_zero * (0.5 * alpha_zero * u2 + rho_roe * u_dot_du_t)
138 + lambda_plus * alpha_plus * (H + a * un);
139
140 // Passive scalars share the contact eigenvalue. Their acoustic share is
141 // carried with the Roe-averaged mass fraction.
142 for (int s = 0; s < gas.L.num_scalars; ++s)
143 {
144 const int eq = gas.L.eq_scalar0 + s;
145 const mfem::real_t y_roe =
146 (rootL * qL[eq] / rhoL + rootR * qR[eq] / rhoR) * root_sum_inv;
147 diss[eq] = y_roe * dmass
148 + lambda_zero * ((qR[eq] - qL[eq]) - y_roe * drho);
149 }
150
151 for (int eq = 0; eq < neq; ++eq)
152 {
153 mfem::real_t central = 0.0;
154 for (int d = 0; d < dim; ++d)
155 {
156 central += 0.5 * (fL[eq][d] + fR[eq][d]) * nor[d];
157 }
158 flux[eq] = central - 0.5 * diss[eq];
159 }
160
161 }
162
164 {
165 template<typename GasModelT>
166 MFEM_HOST_DEVICE inline void
167 ComputeVolumeFlux(const GasModelT &gas,
168 const mfem::real_t *q1,
169 const mfem::real_t *q2,
170 const mfem::real_t *met1,
171 const mfem::real_t *met2,
172 mfem::real_t *flux) const
173 {
174 ComputeVolumeFluxKernel(gas, q1, q2, met1, met2, flux);
175 }
176
177 template<typename GasModelT>
178 MFEM_HOST_DEVICE inline void
179 ComputeFaceFlux(const GasModelT &gas,
180 const mfem::real_t *qminus,
181 const mfem::real_t *qplus,
182 const mfem::real_t *nor,
183 mfem::real_t *flux) const
184 {
185 ComputeFaceFluxKernel(gas, qminus, qplus, nor, flux);
186 }
187 };
188 }
189}
MFEM_HOST_DEVICE void ComputeMeanVec(const mfem::real_t *a, const mfem::real_t *b, mfem::real_t *out, int n)
Definition theseus_kernels.hpp:101
MFEM_HOST_DEVICE mfem::real_t rsqrt(mfem::real_t x)
Definition theseus_kernels.hpp:19
MFEM_HOST_DEVICE mfem::real_t rabs(mfem::real_t x)
Definition theseus_kernels.hpp:22
Definition AxisymmetricGeometry.hpp:15
constexpr const int MAXDIM
Definition theseus_kernels.hpp:14
constexpr const int MAXEQ
Definition theseus_kernels.hpp:13
Definition GasState.hpp:127
Definition RoeFlux.hpp:164
MFEM_HOST_DEVICE void ComputeFaceFlux(const GasModelT &gas, const mfem::real_t *qminus, const mfem::real_t *qplus, const mfem::real_t *nor, mfem::real_t *flux) const
Definition RoeFlux.hpp:179
MFEM_HOST_DEVICE void ComputeVolumeFlux(const GasModelT &gas, const mfem::real_t *q1, const mfem::real_t *q2, const mfem::real_t *met1, const mfem::real_t *met2, mfem::real_t *flux) const
Definition RoeFlux.hpp:167