Theseus
Compressible flow solver
Loading...
Searching...
No Matches
ChandrashekarFlux.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"
9
10namespace Theseus
11{
12
13 namespace ChandrashekarFlux
14 {
15 // This is Riemann solver that computes the numerical flux for 2 point states
16 // Will be ctx.iflux.ComputeVolumeFlux
17 template<typename GasModelT>
18 MFEM_HOST_DEVICE
19 inline static mfem::real_t ComputeVolumeFluxKernel(const GasModelT &gasModel,
20 const mfem::real_t* q1,
21 const mfem::real_t* q2,
22 const mfem::real_t* met1,
23 const mfem::real_t* met2,
24 mfem::real_t* F_tilde)
25 {
26 const int dim = gasModel.dim();
27 const int neq = gasModel.num_equations();
28
29 // mean metric row
30 mfem::real_t met[3] = {0,0,0};
31 Kernels::ComputeMeanVec(met1, met2, met, dim);
34
35 const mfem::real_t rho1 = gasModel.density(S1);
36 const mfem::real_t rho2 = gasModel.density(S2);
37 const mfem::real_t rho_ln = Kernels::ComputeLogMean(rho1, rho2, 1e-4);
38
39 mfem::real_t mom_hat[3] = {0,0,0};
40 mfem::real_t h_hat = 0;
41 mfem::real_t vn = 0;
42 mfem::real_t v2_1 = 0;
43 mfem::real_t v2_2 = 0;
44
45 for (int d=0; d<dim; ++d)
46 {
47 const mfem::real_t v1 = gasModel.velocity(S1, d);
48 const mfem::real_t v2 = gasModel.velocity(S2, d);
49 const mfem::real_t vbar = mfem::real_t(0.5)*(v1+v2);
50
51 v2_1 += v1*v1;
52 v2_2 += v2*v2;
53 vn += vbar * met[d];
54
55 mom_hat[d] = rho_ln * vbar;
56
57 h_hat += -mfem::real_t(0.25)*(v1*v1 + v2*v2) + vbar*vbar;
58 }
59
60
61 const mfem::real_t p1 = gasModel.pressure(S1);
62 const mfem::real_t p2 = gasModel.pressure(S2);
63
64 const mfem::real_t speed1 = Kernels::rsqrt(v2_1);
65 const mfem::real_t speed2 = Kernels::rsqrt(v2_2);
66
67 const mfem::real_t c1 = gasModel.sound_speed(S1);
68 const mfem::real_t c2 = gasModel.sound_speed(S2);
69
70 const mfem::real_t lambda_max = Kernels::rmax(speed1 + c1, speed2 + c2);
71
72 // Single-component ideal-gas-specific KEPEC bits
73 // TODO: Update/Craft KPEC fluxes for mixtures (and passive scalar components)
74 const mfem::real_t beta1 = mfem::real_t(0.5) * rho1 / p1;
75 const mfem::real_t beta2 = mfem::real_t(0.5) * rho2 / p2;
76 const mfem::real_t beta_ln = Kernels::ComputeLogMean(beta1, beta2, 1e-4);
77
78 const mfem::real_t p_hat = mfem::real_t(0.5) * (rho1 + rho2) / (beta1 + beta2);
79
80 const mfem::real_t gm11 = gasModel.gamma(S1);
81 const mfem::real_t gm12 = gasModel.gamma(S2);
82 const mfem::real_t gm1_av_inv = mfem::real_t(2.0) / (gm11 + gm12 - mfem::real_t(2.0));
83
84 h_hat += mfem::real_t(0.5) / beta_ln * gm1_av_inv + p_hat / rho_ln;
85
86 // F_tilde layout: [rho, rhoV, rhoE]
87 // NOTE: Caller *must* zero(or own) F_tilde (size: neq)
88 // NOTE: HRM! Why ZERO? It appears that F_tilde is overwritten below
89 const int mass_eq = gasModel.L.eq_mass;
90 const int mom0_eq = gasModel.L.eq_mom0;
91 const int ener_eq = gasModel.L.eq_energy;
92 F_tilde[mass_eq] = rho_ln * vn;
93 for (int d=0; d<dim; ++d)
94 {
95 F_tilde[mom0_eq + d] = vn * mom_hat[d] + p_hat * met[d];
96 }
97 F_tilde[ener_eq] = rho_ln * vn * h_hat;
98
99 // TODO: Updte for scalars, sigh
100 // for (s=0; s<num_scalars; ++s) F_tilde[XXXX]= XXX
101
102 return lambda_max;
103 }
104
105 template<typename GasModelT>
106 MFEM_HOST_DEVICE inline static mfem::real_t ComputeFaceFluxKernel(const GasModelT &gasModel,const mfem::real_t *state1,
107 const mfem::real_t *state2, const mfem::real_t *nor,
108 mfem::real_t *flux)
109 {
110 const int dim = gasModel.dim();
111 const int neq = gasModel.num_equations();
112
113 Theseus::PointStateView S1{state1};
114 Theseus::PointStateView S2{state2};
115
116 const mfem::real_t rho1 = gasModel.density(S1);
117 const mfem::real_t rho2 = gasModel.density(S2);
118 const mfem::real_t rho_mean = 0.5 * (rho1 + rho2);
119 const mfem::real_t rho_ln = Kernels::ComputeLogMean(rho1, rho2, 1e-4);
120 const mfem::real_t drho = rho2 - rho1;
121 mfem::real_t mom[3] = {0.0, 0.0, 0.0};
122 mfem::real_t mom1[3] = {0.0, 0.0, 0.0};
123 mfem::real_t mom2[3] = {0.0, 0.0, 0.0};
124 mfem::real_t hhat = 0.0;
125 mfem::real_t diss = 0.0;
126 mfem::real_t v21 = 0.0;
127 mfem::real_t v22 = 0.0;
128 mfem::real_t vn = 0.0;
129 mfem::real_t nor_mag = 0.0;
130
131 for(int idim = 0;idim < dim;idim++){
132 nor_mag += nor[idim]*nor[idim];
133 mom1[idim] = gasModel.momentum(S1, idim);
134 mom2[idim] = gasModel.momentum(S2, idim);
135 const mfem::real_t v1 = mom1[idim]/rho1;
136 const mfem::real_t v2 = mom2[idim]/rho2;
137 const mfem::real_t vbar = 0.5 * (v1 + v2);
138 const mfem::real_t dv = v2 - v1;
139 v21 += v1*v1;
140 v22 += v2*v2;
141 vn += vbar * nor[idim];
142 mom[idim] = rho_ln * vbar;
143 hhat += -0.25 * (v1*v1 + v2*v2) + vbar * vbar;
144 diss += 0.5 * drho * v1*v2 + rho_mean * dv * vbar;
145 }
146 nor_mag = std::sqrt(nor_mag);
147
148 const mfem::real_t p1 = gasModel.pressure(S1);
149 const mfem::real_t p2 = gasModel.pressure(S2);
150
151 const mfem::real_t vmag1 = std::sqrt(v21);
152 const mfem::real_t vmag2 = std::sqrt(v22);
153
154 const mfem::real_t c1 = gasModel.sound_speed(S1);
155 const mfem::real_t c2 = gasModel.sound_speed(S2);
156
157 const mfem::real_t lambda_max = Kernels::rmax(vmag1 + c1, vmag2 + c2);
158
159 const mfem::real_t beta1 = 0.5 * rho1 / p1;
160 const mfem::real_t beta2 = 0.5 * rho2 / p2;
161 const mfem::real_t beta_ln = Kernels::ComputeLogMean(beta1, beta2, 1e-4);
162
163 const mfem::real_t p_hat = 0.5 * (rho1 + rho2) / (beta1 + beta2);
164
165 // Use the average gamma for now
166 // TODO: Craft KEPEC fluxes for LTE/NLTE
167 const mfem::real_t gm11 = gasModel.gamma(S1);
168 const mfem::real_t gm12 = gasModel.gamma(S2);
169 const mfem::real_t gm1_av_inv = 2.0/(gm11 + gm12 - 2.0);
170
171 hhat += 0.5 / beta_ln * gm1_av_inv + p_hat / rho_ln;
172 diss += 0.5 * drho * gm1_av_inv / beta_ln + 0.5 * rho_mean * gm1_av_inv * (1.0 / beta2 - 1.0 / beta1);
173 const int mass_eq = gasModel.L.eq_mass;
174 const int mom0_eq = gasModel.L.eq_mom0;
175 const int ener_eq = gasModel.L.eq_energy;
176
177 flux[mass_eq] = rho_ln * vn - 0.5 * lambda_max * (rho2 - rho1) * nor_mag;
178 for (int d = 0; d < dim; d++)
179 {
180 flux[mom0_eq + d] = vn * mom[d] + p_hat * nor[d] - 0.5 * lambda_max * (mom2[d]-mom1[d]) * nor_mag;
181 }
182 flux[ener_eq] = rho_ln * vn * hhat - 0.5 * lambda_max * diss * nor_mag;
183
184 return lambda_max;
185 }
187
188 template<typename GasModelT>
189 MFEM_HOST_DEVICE inline mfem::real_t ComputeVolumeFlux(const GasModelT &gasModel,
190 const mfem::real_t *q1, const mfem::real_t *q2,
191 const mfem::real_t *met1, const mfem::real_t *met2,
192 mfem::real_t *F_tilde) const{
193 return ComputeVolumeFluxKernel(gasModel, q1, q2, met1, met2, F_tilde);
194 }
195
196 template<typename GasModelT>
197 MFEM_HOST_DEVICE inline mfem::real_t ComputeFaceFlux(const GasModelT &gasModel,const mfem::real_t *qminus,
198 const mfem::real_t *qplus, const mfem::real_t *nor,
199 mfem::real_t *flux) const {
200 return ComputeFaceFluxKernel(gasModel, qminus, qplus, nor, flux);
201 }
202 };
203 };
204}
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 ComputeLogMean(mfem::real_t x, mfem::real_t y, mfem::real_t eps)
Definition theseus_kernels.hpp:107
MFEM_HOST_DEVICE mfem::real_t rmax(mfem::real_t a, mfem::real_t b)
Definition theseus_kernels.hpp:17
Definition AxisymmetricGeometry.hpp:15
Definition ChandrashekarFlux.hpp:186
MFEM_HOST_DEVICE mfem::real_t ComputeFaceFlux(const GasModelT &gasModel, const mfem::real_t *qminus, const mfem::real_t *qplus, const mfem::real_t *nor, mfem::real_t *flux) const
Definition ChandrashekarFlux.hpp:197
MFEM_HOST_DEVICE mfem::real_t ComputeVolumeFlux(const GasModelT &gasModel, const mfem::real_t *q1, const mfem::real_t *q2, const mfem::real_t *met1, const mfem::real_t *met2, mfem::real_t *F_tilde) const
Definition ChandrashekarFlux.hpp:189
Definition GasState.hpp:127