Theseus
Compressible flow solver
Loading...
Searching...
No Matches
NavierStokesFlux.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 "GasModel.hpp"
11
12namespace Theseus
13{
14 namespace NavierStokesFlux
15 {
16 // Inviscid / Euler Flux
17 template<typename GasT>
18 MFEM_HOST_DEVICE inline static void
19 ComputeInviscidFluxKernel(const GasT &gas,
20 const mfem::real_t *state,
21 mfem::real_t inv_flux[Theseus::MAXEQ][Theseus::MAXDIM])
22 {
23 PointStateView S{state};
24
25 // 1. Get states
26 const int dim = gas.dim();
27 const mfem::real_t density = gas.density(S);
28 const mfem::real_t spec_vol = 1.0/density;
29 mfem::real_t momentum[Theseus::MAXDIM] = {0.,0.,0.};
30 for(int idim = 0;idim < dim;idim++){
31 momentum[idim] = gas.momentum(S, idim);
32 }
33
34 const mfem::real_t energy = gas.energy(S);
35 const mfem::real_t pressure = gas.pressure(S);
36 const mfem::real_t ke = gas.kinetic_energy_density(S);
37 const int eq_mass = gas.L.eq_mass;
38 const int eq_mom0 = gas.L.eq_mom0;
39 const int eq_ener = gas.L.eq_energy;
40 const int eq_spec = gas.L.eq_scalar0;
41
42 const mfem::real_t H = (energy + pressure)*spec_vol;
43 // 2. Compute Flux
44 for (int d = 0; d < dim; d++)
45 {
46 inv_flux[eq_mass][d] = momentum[d];
47 for (int i = 0; i < dim; i++)
48 {
49 // ρuuᵀ
50 inv_flux[eq_mom0+i][d] = momentum[i]*momentum[d]*spec_vol;
51 }
52 // (ρuuᵀ) + p
53 inv_flux[eq_mom0+d][d] += pressure;
54 inv_flux[eq_ener][d] = momentum[d]*H;
55 for(int s = 0;s < gas.L.num_scalars;s++){
56 inv_flux[eq_spec+s][d] = gas.scalar(S, s) * momentum[d] * spec_vol;
57 }
58 }
59 // 3. Compute maximum characteristic speed
60 // const mfem::real_t sound = gas.sound_speed(S);
61 // fluid speed |u|
62 // const mfem::real_t speed = Theseus::Kernels::rsqrt(2.0 * ke / density);
63 // max characteristic speed = fluid speed + sound speed
64 // return speed + sound;
65 }
66
67 template<typename GasT>
68 MFEM_HOST_DEVICE inline
69 static void ComputeViscousFluxKernel(const GasT &gas,
70 const mfem::real_t *state,
71 const mfem::real_t *dprim_x,
72 const mfem::real_t *dprim_y,
73 const mfem::real_t *dprim_z,
74 mfem::real_t visc_flux[Theseus::MAXEQ][Theseus::MAXDIM],
75 bool axisymmetric = false,
76 mfem::real_t radius = 0.0,
77 mfem::real_t *azimuthal_stress = nullptr)
78 {
79
80 // TODO: Update for scalar transport
81 const int dim = gas.dim();
82 // Zero the flux to start
83 for(int q = 0;q < Theseus::MAXEQ;q++){
84 for(int idir = 0;idir < dim;idir++){
85 visc_flux[q][idir] = 0.0;
86 }
87 }
88
89 PointStateView S{state};
90
91 // Access some physical constants
92 const mfem::real_t mu = gas.viscosity(S);
93 const mfem::real_t kappa = gas.thermal_conductivity(S);
94 const mfem::real_t mu_bulk = gas.bulk_viscosity(S);
95
96 // State structure constants
97 const int eq_mass = gas.L.eq_mass;
98 const int eq_mom0 = gas.L.eq_mom0;
99 const int eq_ener = gas.L.eq_energy;
100 const int nscalar = gas.L.num_scalars;
101
102 // Make & populate gradient containers
103 mfem::real_t grad_rho[Theseus::MAXDIM] = {0.0, 0.0, 0.0};
104 mfem::real_t grad_p[Theseus::MAXDIM] = {0.0, 0.0, 0.0};
105 mfem::real_t grad_t[Theseus::MAXDIM] = {0.0, 0.0, 0.0};
106 mfem::real_t grad_vel[Theseus::MAXDIM][Theseus::MAXDIM] = {{0.0}};
107
108 grad_rho[0] = dprim_x[eq_mass];
109 grad_vel[0][0] = dprim_x[eq_mom0];
110 grad_p[0] = dprim_x[eq_ener];
111 if(dim > 1){
112 grad_rho[1] = dprim_y[eq_mass];
113 grad_vel[0][1] = dprim_y[eq_mom0];
114 grad_vel[1][0] = dprim_x[eq_mom0+1];
115 grad_vel[1][1] = dprim_y[eq_mom0+1];
116 grad_p[1] = dprim_y[eq_ener];
117 if(dim > 2){
118 grad_rho[2] = dprim_z[eq_mass];
119 grad_vel[0][2] = dprim_z[eq_mom0];
120 grad_vel[1][2] = dprim_z[eq_mom0+1];
121 grad_vel[2][0] = dprim_x[eq_mom0+2];
122 grad_vel[2][1] = dprim_y[eq_mom0+2];
123 grad_vel[2][2] = dprim_z[eq_mom0+2];
124 grad_p[2] = dprim_z[eq_ener];
125 }
126 }
127
128 gas.grad_temperature(S, grad_rho, grad_p, grad_t);
129 mfem::real_t vel[Theseus::MAXDIM] = {0.0, 0.0, 0.0};
130 mfem::real_t div_vel = 0.0;
131 for(int i = 0;i < dim;i++){
132 vel[i] = gas.velocity(S, i);
133 div_vel += grad_vel[i][i];
134 }
135 mfem::real_t radial_rate = 0.0;
136 if (axisymmetric)
137 {
139 //radial_rate = radius > AxisymmetricGeometry::radius_tolerance ?
140 // vel[radial] / radius : grad_vel[radial][radial];
142 {
143 radial_rate = vel[radial] / radius;
144 }
145 else
146 {
147 radial_rate = grad_vel[radial][radial];
148 // Axis parity requires u_r=0. Use the regular trace in the
149 // viscous energy flux even if the weak axis BC leaves a small
150 // nonzero radial momentum at the boundary node.
151 vel[radial] = 0.0;
152 }
153 div_vel += radial_rate;
154 }
155
156 if (azimuthal_stress)
157 {
158 *azimuthal_stress = 0.0;
159 if (axisymmetric)
160 {
161 *azimuthal_stress = mu *
162 (2.0 * radial_rate - mu_bulk * div_vel);
163 }
164 }
165
166 // Build momentum/energy viscous fluxes by physical direction.
167 // Output convention:
168 // flux_eq_dir[eq][dir]
169 //
170 // Mass row eq=0 remains zero.
171 for (int dir = 0; dir < dim; ++dir)
172 {
173 // viscous stress tensor components tau[mom,dir]
174 for (int mom = 0; mom < dim; ++mom)
175 {
176 mfem::real_t tau = 0.0;
177
178 if (mom == dir)
179 {
180 // Perserve legacy tau exactly
181 tau = mu * (2.0 * grad_vel[mom][dir] - mu_bulk * div_vel);
182 }
183 else
184 {
185 tau = mu * (grad_vel[mom][dir] + grad_vel[dir][mom]);
186 }
187
188 visc_flux[eq_mom0 + mom][dir] = tau;
189 }
190 mfem::real_t eflux = kappa * grad_t[dir];
191 for(int mom = 0;mom < dim;mom++)
192 {
193 eflux += vel[mom] * visc_flux[eq_mom0 + mom][dir];
194 }
195 visc_flux[eq_ener][dir] = eflux;
196 }
197 }
198
199 template<typename GasT>
200 MFEM_HOST_DEVICE inline
201 static void compute_ref_viscous_flux(const GasT &gas,
202 const int dim,
203 const int neq,
204 const mfem::real_t *state,
205 const mfem::real_t *dqx,
206 const mfem::real_t *dqy,
207 const mfem::real_t *dqz,
208 const mfem::real_t *adj_row,
209 mfem::real_t *f_ref,
210 bool axisymmetric = false,
211 mfem::real_t radius = 0.0)
212 {
213 mfem::real_t flux_phys[Theseus::MAXEQ][Theseus::MAXDIM] = {{0.}};
214
215 // Grab the physical flux
216 ComputeViscousFluxKernel(gas, state, dqx, dqy, dqz, flux_phys,
217 axisymmetric, radius);
218
219 for (int q = 0; q < neq; ++q)
220 {
221 f_ref[q] = 0.0;
222 for (int j = 0; j < dim; ++j)
223 f_ref[q] += adj_row[j] * flux_phys[q][j];
224 }
225 }
226
227 };
228
229}
Definition AxisymmetricGeometry.hpp:15
constexpr const int MAXDIM
Definition theseus_kernels.hpp:14
constexpr const int MAXEQ
Definition theseus_kernels.hpp:13
static constexpr mfem::real_t radius_tolerance
Definition AxisymmetricGeometry.hpp:22
static constexpr int radial_coordinate
Definition AxisymmetricGeometry.hpp:20
Definition GasState.hpp:127
MFEM_HOST_DEVICE mfem::real_t momentum(const StateLayout &L, int d) const
Definition GasState.hpp:147
MFEM_HOST_DEVICE mfem::real_t velocity(const StateLayout &L, int d) const
Definition GasState.hpp:169