Theseus
Compressible flow solver
Loading...
Searching...
No Matches
bc_kernels.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
9#include "Flow.hpp"
10#include "NavierStokesFlux.hpp"
11
12namespace Theseus {
13
14 namespace BC {
15
16 template<typename GasModelT>
17 MFEM_HOST_DEVICE inline void ReflectAxisState(
18 const GasModelT &gas, const mfem::real_t *interior,
19 mfem::real_t *exterior)
20 {
21 const int equations = gas.L.nequations();
22 for (int equation = 0; equation < equations; ++equation)
23 {
24 exterior[equation] = interior[equation];
25 }
26 const int radial_momentum =
28 exterior[radial_momentum] = -interior[radial_momentum];
29 }
30
31
32 template <typename DeviceCacheT>
33 MFEM_HOST_DEVICE inline
34 void ComputeBdrFaceGradFlux(const DeviceCacheT &dc,
35 const Theseus::BCDescriptor &bc,
36 const mfem::real_t *state1,
37 mfem::real_t *fluxN)
38 {
39 const auto &gas = dc.gas;
40 const int neq = dc.num_equations;
41 const int dim = dc.dim;
42
43 for (int q = 0; q < neq; ++q)
44 {
45 fluxN[q] = mfem::real_t(0);
46 }
47
48 switch (static_cast<Theseus::BCType>(bc.type))
49 {
51 {
52 const mfem::real_t *vector_data = dc.bc_vector_d;
53 const mfem::real_t *Vwall = vector_data + bc.data_index;
54 // unused - but be aware
55 // const mfem::real_t *wallHeat = vector_data + bc.data_index + dim;
56
57 // Note this must be entropy state
60
61 const mfem::real_t v = -gas.energy(S);
62
63 // Build the same provisional "boundary" state as legacy, then subtract state1.
64 F.set_mass(gas.L, gas.mass(S)); // Is this really correct? v+ != v- so.. hrm.
65 for(int idim = 0;idim < dim;idim++){
66 F.set_momentum(gas.L, idim, Vwall[idim] * v);
67 }
68 F.set_energy(gas.L, -v);
69 for (int q = 0; q < neq; ++q)
70 {
71 fluxN[q] -= state1[q];
72 }
73 return;
74 }
75
77 {
78 const mfem::real_t *bc_data = dc.bc_vector_d + bc.data_index;
79 const mfem::real_t *Vwall = bc_data;
80 const mfem::real_t Twall = bc_data[dim];
81
82 // state1 is entropy state
85
86 /*
87 * Need the entropy state / beta corresponding to Twall and Vwall.
88 *
89 * Legacy Prandtl does this (roughly):
90 * beta = isothermal_wall_beta(S, Twall, gas);
91 * F[mom] = Vwall * beta;
92 * F[energy] = -beta;
93 *
94 * So this method should return the same quantity that -gas.energy(S)
95 * returns for the adiabatic case, but evaluated at Twall. I am going
96 * to match legacy-like behavior here - but I'm skeptical of it.
97 *
98 * Note:
99 * Strictly speaking, shouldn't we form the (+) entropy state by prescribing
100 * Twall, and Vwall? In that case, I think at least the mass component is off
101 *
102 */
103
104 const mfem::real_t beta_like = Theseus::Flow::isothermal_wall_beta(S, Twall, gas);
105
106 F.set_mass(gas.L, gas.mass(S));
107 for (int idim = 0; idim < dim; ++idim)
108 {
109 F.set_momentum(gas.L, idim, Vwall[idim] * beta_like);
110 }
111 F.set_energy(gas.L, -beta_like);
112
113 for (int q = 0; q < neq; ++q)
114 {
115 fluxN[q] -= state1[q];
116 }
117
118 return;
119 }
120
122 {
123 mfem::real_t exterior[Theseus::MAXEQ];
124 ReflectAxisState(gas, state1, exterior);
125 for (int equation = 0; equation < neq; ++equation)
126 {
127 fluxN[equation] = exterior[equation] - state1[equation];
128 }
129 return;
130 }
131
132 default:
133 {
134 // Conservative placeholder for unsupported BCs.
135 for (int q = 0; q < neq; ++q)
136 {
137 fluxN[q] = mfem::real_t(0);
138 }
139 return;
140 }
141 }
142 }
143
144
145 template<typename GasModelT>
146 MFEM_HOST_DEVICE
147 mfem::real_t SlipWallInviscidFluxKernel(const GasModelT &gasModel, const mfem::real_t *state1,
148 const mfem::real_t *nor, mfem::real_t *fluxN)
149 {
150
151 mfem::real_t unit_nor[Theseus::MAXDIM];
152 mfem::real_t state2[Theseus::MAXEQ];
153 const int dim = gasModel.L.dim;
154 const int neq = gasModel.L.nequations();
155 for(int idim = 0;idim < dim;idim++)
156 unit_nor[idim] = nor[idim];
157 for(int ieq = 0;ieq < neq;ieq++){
158 state2[ieq] = state1[ieq];
159 fluxN[ieq] = 0.0;
160 }
161 Theseus::Kernels::Normalize(dim, unit_nor);
163 Theseus::Flow::RotateState(gasModel.L, unit_nor, S);
164 const mfem::real_t p_star = Theseus::Flow::slipwall_pstar(S, gasModel);
165 const mfem::real_t v = gasModel.velocity(S, 0); // the "x" component is v*n
166 const mfem::real_t c = gasModel.sound_speed(S);
167 const int mom_eq = gasModel.L.eq_mom0;
168 for(int idim = 0;idim < dim;idim++)
169 fluxN[mom_eq+idim] = p_star * nor[idim];
170 return std::abs(v) + c;
171 }
172
173 template<typename GasModelT>
174 MFEM_HOST_DEVICE
175 mfem::real_t NoSlipAdiabWallFluxKernel(const GasModelT &gasModel, const mfem::real_t *state1,
176 const mfem::real_t *gradPrim_x, const mfem::real_t *gradPrim_y,
177 const mfem::real_t *gradPrim_z,
178 const mfem::real_t *nor, const mfem::real_t vWall[Theseus::MAXDIM],
179 const mfem::real_t qWall, bool axisymmetric,
180 mfem::real_t radius, mfem::real_t *fluxN)
181 {
182 mfem::real_t unit_nor[Theseus::MAXDIM];
183 mfem::real_t state2[Theseus::MAXEQ];
184 mfem::real_t visc_flux[Theseus::MAXEQ][Theseus::MAXDIM];
185 const int dim = gasModel.L.dim;
186 const int neq = gasModel.L.nequations();
187 mfem::real_t normag = 0.0;
188 for(int idim = 0;idim < dim;idim++){
189 unit_nor[idim] = nor[idim];
190 normag += nor[idim]*nor[idim];
191 }
192 normag = Theseus::Kernels::rsqrt(normag);
193
194 for(int ieq = 0;ieq < neq;ieq++){
195 state2[ieq] = state1[ieq];
196 fluxN[ieq] = 0.0;
197 }
198 // Inviscid part is just like slipwall
199 Theseus::Kernels::Normalize(dim, unit_nor);
201 Theseus::Flow::RotateState(gasModel.L, unit_nor, S);
202 const mfem::real_t p_star = Theseus::Flow::slipwall_pstar(S, gasModel);
203 const mfem::real_t v = gasModel.velocity(S, 0); // the "x" component is v*n
204 const mfem::real_t c = gasModel.sound_speed(S);
205 const int mom_eq = gasModel.L.eq_mom0;
206 for(int idim = 0;idim < dim;idim++)
207 fluxN[mom_eq+idim] = p_star * nor[idim];
208
209 // Inviscid part is done, now for the viscous part
210 mfem::real_t qn = qWall * normag;
211 NavierStokesFlux::ComputeViscousFluxKernel(gasModel, state1, gradPrim_x, gradPrim_y,
212 gradPrim_z, visc_flux, axisymmetric, radius);
213 mfem::real_t vflux_n[Theseus::MAXEQ];
214 for(int j = 0;j < neq;j++){
215 vflux_n[j] = 0.0;
216 for(int idim = 0;idim < dim;idim++){
217 vflux_n[j] += nor[idim]*visc_flux[j][idim];
218 }
219 }
220 const int ener_eq = gasModel.L.eq_energy;
221 vflux_n[ener_eq] = qn;
222 for(int idim = 0;idim < dim;idim++){
223 vflux_n[ener_eq] += vWall[idim]*vflux_n[mom_eq+idim];
224 }
225 for(int j = 0; j < neq;j++){
226 fluxN[j] -= vflux_n[j];
227 }
228 return std::abs(v) + c;
229 }
230
231 template<typename GasModelT>
232 MFEM_HOST_DEVICE
233 mfem::real_t NoSlipIsothWallFluxKernel(const GasModelT &gasModel,
234 const mfem::real_t *state1,
235 const mfem::real_t *gradPrim_x,
236 const mfem::real_t *gradPrim_y,
237 const mfem::real_t *gradPrim_z,
238 const mfem::real_t *nor,
239 const mfem::real_t vWall[Theseus::MAXDIM],
240 const mfem::real_t tWall,
241 bool axisymmetric, mfem::real_t radius,
242 mfem::real_t *fluxN)
243 {
244 mfem::real_t unit_nor[Theseus::MAXDIM];
245 mfem::real_t state2[Theseus::MAXEQ];
246 mfem::real_t visc_flux[Theseus::MAXEQ][Theseus::MAXDIM];
247
248 const int dim = gasModel.L.dim;
249 const int neq = gasModel.L.nequations();
250 const int mom_eq = gasModel.L.eq_mom0;
251 const int ener_eq = gasModel.L.eq_energy;
252
253 for (int idim = 0; idim < dim; ++idim)
254 {
255 unit_nor[idim] = nor[idim];
256 }
257
258 for (int ieq = 0; ieq < neq; ++ieq)
259 {
260 state2[ieq] = state1[ieq];
261 fluxN[ieq] = 0.0;
262 }
263
264 // Inviscid part - same as slipwall
265 Theseus::Kernels::Normalize(dim, unit_nor);
266 Theseus::PointStateViewRW Srot{state2};
267 Theseus::Flow::RotateState(gasModel.L, unit_nor, Srot);
268
269 const mfem::real_t p_star = Theseus::Flow::slipwall_pstar(Srot, gasModel);
270 const mfem::real_t vn = gasModel.velocity(Srot, 0);
271 const mfem::real_t c = gasModel.sound_speed(Srot);
272
273 for (int idim = 0; idim < dim; ++idim)
274 {
275 fluxN[mom_eq + idim] = p_star * nor[idim];
276 }
277
278 // Viscous part
279 Theseus::NavierStokesFlux::ComputeViscousFluxKernel(gasModel, state1, gradPrim_x,
280 gradPrim_y, gradPrim_z, visc_flux,
281 axisymmetric, radius);
282
283 mfem::real_t vflux_n[Theseus::MAXEQ];
284 for (int eq = 0; eq < neq; ++eq)
285 {
286 vflux_n[eq] = 0.0;
287 for (int idim = 0; idim < dim; ++idim)
288 {
289 vflux_n[eq] += nor[idim] * visc_flux[eq][idim];
290 }
291 }
292
293 // Recover just the heat flux part so we can adjust
294 // the mechanical part
295 Theseus::PointStateView S{state1};
296 mfem::real_t conductive_n = vflux_n[ener_eq];
297 for (int idim = 0; idim < dim; ++idim)
298 {
299 const mfem::real_t u_trace = gasModel.velocity(S, idim);
300 conductive_n -= u_trace * vflux_n[mom_eq + idim];
301 }
302
303 // reinsert heat flux and adjust mechanical work for Vwall
304 vflux_n[ener_eq] = conductive_n;
305 for (int idim = 0; idim < dim; ++idim)
306 {
307 vflux_n[ener_eq] += vWall[idim] * vflux_n[mom_eq + idim];
308 }
309
310 // Same sign convention as adiabatic kernel:
311 // Ultimately we want F_visc - F_inv, note
312 // that this result is negated at the top level (sigh)
313 for (int eq = 0; eq < neq; ++eq)
314 {
315 fluxN[eq] -= vflux_n[eq];
316 }
317
318 return std::abs(vn) + c;
319 }
320
321
322 template <typename DeviceCacheT>
323 MFEM_HOST_DEVICE
324 mfem::real_t ApplyBoundaryConditionInviscid(const DeviceCacheT &dc,
325 const Theseus::BCDescriptor &bc,
326 const mfem::real_t *state1,
327 const mfem::real_t *nor,
328 mfem::real_t *fluxN)
329 {
330 const auto &gas = dc.gas;
331 const mfem::real_t *scalar_data = dc.bc_scalar_d;
332 const mfem::real_t *vector_data = dc.bc_vector_d;
333 switch (static_cast<Theseus::BCType>(bc.type))
334 {
336 return SlipWallInviscidFluxKernel(gas, state1, nor, fluxN);
337
339 return dc.iflux.ComputeFaceFlux(gas, state1, state1, nor, fluxN);
340
342 {
343 const mfem::real_t *bc_state = vector_data + bc.data_index;
344 return dc.iflux.ComputeFaceFlux(gas, state1, bc_state, nor, fluxN);
345 }
346
348 {
349 const int neq = dc.num_equations;
350 Theseus::PointStateView S{state1};
351 mfem::real_t bc_state[Theseus::MAXEQ];
352 for(int ieq = 0;ieq < neq;ieq++){
353 bc_state[ieq] = state1[ieq];
354 }
355 Theseus::PointStateViewRW S2{bc_state};
356 const int dim = dc.dim;
357 mfem::real_t unorm[Theseus::MAXDIM];
358 mfem::real_t mom[Theseus::MAXDIM];
359 for(int idim = 0;idim < dim;idim++){
360 unorm[idim] = nor[idim];
361 mom[idim] = S.momentum(gas.L, idim);
362 }
363 Theseus::Kernels::Normalize(dim, unorm);
364 mfem::real_t nv = Theseus::Kernels::Dot(dim, mom, unorm);
365 for(int idim = 0;idim < dim;idim++){
366 mfem::real_t mm = -2.0*nv*unorm[idim] + mom[idim];
367 S2.set_momentum(gas.L, idim, mm);
368 }
369 return dc.iflux.ComputeFaceFlux(gas, state1, bc_state, nor, fluxN);
370 }
372 {
373 mfem::real_t bc_state[Theseus::MAXEQ];
374 ReflectAxisState(gas, state1, bc_state);
375 return dc.iflux.ComputeFaceFlux(gas, state1, bc_state, nor, fluxN);
376 }
377 default:
378 {
379 const int neq = dc.num_equations;
380 for (int eq = 0; eq < neq; ++eq) { fluxN[eq] = 0.0; }
381 return 0.0;
382 }
383 }
384 return 0.0;
385 }
386
387 template <typename DeviceCacheT>
388 MFEM_HOST_DEVICE
389 mfem::real_t ApplyViscousBoundaryCondition(const DeviceCacheT &dc,
390 const Theseus::BCDescriptor &bc,
391 const mfem::real_t *state1,
392 const mfem::real_t *gradPrim_x,
393 const mfem::real_t *gradPrim_y,
394 const mfem::real_t *gradPrim_z,
395 const mfem::real_t *nor,
396 mfem::real_t radius,
397 mfem::real_t *fluxN)
398 {
399 const auto &gas = dc.gas;
400 const int dim = dc.dim;
401 const mfem::real_t *scalar_data = dc.bc_scalar_d;
402 const mfem::real_t *vector_data = dc.bc_vector_d;
403 switch (static_cast<Theseus::BCType>(bc.type))
404 {
410 dc, bc, state1, nor, fluxN);
411
413 {
414 const mfem::real_t *bc_vec_data = vector_data + bc.data_index;
415 mfem::real_t vWall[Theseus::MAXDIM];
416 for(int idim=0;idim < dim;idim++){
417 vWall[idim] = bc_vec_data[idim];
418 }
419 const mfem::real_t qWall = bc_vec_data[dim];
420 return NoSlipAdiabWallFluxKernel(gas, state1, gradPrim_x, gradPrim_y,
421 gradPrim_z, nor, vWall, qWall,
422 dc.axisymmetric, radius, fluxN);
423 }
425 {
426 const mfem::real_t *bc_vec_data = vector_data + bc.data_index;
427 mfem::real_t vWall[Theseus::MAXDIM];
428 for(int idim=0;idim < dim;idim++){
429 vWall[idim] = bc_vec_data[idim];
430 }
431 const mfem::real_t tWall = bc_vec_data[dim];
432 return NoSlipIsothWallFluxKernel(gas, state1, gradPrim_x, gradPrim_y,
433 gradPrim_z, nor, vWall, tWall,
434 dc.axisymmetric, radius, fluxN);
435 }
437 {
438 const mfem::real_t wave_speed = ApplyBoundaryConditionInviscid(
439 dc, bc, state1, nor, fluxN);
440 mfem::real_t viscous_flux[Theseus::MAXEQ][Theseus::MAXDIM];
441 NavierStokesFlux::ComputeViscousFluxKernel(
442 gas, state1, gradPrim_x, gradPrim_y, gradPrim_z,
443 viscous_flux, dc.axisymmetric, radius);
444 for (int equation = 0; equation < dc.num_equations; ++equation)
445 {
446 for (int direction = 0; direction < dim; ++direction)
447 {
448 fluxN[equation] -=
449 nor[direction] * viscous_flux[equation][direction];
450 }
451 }
452 return wave_speed;
453 }
454 default:
455 {
456 const int neq = dc.num_equations;
457 for (int eq = 0; eq < neq; ++eq) { fluxN[eq] = 0.0; }
458 return 0.0;
459 }
460 }
461 return 0.0;
462 }
463 }
464}
MFEM_HOST_DEVICE mfem::real_t SlipWallInviscidFluxKernel(const GasModelT &gasModel, const mfem::real_t *state1, const mfem::real_t *nor, mfem::real_t *fluxN)
Definition bc_kernels.hpp:147
MFEM_HOST_DEVICE mfem::real_t NoSlipIsothWallFluxKernel(const GasModelT &gasModel, const mfem::real_t *state1, const mfem::real_t *gradPrim_x, const mfem::real_t *gradPrim_y, const mfem::real_t *gradPrim_z, const mfem::real_t *nor, const mfem::real_t vWall[Theseus::MAXDIM], const mfem::real_t tWall, bool axisymmetric, mfem::real_t radius, mfem::real_t *fluxN)
Definition bc_kernels.hpp:233
MFEM_HOST_DEVICE mfem::real_t ApplyBoundaryConditionInviscid(const DeviceCacheT &dc, const Theseus::BCDescriptor &bc, const mfem::real_t *state1, const mfem::real_t *nor, mfem::real_t *fluxN)
Definition bc_kernels.hpp:324
MFEM_HOST_DEVICE void ComputeBdrFaceGradFlux(const DeviceCacheT &dc, const Theseus::BCDescriptor &bc, const mfem::real_t *state1, mfem::real_t *fluxN)
Definition bc_kernels.hpp:34
MFEM_HOST_DEVICE mfem::real_t ApplyViscousBoundaryCondition(const DeviceCacheT &dc, const Theseus::BCDescriptor &bc, const mfem::real_t *state1, const mfem::real_t *gradPrim_x, const mfem::real_t *gradPrim_y, const mfem::real_t *gradPrim_z, const mfem::real_t *nor, mfem::real_t radius, mfem::real_t *fluxN)
Definition bc_kernels.hpp:389
MFEM_HOST_DEVICE mfem::real_t NoSlipAdiabWallFluxKernel(const GasModelT &gasModel, const mfem::real_t *state1, const mfem::real_t *gradPrim_x, const mfem::real_t *gradPrim_y, const mfem::real_t *gradPrim_z, const mfem::real_t *nor, const mfem::real_t vWall[Theseus::MAXDIM], const mfem::real_t qWall, bool axisymmetric, mfem::real_t radius, mfem::real_t *fluxN)
Definition bc_kernels.hpp:175
MFEM_HOST_DEVICE void ReflectAxisState(const GasModelT &gas, const mfem::real_t *interior, mfem::real_t *exterior)
Definition bc_kernels.hpp:17
MFEM_HOST_DEVICE mfem::real_t isothermal_wall_beta(const StateView &Se, mfem::real_t Tw, const GasModelT &gasModel)
Definition Flow.hpp:84
MFEM_HOST_DEVICE mfem::real_t slipwall_pstar(const StateView &S, const GasModelT &gasModel)
Definition Flow.hpp:57
MFEM_HOST_DEVICE void RotateState(const StateLayout layout, const mfem::real_t *nor, Theseus::PointStateViewRW &S)
Definition Flow.hpp:24
MFEM_HOST_DEVICE void Normalize(const int dim, mfem::real_t *vec)
Definition theseus_kernels.hpp:24
MFEM_HOST_DEVICE mfem::real_t rsqrt(mfem::real_t x)
Definition theseus_kernels.hpp:19
MFEM_HOST_DEVICE mfem::real_t Dot(const int dim, const mfem::real_t *vec1, const mfem::real_t *vec2)
Definition theseus_kernels.hpp:82
Definition AxisymmetricGeometry.hpp:15
constexpr const int MAXDIM
Definition theseus_kernels.hpp:14
constexpr const int MAXEQ
Definition theseus_kernels.hpp:13
BCType
Definition bc_cache_utilities.hpp:12
static constexpr int radial_coordinate
Definition AxisymmetricGeometry.hpp:20
Definition bc_cache_utilities.hpp:35
int type
Definition bc_cache_utilities.hpp:36
int data_index
Definition bc_cache_utilities.hpp:38
Definition GasState.hpp:218
MFEM_HOST_DEVICE mfem::real_t energy(const StateLayout &L) const
Definition GasState.hpp:295
MFEM_HOST_DEVICE mfem::real_t momentum(const StateLayout &L, int d) const
Definition GasState.hpp:245
Definition GasState.hpp:127
MFEM_HOST_DEVICE mfem::real_t velocity(const StateLayout &L, int d) const
Definition GasState.hpp:169