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