xref: /libCEED/examples/fluids/qfunctions/bc_freestream.h (revision 9f84436874b0bedcb473aab1faa446b08959d274)
1*9f844368SJames Wright // Copyright (c) 2017-2022, Lawrence Livermore National Security, LLC and other CEED contributors.
2*9f844368SJames Wright // All Rights Reserved. See the top-level LICENSE and NOTICE files for details.
3*9f844368SJames Wright //
4*9f844368SJames Wright // SPDX-License-Identifier: BSD-2-Clause
5*9f844368SJames Wright //
6*9f844368SJames Wright // This file is part of CEED:  http://github.com/ceed
7*9f844368SJames Wright 
8*9f844368SJames Wright /// @file
9*9f844368SJames Wright /// QFunctions for the `bc_freestream` and `bc_outflow` boundary conditions
10*9f844368SJames Wright 
11*9f844368SJames Wright #include "bc_freestream_type.h"
12*9f844368SJames Wright #include "newtonian_state.h"
13*9f844368SJames Wright #include "newtonian_types.h"
14*9f844368SJames Wright #include "riemann_solver.h"
15*9f844368SJames Wright 
16*9f844368SJames Wright // *****************************************************************************
17*9f844368SJames Wright // Freestream Boundary Condition
18*9f844368SJames Wright // *****************************************************************************
19*9f844368SJames Wright CEED_QFUNCTION_HELPER int Freestream(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out, StateVariable state_var,
20*9f844368SJames Wright                                      RiemannFluxType flux_type) {
21*9f844368SJames Wright   const CeedScalar(*q)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0];
22*9f844368SJames Wright   const CeedScalar(*q_data_sur)    = in[2];
23*9f844368SJames Wright 
24*9f844368SJames Wright   CeedScalar(*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0];
25*9f844368SJames Wright   CeedScalar(*jac_data_sur)  = out[1];
26*9f844368SJames Wright 
27*9f844368SJames Wright   const FreestreamContext        context     = (FreestreamContext)ctx;
28*9f844368SJames Wright   const NewtonianIdealGasContext newt_ctx    = &context->newtonian_ctx;
29*9f844368SJames Wright   const bool                     is_implicit = newt_ctx->is_implicit;
30*9f844368SJames Wright   const CeedScalar               zeros[6]    = {0.};
31*9f844368SJames Wright 
32*9f844368SJames Wright   CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
33*9f844368SJames Wright     const CeedScalar qi[5] = {q[0][i], q[1][i], q[2][i], q[3][i], q[4][i]};
34*9f844368SJames Wright     State            s     = StateFromQ(newt_ctx, qi, state_var);
35*9f844368SJames Wright 
36*9f844368SJames Wright     CeedScalar wdetJb, norm[3];
37*9f844368SJames Wright     QdataBoundaryUnpack_3D(Q, i, q_data_sur, &wdetJb, NULL, norm);
38*9f844368SJames Wright     wdetJb *= is_implicit ? -1. : 1.;
39*9f844368SJames Wright 
40*9f844368SJames Wright     StateConservative flux;
41*9f844368SJames Wright     switch (flux_type) {
42*9f844368SJames Wright       case RIEMANN_HLL:
43*9f844368SJames Wright         flux = RiemannFlux_HLL(newt_ctx, s, context->S_infty, norm);
44*9f844368SJames Wright         break;
45*9f844368SJames Wright       case RIEMANN_HLLC:
46*9f844368SJames Wright         flux = RiemannFlux_HLLC(newt_ctx, s, context->S_infty, norm);
47*9f844368SJames Wright         break;
48*9f844368SJames Wright     }
49*9f844368SJames Wright     CeedScalar Flux[5];
50*9f844368SJames Wright     UnpackState_U(flux, Flux);
51*9f844368SJames Wright     for (CeedInt j = 0; j < 5; j++) v[j][i] = -wdetJb * Flux[j];
52*9f844368SJames Wright 
53*9f844368SJames Wright     StoredValuesPack(Q, i, 0, 5, qi, jac_data_sur);
54*9f844368SJames Wright     StoredValuesPack(Q, i, 5, 6, zeros, jac_data_sur);  // Every output value must be set
55*9f844368SJames Wright   }
56*9f844368SJames Wright   return 0;
57*9f844368SJames Wright }
58*9f844368SJames Wright 
59*9f844368SJames Wright CEED_QFUNCTION(Freestream_Conserv_HLL)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
60*9f844368SJames Wright   return Freestream(ctx, Q, in, out, STATEVAR_CONSERVATIVE, RIEMANN_HLL);
61*9f844368SJames Wright }
62*9f844368SJames Wright 
63*9f844368SJames Wright CEED_QFUNCTION(Freestream_Prim_HLL)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
64*9f844368SJames Wright   return Freestream(ctx, Q, in, out, STATEVAR_PRIMITIVE, RIEMANN_HLL);
65*9f844368SJames Wright }
66*9f844368SJames Wright 
67*9f844368SJames Wright CEED_QFUNCTION(Freestream_Conserv_HLLC)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
68*9f844368SJames Wright   return Freestream(ctx, Q, in, out, STATEVAR_CONSERVATIVE, RIEMANN_HLLC);
69*9f844368SJames Wright }
70*9f844368SJames Wright 
71*9f844368SJames Wright CEED_QFUNCTION(Freestream_Prim_HLLC)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
72*9f844368SJames Wright   return Freestream(ctx, Q, in, out, STATEVAR_PRIMITIVE, RIEMANN_HLLC);
73*9f844368SJames Wright }
74*9f844368SJames Wright 
75*9f844368SJames Wright CEED_QFUNCTION_HELPER int Freestream_Jacobian(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out, StateVariable state_var,
76*9f844368SJames Wright                                               RiemannFluxType flux_type) {
77*9f844368SJames Wright   const CeedScalar(*dq)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0];
78*9f844368SJames Wright   const CeedScalar(*q_data_sur)     = in[2];
79*9f844368SJames Wright   const CeedScalar(*jac_data_sur)   = in[4];
80*9f844368SJames Wright 
81*9f844368SJames Wright   CeedScalar(*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0];
82*9f844368SJames Wright 
83*9f844368SJames Wright   const FreestreamContext        context     = (FreestreamContext)ctx;
84*9f844368SJames Wright   const NewtonianIdealGasContext newt_ctx    = &context->newtonian_ctx;
85*9f844368SJames Wright   const bool                     is_implicit = newt_ctx->is_implicit;
86*9f844368SJames Wright   const State                    dS_infty    = {0};
87*9f844368SJames Wright 
88*9f844368SJames Wright   CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
89*9f844368SJames Wright     CeedScalar wdetJb, norm[3];
90*9f844368SJames Wright     QdataBoundaryUnpack_3D(Q, i, q_data_sur, &wdetJb, NULL, norm);
91*9f844368SJames Wright     wdetJb *= is_implicit ? -1. : 1.;
92*9f844368SJames Wright 
93*9f844368SJames Wright     CeedScalar qi[5], dqi[5];
94*9f844368SJames Wright     StoredValuesUnpack(Q, i, 0, 5, jac_data_sur, qi);
95*9f844368SJames Wright     for (int j = 0; j < 5; j++) dqi[j] = dq[j][i];
96*9f844368SJames Wright     State s  = StateFromQ(newt_ctx, qi, state_var);
97*9f844368SJames Wright     State ds = StateFromQ_fwd(newt_ctx, s, dqi, state_var);
98*9f844368SJames Wright 
99*9f844368SJames Wright     StateConservative dflux;
100*9f844368SJames Wright     switch (flux_type) {
101*9f844368SJames Wright       case RIEMANN_HLL:
102*9f844368SJames Wright         dflux = RiemannFlux_HLL_fwd(newt_ctx, s, ds, context->S_infty, dS_infty, norm);
103*9f844368SJames Wright         break;
104*9f844368SJames Wright       case RIEMANN_HLLC:
105*9f844368SJames Wright         dflux = RiemannFlux_HLLC_fwd(newt_ctx, s, ds, context->S_infty, dS_infty, norm);
106*9f844368SJames Wright         break;
107*9f844368SJames Wright     }
108*9f844368SJames Wright     CeedScalar dFlux[5];
109*9f844368SJames Wright     UnpackState_U(dflux, dFlux);
110*9f844368SJames Wright     for (CeedInt j = 0; j < 5; j++) v[j][i] = -wdetJb * dFlux[j];
111*9f844368SJames Wright   }
112*9f844368SJames Wright   return 0;
113*9f844368SJames Wright }
114*9f844368SJames Wright 
115*9f844368SJames Wright CEED_QFUNCTION(Freestream_Jacobian_Conserv_HLL)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
116*9f844368SJames Wright   return Freestream_Jacobian(ctx, Q, in, out, STATEVAR_CONSERVATIVE, RIEMANN_HLL);
117*9f844368SJames Wright }
118*9f844368SJames Wright 
119*9f844368SJames Wright CEED_QFUNCTION(Freestream_Jacobian_Prim_HLL)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
120*9f844368SJames Wright   return Freestream_Jacobian(ctx, Q, in, out, STATEVAR_PRIMITIVE, RIEMANN_HLL);
121*9f844368SJames Wright }
122*9f844368SJames Wright 
123*9f844368SJames Wright CEED_QFUNCTION(Freestream_Jacobian_Conserv_HLLC)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
124*9f844368SJames Wright   return Freestream_Jacobian(ctx, Q, in, out, STATEVAR_CONSERVATIVE, RIEMANN_HLLC);
125*9f844368SJames Wright }
126*9f844368SJames Wright 
127*9f844368SJames Wright CEED_QFUNCTION(Freestream_Jacobian_Prim_HLLC)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
128*9f844368SJames Wright   return Freestream_Jacobian(ctx, Q, in, out, STATEVAR_PRIMITIVE, RIEMANN_HLLC);
129*9f844368SJames Wright }
130*9f844368SJames Wright 
131*9f844368SJames Wright // Note the identity
132*9f844368SJames Wright //
133*9f844368SJames Wright // softplus(x) - x = log(1 + exp(x)) - x
134*9f844368SJames Wright //                 = log(1 + exp(x)) + log(exp(-x))
135*9f844368SJames Wright //                 = log((1 + exp(x)) * exp(-x))
136*9f844368SJames Wright //                 = log(exp(-x) + 1)
137*9f844368SJames Wright //                 = softplus(-x)
138*9f844368SJames Wright CEED_QFUNCTION_HELPER CeedScalar Softplus(CeedScalar x, CeedScalar width) {
139*9f844368SJames Wright   if (x > 40 * width) return x;
140*9f844368SJames Wright   return width * log1p(exp(x / width));
141*9f844368SJames Wright }
142*9f844368SJames Wright 
143*9f844368SJames Wright CEED_QFUNCTION_HELPER CeedScalar Softplus_fwd(CeedScalar x, CeedScalar dx, CeedScalar width) {
144*9f844368SJames Wright   if (x > 40 * width) return 1;
145*9f844368SJames Wright   const CeedScalar t = exp(x / width);
146*9f844368SJames Wright   return t / (1 + t);
147*9f844368SJames Wright }
148*9f844368SJames Wright 
149*9f844368SJames Wright // Viscous Outflow boundary condition, setting a constant exterior pressure and
150*9f844368SJames Wright // temperature as input for a Riemann solve. This condition is stable even in
151*9f844368SJames Wright // recirculating flow so long as the exterior temperature is sensible.
152*9f844368SJames Wright //
153*9f844368SJames Wright // The velocity in the exterior state has optional softplus regularization to
154*9f844368SJames Wright // keep it outflow. These parameters have been finnicky in practice and provide
155*9f844368SJames Wright // little or no benefit in the tests we've run thus far, thus we recommend
156*9f844368SJames Wright // skipping this feature and just allowing recirculation.
157*9f844368SJames Wright CEED_QFUNCTION_HELPER int RiemannOutflow(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out, StateVariable state_var) {
158*9f844368SJames Wright   // Inputs
159*9f844368SJames Wright   const CeedScalar(*q)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0];
160*9f844368SJames Wright   const CeedScalar(*Grad_q)        = in[1];
161*9f844368SJames Wright   const CeedScalar(*q_data_sur)    = in[2];
162*9f844368SJames Wright 
163*9f844368SJames Wright   // Outputs
164*9f844368SJames Wright   CeedScalar(*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0];
165*9f844368SJames Wright   CeedScalar(*jac_data_sur)  = out[1];
166*9f844368SJames Wright 
167*9f844368SJames Wright   const OutflowContext           outflow     = (OutflowContext)ctx;
168*9f844368SJames Wright   const NewtonianIdealGasContext gas         = &outflow->gas;
169*9f844368SJames Wright   const bool                     is_implicit = gas->is_implicit;
170*9f844368SJames Wright 
171*9f844368SJames Wright   CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
172*9f844368SJames Wright     CeedScalar wdetJb, dXdx[2][3], norm[3];
173*9f844368SJames Wright     QdataBoundaryUnpack_3D(Q, i, q_data_sur, &wdetJb, dXdx, norm);
174*9f844368SJames Wright     wdetJb *= is_implicit ? -1. : 1.;
175*9f844368SJames Wright     const CeedScalar qi[5] = {q[0][i], q[1][i], q[2][i], q[3][i], q[4][i]};
176*9f844368SJames Wright     State            s_int = StateFromQ(gas, qi, state_var);
177*9f844368SJames Wright 
178*9f844368SJames Wright     StatePrimitive y_ext      = s_int.Y;
179*9f844368SJames Wright     y_ext.pressure            = outflow->pressure;
180*9f844368SJames Wright     y_ext.temperature         = outflow->temperature;
181*9f844368SJames Wright     const CeedScalar u_normal = Dot3(y_ext.velocity, norm);
182*9f844368SJames Wright     const CeedScalar proj     = (1 - outflow->recirc) * Softplus(-u_normal, outflow->softplus_velocity);
183*9f844368SJames Wright     for (CeedInt j = 0; j < 3; j++) {
184*9f844368SJames Wright       y_ext.velocity[j] += norm[j] * proj;  // (I - n n^T) projects into the plane tangent to the normal
185*9f844368SJames Wright     }
186*9f844368SJames Wright     State s_ext = StateFromPrimitive(gas, y_ext);
187*9f844368SJames Wright 
188*9f844368SJames Wright     State grad_s[3];
189*9f844368SJames Wright     StatePhysicalGradientFromReference_Boundary(Q, i, gas, s_int, state_var, Grad_q, dXdx, grad_s);
190*9f844368SJames Wright 
191*9f844368SJames Wright     CeedScalar strain_rate[6], kmstress[6], stress[3][3], Fe[3];
192*9f844368SJames Wright     KMStrainRate_State(grad_s, strain_rate);
193*9f844368SJames Wright     NewtonianStress(gas, strain_rate, kmstress);
194*9f844368SJames Wright     KMUnpack(kmstress, stress);
195*9f844368SJames Wright     ViscousEnergyFlux(gas, s_int.Y, grad_s, stress, Fe);
196*9f844368SJames Wright 
197*9f844368SJames Wright     StateConservative F_inviscid_normal = RiemannFlux_HLLC(gas, s_int, s_ext, norm);
198*9f844368SJames Wright 
199*9f844368SJames Wright     CeedScalar Flux[5];
200*9f844368SJames Wright     FluxTotal_RiemannBoundary(F_inviscid_normal, stress, Fe, norm, Flux);
201*9f844368SJames Wright 
202*9f844368SJames Wright     for (CeedInt j = 0; j < 5; j++) v[j][i] = -wdetJb * Flux[j];
203*9f844368SJames Wright 
204*9f844368SJames Wright     // Save values for Jacobian
205*9f844368SJames Wright     StoredValuesPack(Q, i, 0, 5, qi, jac_data_sur);
206*9f844368SJames Wright     StoredValuesPack(Q, i, 5, 6, kmstress, jac_data_sur);
207*9f844368SJames Wright   }
208*9f844368SJames Wright   return 0;
209*9f844368SJames Wright }
210*9f844368SJames Wright 
211*9f844368SJames Wright CEED_QFUNCTION(RiemannOutflow_Conserv)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
212*9f844368SJames Wright   return RiemannOutflow(ctx, Q, in, out, STATEVAR_CONSERVATIVE);
213*9f844368SJames Wright }
214*9f844368SJames Wright 
215*9f844368SJames Wright CEED_QFUNCTION(RiemannOutflow_Prim)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
216*9f844368SJames Wright   return RiemannOutflow(ctx, Q, in, out, STATEVAR_PRIMITIVE);
217*9f844368SJames Wright }
218*9f844368SJames Wright 
219*9f844368SJames Wright // *****************************************************************************
220*9f844368SJames Wright // Jacobian for Riemann pressure/temperature outflow boundary condition
221*9f844368SJames Wright // *****************************************************************************
222*9f844368SJames Wright CEED_QFUNCTION_HELPER int RiemannOutflow_Jacobian(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out,
223*9f844368SJames Wright                                                   StateVariable state_var) {
224*9f844368SJames Wright   // Inputs
225*9f844368SJames Wright   const CeedScalar(*dq)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0];
226*9f844368SJames Wright   const CeedScalar(*Grad_dq)        = in[1];
227*9f844368SJames Wright   const CeedScalar(*q_data_sur)     = in[2];
228*9f844368SJames Wright   const CeedScalar(*jac_data_sur)   = in[4];
229*9f844368SJames Wright 
230*9f844368SJames Wright   // Outputs
231*9f844368SJames Wright   CeedScalar(*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0];
232*9f844368SJames Wright 
233*9f844368SJames Wright   const OutflowContext           outflow     = (OutflowContext)ctx;
234*9f844368SJames Wright   const NewtonianIdealGasContext gas         = &outflow->gas;
235*9f844368SJames Wright   const bool                     is_implicit = gas->is_implicit;
236*9f844368SJames Wright 
237*9f844368SJames Wright   // Quadrature Point Loop
238*9f844368SJames Wright   CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
239*9f844368SJames Wright     CeedScalar wdetJb, dXdx[2][3], norm[3];
240*9f844368SJames Wright     QdataBoundaryUnpack_3D(Q, i, q_data_sur, &wdetJb, dXdx, norm);
241*9f844368SJames Wright     wdetJb *= is_implicit ? -1. : 1.;
242*9f844368SJames Wright 
243*9f844368SJames Wright     CeedScalar qi[5], kmstress[6], dqi[5];
244*9f844368SJames Wright     StoredValuesUnpack(Q, i, 0, 5, jac_data_sur, qi);
245*9f844368SJames Wright     StoredValuesUnpack(Q, i, 5, 6, jac_data_sur, kmstress);
246*9f844368SJames Wright     for (int j = 0; j < 5; j++) dqi[j] = dq[j][i];
247*9f844368SJames Wright 
248*9f844368SJames Wright     State          s_int  = StateFromQ(gas, qi, state_var);
249*9f844368SJames Wright     const State    ds_int = StateFromQ_fwd(gas, s_int, dqi, state_var);
250*9f844368SJames Wright     StatePrimitive y_ext = s_int.Y, dy_ext = ds_int.Y;
251*9f844368SJames Wright     y_ext.pressure             = outflow->pressure;
252*9f844368SJames Wright     y_ext.temperature          = outflow->temperature;
253*9f844368SJames Wright     dy_ext.pressure            = 0;
254*9f844368SJames Wright     dy_ext.temperature         = 0;
255*9f844368SJames Wright     const CeedScalar u_normal  = Dot3(s_int.Y.velocity, norm);
256*9f844368SJames Wright     const CeedScalar du_normal = Dot3(ds_int.Y.velocity, norm);
257*9f844368SJames Wright     const CeedScalar proj      = (1 - outflow->recirc) * Softplus(-u_normal, outflow->softplus_velocity);
258*9f844368SJames Wright     const CeedScalar dproj     = (1 - outflow->recirc) * Softplus_fwd(-u_normal, -du_normal, outflow->softplus_velocity);
259*9f844368SJames Wright     for (CeedInt j = 0; j < 3; j++) {
260*9f844368SJames Wright       y_ext.velocity[j] += norm[j] * proj;
261*9f844368SJames Wright       dy_ext.velocity[j] += norm[j] * dproj;
262*9f844368SJames Wright     }
263*9f844368SJames Wright 
264*9f844368SJames Wright     State s_ext  = StateFromPrimitive(gas, y_ext);
265*9f844368SJames Wright     State ds_ext = StateFromPrimitive_fwd(gas, s_ext, dy_ext);
266*9f844368SJames Wright 
267*9f844368SJames Wright     State grad_ds[3];
268*9f844368SJames Wright     StatePhysicalGradientFromReference_Boundary(Q, i, gas, s_int, state_var, Grad_dq, dXdx, grad_ds);
269*9f844368SJames Wright 
270*9f844368SJames Wright     CeedScalar dstrain_rate[6], dkmstress[6], stress[3][3], dstress[3][3], dFe[3];
271*9f844368SJames Wright     KMStrainRate_State(grad_ds, dstrain_rate);
272*9f844368SJames Wright     NewtonianStress(gas, dstrain_rate, dkmstress);
273*9f844368SJames Wright     KMUnpack(dkmstress, dstress);
274*9f844368SJames Wright     KMUnpack(kmstress, stress);
275*9f844368SJames Wright     ViscousEnergyFlux_fwd(gas, s_int.Y, ds_int.Y, grad_ds, stress, dstress, dFe);
276*9f844368SJames Wright 
277*9f844368SJames Wright     StateConservative dF_inviscid_normal = RiemannFlux_HLLC_fwd(gas, s_int, ds_int, s_ext, ds_ext, norm);
278*9f844368SJames Wright 
279*9f844368SJames Wright     CeedScalar dFlux[5];
280*9f844368SJames Wright     FluxTotal_RiemannBoundary(dF_inviscid_normal, dstress, dFe, norm, dFlux);
281*9f844368SJames Wright 
282*9f844368SJames Wright     for (int j = 0; j < 5; j++) v[j][i] = -wdetJb * dFlux[j];
283*9f844368SJames Wright   }  // End Quadrature Point Loop
284*9f844368SJames Wright   return 0;
285*9f844368SJames Wright }
286*9f844368SJames Wright 
287*9f844368SJames Wright CEED_QFUNCTION(RiemannOutflow_Jacobian_Conserv)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
288*9f844368SJames Wright   return RiemannOutflow_Jacobian(ctx, Q, in, out, STATEVAR_CONSERVATIVE);
289*9f844368SJames Wright }
290*9f844368SJames Wright 
291*9f844368SJames Wright CEED_QFUNCTION(RiemannOutflow_Jacobian_Prim)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
292*9f844368SJames Wright   return RiemannOutflow_Jacobian(ctx, Q, in, out, STATEVAR_PRIMITIVE);
293*9f844368SJames Wright }
294*9f844368SJames Wright 
295*9f844368SJames Wright // *****************************************************************************
296*9f844368SJames Wright // Outflow boundary condition, weakly setting a constant pressure. This is the
297*9f844368SJames Wright // classic outflow condition used by PHASTA-C and retained largely for
298*9f844368SJames Wright // comparison. In our experiments, it is never better than RiemannOutflow, and
299*9f844368SJames Wright // will crash if outflow ever becomes an inflow, as occurs with strong
300*9f844368SJames Wright // acoustics, vortices, etc.
301*9f844368SJames Wright // *****************************************************************************
302*9f844368SJames Wright CEED_QFUNCTION_HELPER int PressureOutflow(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out, StateVariable state_var) {
303*9f844368SJames Wright   // Inputs
304*9f844368SJames Wright   const CeedScalar(*q)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0];
305*9f844368SJames Wright   const CeedScalar(*Grad_q)        = in[1];
306*9f844368SJames Wright   const CeedScalar(*q_data_sur)    = in[2];
307*9f844368SJames Wright 
308*9f844368SJames Wright   // Outputs
309*9f844368SJames Wright   CeedScalar(*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0];
310*9f844368SJames Wright   CeedScalar(*jac_data_sur)  = out[1];
311*9f844368SJames Wright 
312*9f844368SJames Wright   const OutflowContext           outflow     = (OutflowContext)ctx;
313*9f844368SJames Wright   const NewtonianIdealGasContext gas         = &outflow->gas;
314*9f844368SJames Wright   const bool                     is_implicit = gas->is_implicit;
315*9f844368SJames Wright 
316*9f844368SJames Wright   // Quadrature Point Loop
317*9f844368SJames Wright   CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
318*9f844368SJames Wright     // Setup
319*9f844368SJames Wright     // -- Interp in
320*9f844368SJames Wright     const CeedScalar qi[5] = {q[0][i], q[1][i], q[2][i], q[3][i], q[4][i]};
321*9f844368SJames Wright     State            s     = StateFromQ(gas, qi, state_var);
322*9f844368SJames Wright     s.Y.pressure           = outflow->pressure;
323*9f844368SJames Wright 
324*9f844368SJames Wright     CeedScalar wdetJb, dXdx[2][3], norm[3];
325*9f844368SJames Wright     QdataBoundaryUnpack_3D(Q, i, q_data_sur, &wdetJb, dXdx, norm);
326*9f844368SJames Wright     wdetJb *= is_implicit ? -1. : 1.;
327*9f844368SJames Wright 
328*9f844368SJames Wright     State grad_s[3];
329*9f844368SJames Wright     StatePhysicalGradientFromReference_Boundary(Q, i, gas, s, state_var, Grad_q, dXdx, grad_s);
330*9f844368SJames Wright 
331*9f844368SJames Wright     CeedScalar strain_rate[6], kmstress[6], stress[3][3], Fe[3];
332*9f844368SJames Wright     KMStrainRate_State(grad_s, strain_rate);
333*9f844368SJames Wright     NewtonianStress(gas, strain_rate, kmstress);
334*9f844368SJames Wright     KMUnpack(kmstress, stress);
335*9f844368SJames Wright     ViscousEnergyFlux(gas, s.Y, grad_s, stress, Fe);
336*9f844368SJames Wright 
337*9f844368SJames Wright     StateConservative F_inviscid[3];
338*9f844368SJames Wright     FluxInviscid(gas, s, F_inviscid);
339*9f844368SJames Wright 
340*9f844368SJames Wright     CeedScalar Flux[5];
341*9f844368SJames Wright     FluxTotal_Boundary(F_inviscid, stress, Fe, norm, Flux);
342*9f844368SJames Wright 
343*9f844368SJames Wright     for (CeedInt j = 0; j < 5; j++) v[j][i] = -wdetJb * Flux[j];
344*9f844368SJames Wright 
345*9f844368SJames Wright     // Save values for Jacobian
346*9f844368SJames Wright     StoredValuesPack(Q, i, 0, 5, qi, jac_data_sur);
347*9f844368SJames Wright     StoredValuesPack(Q, i, 5, 6, kmstress, jac_data_sur);
348*9f844368SJames Wright   }  // End Quadrature Point Loop
349*9f844368SJames Wright   return 0;
350*9f844368SJames Wright }
351*9f844368SJames Wright 
352*9f844368SJames Wright CEED_QFUNCTION(PressureOutflow_Conserv)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
353*9f844368SJames Wright   return PressureOutflow(ctx, Q, in, out, STATEVAR_CONSERVATIVE);
354*9f844368SJames Wright }
355*9f844368SJames Wright 
356*9f844368SJames Wright CEED_QFUNCTION(PressureOutflow_Prim)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
357*9f844368SJames Wright   return PressureOutflow(ctx, Q, in, out, STATEVAR_PRIMITIVE);
358*9f844368SJames Wright }
359*9f844368SJames Wright 
360*9f844368SJames Wright // *****************************************************************************
361*9f844368SJames Wright // Jacobian for weak-pressure outflow boundary condition
362*9f844368SJames Wright // *****************************************************************************
363*9f844368SJames Wright CEED_QFUNCTION_HELPER int PressureOutflow_Jacobian(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out,
364*9f844368SJames Wright                                                    StateVariable state_var) {
365*9f844368SJames Wright   // Inputs
366*9f844368SJames Wright   const CeedScalar(*dq)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0];
367*9f844368SJames Wright   const CeedScalar(*Grad_dq)        = in[1];
368*9f844368SJames Wright   const CeedScalar(*q_data_sur)     = in[2];
369*9f844368SJames Wright   const CeedScalar(*jac_data_sur)   = in[4];
370*9f844368SJames Wright 
371*9f844368SJames Wright   // Outputs
372*9f844368SJames Wright   CeedScalar(*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0];
373*9f844368SJames Wright 
374*9f844368SJames Wright   const OutflowContext           outflow     = (OutflowContext)ctx;
375*9f844368SJames Wright   const NewtonianIdealGasContext gas         = &outflow->gas;
376*9f844368SJames Wright   const bool                     is_implicit = gas->is_implicit;
377*9f844368SJames Wright 
378*9f844368SJames Wright   // Quadrature Point Loop
379*9f844368SJames Wright   CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
380*9f844368SJames Wright     CeedScalar wdetJb, dXdx[2][3], norm[3];
381*9f844368SJames Wright     QdataBoundaryUnpack_3D(Q, i, q_data_sur, &wdetJb, dXdx, norm);
382*9f844368SJames Wright     wdetJb *= is_implicit ? -1. : 1.;
383*9f844368SJames Wright 
384*9f844368SJames Wright     CeedScalar qi[5], kmstress[6], dqi[5];
385*9f844368SJames Wright     StoredValuesUnpack(Q, i, 0, 5, jac_data_sur, qi);
386*9f844368SJames Wright     StoredValuesUnpack(Q, i, 5, 6, jac_data_sur, kmstress);
387*9f844368SJames Wright     for (int j = 0; j < 5; j++) dqi[j] = dq[j][i];
388*9f844368SJames Wright 
389*9f844368SJames Wright     State s       = StateFromQ(gas, qi, state_var);
390*9f844368SJames Wright     State ds      = StateFromQ_fwd(gas, s, dqi, state_var);
391*9f844368SJames Wright     s.Y.pressure  = outflow->pressure;
392*9f844368SJames Wright     ds.Y.pressure = 0.;
393*9f844368SJames Wright 
394*9f844368SJames Wright     State grad_ds[3];
395*9f844368SJames Wright     StatePhysicalGradientFromReference_Boundary(Q, i, gas, s, state_var, Grad_dq, dXdx, grad_ds);
396*9f844368SJames Wright 
397*9f844368SJames Wright     CeedScalar dstrain_rate[6], dkmstress[6], stress[3][3], dstress[3][3], dFe[3];
398*9f844368SJames Wright     KMStrainRate_State(grad_ds, dstrain_rate);
399*9f844368SJames Wright     NewtonianStress(gas, dstrain_rate, dkmstress);
400*9f844368SJames Wright     KMUnpack(dkmstress, dstress);
401*9f844368SJames Wright     KMUnpack(kmstress, stress);
402*9f844368SJames Wright     ViscousEnergyFlux_fwd(gas, s.Y, ds.Y, grad_ds, stress, dstress, dFe);
403*9f844368SJames Wright 
404*9f844368SJames Wright     StateConservative dF_inviscid[3];
405*9f844368SJames Wright     FluxInviscid_fwd(gas, s, ds, dF_inviscid);
406*9f844368SJames Wright 
407*9f844368SJames Wright     CeedScalar dFlux[5];
408*9f844368SJames Wright     FluxTotal_Boundary(dF_inviscid, dstress, dFe, norm, dFlux);
409*9f844368SJames Wright 
410*9f844368SJames Wright     for (int j = 0; j < 5; j++) v[j][i] = -wdetJb * dFlux[j];
411*9f844368SJames Wright   }  // End Quadrature Point Loop
412*9f844368SJames Wright   return 0;
413*9f844368SJames Wright }
414*9f844368SJames Wright 
415*9f844368SJames Wright CEED_QFUNCTION(PressureOutflow_Jacobian_Conserv)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
416*9f844368SJames Wright   return PressureOutflow_Jacobian(ctx, Q, in, out, STATEVAR_CONSERVATIVE);
417*9f844368SJames Wright }
418*9f844368SJames Wright 
419*9f844368SJames Wright CEED_QFUNCTION(PressureOutflow_Jacobian_Prim)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
420*9f844368SJames Wright   return PressureOutflow_Jacobian(ctx, Q, in, out, STATEVAR_PRIMITIVE);
421*9f844368SJames Wright }
422