xref: /honee/qfunctions/bc_outflow.h (revision 224fc8c867bfdf53209e49be2150ec3eb3d75900)
1*224fc8c8SJames Wright // SPDX-FileCopyrightText: Copyright (c) 2017-2025, HONEE contributors.
2*224fc8c8SJames Wright // SPDX-License-Identifier: Apache-2.0 OR BSD-2-Clause
3*224fc8c8SJames Wright 
4*224fc8c8SJames Wright /// @file
5*224fc8c8SJames Wright /// QFunctions for the `bc_freestream` and `bc_outflow` boundary conditions
6*224fc8c8SJames Wright #include "bc_freestream_type.h"
7*224fc8c8SJames Wright #include "newtonian_state.h"
8*224fc8c8SJames Wright #include "newtonian_types.h"
9*224fc8c8SJames Wright #include "riemann_solver.h"
10*224fc8c8SJames Wright 
11*224fc8c8SJames Wright // Note the identity
12*224fc8c8SJames Wright //
13*224fc8c8SJames Wright // softplus(x) - x = log(1 + exp(x)) - x
14*224fc8c8SJames Wright //                 = log(1 + exp(x)) + log(exp(-x))
15*224fc8c8SJames Wright //                 = log((1 + exp(x)) * exp(-x))
16*224fc8c8SJames Wright //                 = log(exp(-x) + 1)
17*224fc8c8SJames Wright //                 = softplus(-x)
18*224fc8c8SJames Wright CEED_QFUNCTION_HELPER CeedScalar Softplus(CeedScalar x, CeedScalar width) {
19*224fc8c8SJames Wright   if (x > 40 * width) return x;
20*224fc8c8SJames Wright   return width * log1p(exp(x / width));
21*224fc8c8SJames Wright }
22*224fc8c8SJames Wright 
23*224fc8c8SJames Wright CEED_QFUNCTION_HELPER CeedScalar Softplus_fwd(CeedScalar x, CeedScalar dx, CeedScalar width) {
24*224fc8c8SJames Wright   if (x > 40 * width) return 1;
25*224fc8c8SJames Wright   const CeedScalar t = exp(x / width);
26*224fc8c8SJames Wright   return t / (1 + t);
27*224fc8c8SJames Wright }
28*224fc8c8SJames Wright 
29*224fc8c8SJames Wright // Viscous Outflow boundary condition, setting a constant exterior pressure and
30*224fc8c8SJames Wright // temperature as input for a Riemann solve. This condition is stable even in
31*224fc8c8SJames Wright // recirculating flow so long as the exterior temperature is sensible.
32*224fc8c8SJames Wright //
33*224fc8c8SJames Wright // The velocity in the exterior state has optional softplus regularization to
34*224fc8c8SJames Wright // keep it outflow. These parameters have been finnicky in practice and provide
35*224fc8c8SJames Wright // little or no benefit in the tests we've run thus far, thus we recommend
36*224fc8c8SJames Wright // skipping this feature and just allowing recirculation.
37*224fc8c8SJames Wright CEED_QFUNCTION_HELPER int RiemannOutflow(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out, StateVariable state_var) {
38*224fc8c8SJames Wright   const OutflowContext outflow     = (OutflowContext)ctx;
39*224fc8c8SJames Wright   const CeedScalar(*q)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0];
40*224fc8c8SJames Wright   const CeedScalar(*Grad_q)        = in[1];
41*224fc8c8SJames Wright   const CeedScalar(*q_data_sur)    = in[2];
42*224fc8c8SJames Wright   CeedScalar(*v)[CEED_Q_VLA]       = (CeedScalar(*)[CEED_Q_VLA])out[0];
43*224fc8c8SJames Wright   CeedScalar(*jac_data_sur)        = outflow->gas.is_implicit ? out[1] : NULL;
44*224fc8c8SJames Wright 
45*224fc8c8SJames Wright   const NewtonianIdealGasContext gas         = &outflow->gas;
46*224fc8c8SJames Wright   const bool                     is_implicit = gas->is_implicit;
47*224fc8c8SJames Wright 
48*224fc8c8SJames Wright   CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
49*224fc8c8SJames Wright     CeedScalar wdetJb, dXdx[2][3], normal[3];
50*224fc8c8SJames Wright     QdataBoundaryUnpack_3D(Q, i, q_data_sur, &wdetJb, dXdx, normal);
51*224fc8c8SJames Wright     wdetJb *= is_implicit ? -1. : 1.;
52*224fc8c8SJames Wright     const CeedScalar qi[5] = {q[0][i], q[1][i], q[2][i], q[3][i], q[4][i]};
53*224fc8c8SJames Wright     const State      s_int = StateFromQ(gas, qi, state_var);
54*224fc8c8SJames Wright 
55*224fc8c8SJames Wright     StatePrimitive y_ext      = s_int.Y;
56*224fc8c8SJames Wright     y_ext.pressure            = outflow->pressure;
57*224fc8c8SJames Wright     y_ext.temperature         = outflow->temperature;
58*224fc8c8SJames Wright     const CeedScalar u_normal = Dot3(y_ext.velocity, normal);
59*224fc8c8SJames Wright     const CeedScalar proj     = (1 - outflow->recirc) * Softplus(-u_normal, outflow->softplus_velocity);
60*224fc8c8SJames Wright     for (CeedInt j = 0; j < 3; j++) {
61*224fc8c8SJames Wright       y_ext.velocity[j] += normal[j] * proj;  // (I - n n^T) projects into the plane tangent to the normal
62*224fc8c8SJames Wright     }
63*224fc8c8SJames Wright     State s_ext = StateFromPrimitive(gas, y_ext);
64*224fc8c8SJames Wright 
65*224fc8c8SJames Wright     State grad_s[3];
66*224fc8c8SJames Wright     StatePhysicalGradientFromReference_Boundary(Q, i, gas, s_int, state_var, Grad_q, dXdx, grad_s);
67*224fc8c8SJames Wright 
68*224fc8c8SJames Wright     CeedScalar strain_rate[6], kmstress[6], stress[3][3], Fe[3];
69*224fc8c8SJames Wright     KMStrainRate_State(grad_s, strain_rate);
70*224fc8c8SJames Wright     NewtonianStress(gas, strain_rate, kmstress);
71*224fc8c8SJames Wright     KMUnpack(kmstress, stress);
72*224fc8c8SJames Wright     ViscousEnergyFlux(gas, s_int.Y, grad_s, stress, Fe);
73*224fc8c8SJames Wright 
74*224fc8c8SJames Wright     StateConservative F_inviscid_normal = RiemannFlux_HLLC(gas, s_int, s_ext, normal);
75*224fc8c8SJames Wright 
76*224fc8c8SJames Wright     CeedScalar Flux[5];
77*224fc8c8SJames Wright     FluxTotal_RiemannBoundary(F_inviscid_normal, stress, Fe, normal, Flux);
78*224fc8c8SJames Wright 
79*224fc8c8SJames Wright     for (CeedInt j = 0; j < 5; j++) v[j][i] = -wdetJb * Flux[j];
80*224fc8c8SJames Wright 
81*224fc8c8SJames Wright     // Save values for Jacobian
82*224fc8c8SJames Wright     if (is_implicit) {
83*224fc8c8SJames Wright       StoredValuesPack(Q, i, 0, 5, qi, jac_data_sur);
84*224fc8c8SJames Wright       StoredValuesPack(Q, i, 5, 6, kmstress, jac_data_sur);
85*224fc8c8SJames Wright     }
86*224fc8c8SJames Wright   }
87*224fc8c8SJames Wright   return 0;
88*224fc8c8SJames Wright }
89*224fc8c8SJames Wright 
90*224fc8c8SJames Wright CEED_QFUNCTION(RiemannOutflow_Conserv)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
91*224fc8c8SJames Wright   return RiemannOutflow(ctx, Q, in, out, STATEVAR_CONSERVATIVE);
92*224fc8c8SJames Wright }
93*224fc8c8SJames Wright 
94*224fc8c8SJames Wright CEED_QFUNCTION(RiemannOutflow_Prim)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
95*224fc8c8SJames Wright   return RiemannOutflow(ctx, Q, in, out, STATEVAR_PRIMITIVE);
96*224fc8c8SJames Wright }
97*224fc8c8SJames Wright 
98*224fc8c8SJames Wright CEED_QFUNCTION(RiemannOutflow_Entropy)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
99*224fc8c8SJames Wright   return RiemannOutflow(ctx, Q, in, out, STATEVAR_ENTROPY);
100*224fc8c8SJames Wright }
101*224fc8c8SJames Wright 
102*224fc8c8SJames Wright // *****************************************************************************
103*224fc8c8SJames Wright // Jacobian for Riemann pressure/temperature outflow boundary condition
104*224fc8c8SJames Wright // *****************************************************************************
105*224fc8c8SJames Wright CEED_QFUNCTION_HELPER int RiemannOutflow_Jacobian(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out,
106*224fc8c8SJames Wright                                                   StateVariable state_var) {
107*224fc8c8SJames Wright   const CeedScalar(*dq)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0];
108*224fc8c8SJames Wright   const CeedScalar(*Grad_dq)        = in[1];
109*224fc8c8SJames Wright   const CeedScalar(*q_data_sur)     = in[2];
110*224fc8c8SJames Wright   const CeedScalar(*jac_data_sur)   = in[4];
111*224fc8c8SJames Wright   CeedScalar(*v)[CEED_Q_VLA]        = (CeedScalar(*)[CEED_Q_VLA])out[0];
112*224fc8c8SJames Wright 
113*224fc8c8SJames Wright   const OutflowContext           outflow     = (OutflowContext)ctx;
114*224fc8c8SJames Wright   const NewtonianIdealGasContext gas         = &outflow->gas;
115*224fc8c8SJames Wright   const bool                     is_implicit = gas->is_implicit;
116*224fc8c8SJames Wright 
117*224fc8c8SJames Wright   CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
118*224fc8c8SJames Wright     CeedScalar wdetJb, dXdx[2][3], normal[3];
119*224fc8c8SJames Wright     QdataBoundaryUnpack_3D(Q, i, q_data_sur, &wdetJb, dXdx, normal);
120*224fc8c8SJames Wright     wdetJb *= is_implicit ? -1. : 1.;
121*224fc8c8SJames Wright 
122*224fc8c8SJames Wright     CeedScalar qi[5], kmstress[6], dqi[5];
123*224fc8c8SJames Wright     StoredValuesUnpack(Q, i, 0, 5, jac_data_sur, qi);
124*224fc8c8SJames Wright     StoredValuesUnpack(Q, i, 5, 6, jac_data_sur, kmstress);
125*224fc8c8SJames Wright     for (int j = 0; j < 5; j++) dqi[j] = dq[j][i];
126*224fc8c8SJames Wright 
127*224fc8c8SJames Wright     State          s_int  = StateFromQ(gas, qi, state_var);
128*224fc8c8SJames Wright     const State    ds_int = StateFromQ_fwd(gas, s_int, dqi, state_var);
129*224fc8c8SJames Wright     StatePrimitive y_ext = s_int.Y, dy_ext = ds_int.Y;
130*224fc8c8SJames Wright     y_ext.pressure             = outflow->pressure;
131*224fc8c8SJames Wright     y_ext.temperature          = outflow->temperature;
132*224fc8c8SJames Wright     dy_ext.pressure            = 0;
133*224fc8c8SJames Wright     dy_ext.temperature         = 0;
134*224fc8c8SJames Wright     const CeedScalar u_normal  = Dot3(s_int.Y.velocity, normal);
135*224fc8c8SJames Wright     const CeedScalar du_normal = Dot3(ds_int.Y.velocity, normal);
136*224fc8c8SJames Wright     const CeedScalar proj      = (1 - outflow->recirc) * Softplus(-u_normal, outflow->softplus_velocity);
137*224fc8c8SJames Wright     const CeedScalar dproj     = (1 - outflow->recirc) * Softplus_fwd(-u_normal, -du_normal, outflow->softplus_velocity);
138*224fc8c8SJames Wright     for (CeedInt j = 0; j < 3; j++) {
139*224fc8c8SJames Wright       y_ext.velocity[j] += normal[j] * proj;
140*224fc8c8SJames Wright       dy_ext.velocity[j] += normal[j] * dproj;
141*224fc8c8SJames Wright     }
142*224fc8c8SJames Wright 
143*224fc8c8SJames Wright     State s_ext  = StateFromPrimitive(gas, y_ext);
144*224fc8c8SJames Wright     State ds_ext = StateFromPrimitive_fwd(gas, s_ext, dy_ext);
145*224fc8c8SJames Wright 
146*224fc8c8SJames Wright     State grad_ds[3];
147*224fc8c8SJames Wright     StatePhysicalGradientFromReference_Boundary(Q, i, gas, s_int, state_var, Grad_dq, dXdx, grad_ds);
148*224fc8c8SJames Wright 
149*224fc8c8SJames Wright     CeedScalar dstrain_rate[6], dkmstress[6], stress[3][3], dstress[3][3], dFe[3];
150*224fc8c8SJames Wright     KMStrainRate_State(grad_ds, dstrain_rate);
151*224fc8c8SJames Wright     NewtonianStress(gas, dstrain_rate, dkmstress);
152*224fc8c8SJames Wright     KMUnpack(dkmstress, dstress);
153*224fc8c8SJames Wright     KMUnpack(kmstress, stress);
154*224fc8c8SJames Wright     ViscousEnergyFlux_fwd(gas, s_int.Y, ds_int.Y, grad_ds, stress, dstress, dFe);
155*224fc8c8SJames Wright 
156*224fc8c8SJames Wright     StateConservative dF_inviscid_normal = RiemannFlux_HLLC_fwd(gas, s_int, ds_int, s_ext, ds_ext, normal);
157*224fc8c8SJames Wright 
158*224fc8c8SJames Wright     CeedScalar dFlux[5];
159*224fc8c8SJames Wright     FluxTotal_RiemannBoundary(dF_inviscid_normal, dstress, dFe, normal, dFlux);
160*224fc8c8SJames Wright 
161*224fc8c8SJames Wright     for (int j = 0; j < 5; j++) v[j][i] = -wdetJb * dFlux[j];
162*224fc8c8SJames Wright   }
163*224fc8c8SJames Wright   return 0;
164*224fc8c8SJames Wright }
165*224fc8c8SJames Wright 
166*224fc8c8SJames Wright CEED_QFUNCTION(RiemannOutflow_Jacobian_Conserv)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
167*224fc8c8SJames Wright   return RiemannOutflow_Jacobian(ctx, Q, in, out, STATEVAR_CONSERVATIVE);
168*224fc8c8SJames Wright }
169*224fc8c8SJames Wright 
170*224fc8c8SJames Wright CEED_QFUNCTION(RiemannOutflow_Jacobian_Prim)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
171*224fc8c8SJames Wright   return RiemannOutflow_Jacobian(ctx, Q, in, out, STATEVAR_PRIMITIVE);
172*224fc8c8SJames Wright }
173*224fc8c8SJames Wright 
174*224fc8c8SJames Wright CEED_QFUNCTION(RiemannOutflow_Jacobian_Entropy)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
175*224fc8c8SJames Wright   return RiemannOutflow_Jacobian(ctx, Q, in, out, STATEVAR_ENTROPY);
176*224fc8c8SJames Wright }
177*224fc8c8SJames Wright 
178*224fc8c8SJames Wright // *****************************************************************************
179*224fc8c8SJames Wright // Outflow boundary condition, weakly setting a constant pressure. This is the
180*224fc8c8SJames Wright // classic outflow condition used by PHASTA-C and retained largely for
181*224fc8c8SJames Wright // comparison. In our experiments, it is never better than RiemannOutflow, and
182*224fc8c8SJames Wright // will crash if outflow ever becomes an inflow, as occurs with strong
183*224fc8c8SJames Wright // acoustics, vortices, etc.
184*224fc8c8SJames Wright // *****************************************************************************
185*224fc8c8SJames Wright CEED_QFUNCTION_HELPER int PressureOutflow(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out, StateVariable state_var) {
186*224fc8c8SJames Wright   const OutflowContext outflow     = (OutflowContext)ctx;
187*224fc8c8SJames Wright   const CeedScalar(*q)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0];
188*224fc8c8SJames Wright   const CeedScalar(*Grad_q)        = in[1];
189*224fc8c8SJames Wright   const CeedScalar(*q_data_sur)    = in[2];
190*224fc8c8SJames Wright   CeedScalar(*v)[CEED_Q_VLA]       = (CeedScalar(*)[CEED_Q_VLA])out[0];
191*224fc8c8SJames Wright   CeedScalar(*jac_data_sur)        = outflow->gas.is_implicit ? out[1] : NULL;
192*224fc8c8SJames Wright 
193*224fc8c8SJames Wright   const NewtonianIdealGasContext gas         = &outflow->gas;
194*224fc8c8SJames Wright   const bool                     is_implicit = gas->is_implicit;
195*224fc8c8SJames Wright 
196*224fc8c8SJames Wright   CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
197*224fc8c8SJames Wright     const CeedScalar qi[5] = {q[0][i], q[1][i], q[2][i], q[3][i], q[4][i]};
198*224fc8c8SJames Wright     State            s     = StateFromQ(gas, qi, state_var);
199*224fc8c8SJames Wright     s.Y.pressure           = outflow->pressure;
200*224fc8c8SJames Wright 
201*224fc8c8SJames Wright     CeedScalar wdetJb, dXdx[2][3], normal[3];
202*224fc8c8SJames Wright     QdataBoundaryUnpack_3D(Q, i, q_data_sur, &wdetJb, dXdx, normal);
203*224fc8c8SJames Wright     wdetJb *= is_implicit ? -1. : 1.;
204*224fc8c8SJames Wright 
205*224fc8c8SJames Wright     State grad_s[3];
206*224fc8c8SJames Wright     StatePhysicalGradientFromReference_Boundary(Q, i, gas, s, state_var, Grad_q, dXdx, grad_s);
207*224fc8c8SJames Wright 
208*224fc8c8SJames Wright     CeedScalar strain_rate[6], kmstress[6], stress[3][3], Fe[3];
209*224fc8c8SJames Wright     KMStrainRate_State(grad_s, strain_rate);
210*224fc8c8SJames Wright     NewtonianStress(gas, strain_rate, kmstress);
211*224fc8c8SJames Wright     KMUnpack(kmstress, stress);
212*224fc8c8SJames Wright     ViscousEnergyFlux(gas, s.Y, grad_s, stress, Fe);
213*224fc8c8SJames Wright 
214*224fc8c8SJames Wright     StateConservative F_inviscid[3];
215*224fc8c8SJames Wright     FluxInviscid(gas, s, F_inviscid);
216*224fc8c8SJames Wright 
217*224fc8c8SJames Wright     CeedScalar Flux[5];
218*224fc8c8SJames Wright     FluxTotal_Boundary(F_inviscid, stress, Fe, normal, Flux);
219*224fc8c8SJames Wright 
220*224fc8c8SJames Wright     for (CeedInt j = 0; j < 5; j++) v[j][i] = -wdetJb * Flux[j];
221*224fc8c8SJames Wright 
222*224fc8c8SJames Wright     // Save values for Jacobian
223*224fc8c8SJames Wright     if (is_implicit) {
224*224fc8c8SJames Wright       StoredValuesPack(Q, i, 0, 5, qi, jac_data_sur);
225*224fc8c8SJames Wright       StoredValuesPack(Q, i, 5, 6, kmstress, jac_data_sur);
226*224fc8c8SJames Wright     }
227*224fc8c8SJames Wright   }
228*224fc8c8SJames Wright   return 0;
229*224fc8c8SJames Wright }
230*224fc8c8SJames Wright 
231*224fc8c8SJames Wright CEED_QFUNCTION(PressureOutflow_Conserv)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
232*224fc8c8SJames Wright   return PressureOutflow(ctx, Q, in, out, STATEVAR_CONSERVATIVE);
233*224fc8c8SJames Wright }
234*224fc8c8SJames Wright 
235*224fc8c8SJames Wright CEED_QFUNCTION(PressureOutflow_Prim)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
236*224fc8c8SJames Wright   return PressureOutflow(ctx, Q, in, out, STATEVAR_PRIMITIVE);
237*224fc8c8SJames Wright }
238*224fc8c8SJames Wright 
239*224fc8c8SJames Wright CEED_QFUNCTION(PressureOutflow_Entropy)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
240*224fc8c8SJames Wright   return PressureOutflow(ctx, Q, in, out, STATEVAR_ENTROPY);
241*224fc8c8SJames Wright }
242*224fc8c8SJames Wright 
243*224fc8c8SJames Wright // *****************************************************************************
244*224fc8c8SJames Wright // Jacobian for weak-pressure outflow boundary condition
245*224fc8c8SJames Wright // *****************************************************************************
246*224fc8c8SJames Wright CEED_QFUNCTION_HELPER int PressureOutflow_Jacobian(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out,
247*224fc8c8SJames Wright                                                    StateVariable state_var) {
248*224fc8c8SJames Wright   const CeedScalar(*dq)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0];
249*224fc8c8SJames Wright   const CeedScalar(*Grad_dq)        = in[1];
250*224fc8c8SJames Wright   const CeedScalar(*q_data_sur)     = in[2];
251*224fc8c8SJames Wright   const CeedScalar(*jac_data_sur)   = in[4];
252*224fc8c8SJames Wright   CeedScalar(*v)[CEED_Q_VLA]        = (CeedScalar(*)[CEED_Q_VLA])out[0];
253*224fc8c8SJames Wright 
254*224fc8c8SJames Wright   const OutflowContext           outflow     = (OutflowContext)ctx;
255*224fc8c8SJames Wright   const NewtonianIdealGasContext gas         = &outflow->gas;
256*224fc8c8SJames Wright   const bool                     is_implicit = gas->is_implicit;
257*224fc8c8SJames Wright 
258*224fc8c8SJames Wright   CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
259*224fc8c8SJames Wright     CeedScalar wdetJb, dXdx[2][3], normal[3];
260*224fc8c8SJames Wright     QdataBoundaryUnpack_3D(Q, i, q_data_sur, &wdetJb, dXdx, normal);
261*224fc8c8SJames Wright     wdetJb *= is_implicit ? -1. : 1.;
262*224fc8c8SJames Wright 
263*224fc8c8SJames Wright     CeedScalar qi[5], kmstress[6], dqi[5];
264*224fc8c8SJames Wright     StoredValuesUnpack(Q, i, 0, 5, jac_data_sur, qi);
265*224fc8c8SJames Wright     StoredValuesUnpack(Q, i, 5, 6, jac_data_sur, kmstress);
266*224fc8c8SJames Wright     for (int j = 0; j < 5; j++) dqi[j] = dq[j][i];
267*224fc8c8SJames Wright 
268*224fc8c8SJames Wright     State s       = StateFromQ(gas, qi, state_var);
269*224fc8c8SJames Wright     State ds      = StateFromQ_fwd(gas, s, dqi, state_var);
270*224fc8c8SJames Wright     s.Y.pressure  = outflow->pressure;
271*224fc8c8SJames Wright     ds.Y.pressure = 0.;
272*224fc8c8SJames Wright 
273*224fc8c8SJames Wright     State grad_ds[3];
274*224fc8c8SJames Wright     StatePhysicalGradientFromReference_Boundary(Q, i, gas, s, state_var, Grad_dq, dXdx, grad_ds);
275*224fc8c8SJames Wright 
276*224fc8c8SJames Wright     CeedScalar dstrain_rate[6], dkmstress[6], stress[3][3], dstress[3][3], dFe[3];
277*224fc8c8SJames Wright     KMStrainRate_State(grad_ds, dstrain_rate);
278*224fc8c8SJames Wright     NewtonianStress(gas, dstrain_rate, dkmstress);
279*224fc8c8SJames Wright     KMUnpack(dkmstress, dstress);
280*224fc8c8SJames Wright     KMUnpack(kmstress, stress);
281*224fc8c8SJames Wright     ViscousEnergyFlux_fwd(gas, s.Y, ds.Y, grad_ds, stress, dstress, dFe);
282*224fc8c8SJames Wright 
283*224fc8c8SJames Wright     StateConservative dF_inviscid[3];
284*224fc8c8SJames Wright     FluxInviscid_fwd(gas, s, ds, dF_inviscid);
285*224fc8c8SJames Wright 
286*224fc8c8SJames Wright     CeedScalar dFlux[5];
287*224fc8c8SJames Wright     FluxTotal_Boundary(dF_inviscid, dstress, dFe, normal, dFlux);
288*224fc8c8SJames Wright 
289*224fc8c8SJames Wright     for (int j = 0; j < 5; j++) v[j][i] = -wdetJb * dFlux[j];
290*224fc8c8SJames Wright   }
291*224fc8c8SJames Wright   return 0;
292*224fc8c8SJames Wright }
293*224fc8c8SJames Wright 
294*224fc8c8SJames Wright CEED_QFUNCTION(PressureOutflow_Jacobian_Conserv)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
295*224fc8c8SJames Wright   return PressureOutflow_Jacobian(ctx, Q, in, out, STATEVAR_CONSERVATIVE);
296*224fc8c8SJames Wright }
297*224fc8c8SJames Wright 
298*224fc8c8SJames Wright CEED_QFUNCTION(PressureOutflow_Jacobian_Prim)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
299*224fc8c8SJames Wright   return PressureOutflow_Jacobian(ctx, Q, in, out, STATEVAR_PRIMITIVE);
300*224fc8c8SJames Wright }
301*224fc8c8SJames Wright 
302*224fc8c8SJames Wright CEED_QFUNCTION(PressureOutflow_Jacobian_Entropy)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
303*224fc8c8SJames Wright   return PressureOutflow_Jacobian(ctx, Q, in, out, STATEVAR_ENTROPY);
304*224fc8c8SJames Wright }
305