xref: /honee/qfunctions/bc_freestream.h (revision feb491a77be32dae146404f2deafdaea5277b68a)
1dc936754SJeremy L Thompson // Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and other CEED contributors.
29ed3d70dSJames Wright // All Rights Reserved. See the top-level LICENSE and NOTICE files for details.
39ed3d70dSJames Wright //
49ed3d70dSJames Wright // SPDX-License-Identifier: BSD-2-Clause
59ed3d70dSJames Wright //
69ed3d70dSJames Wright // This file is part of CEED:  http://github.com/ceed
79ed3d70dSJames Wright 
89ed3d70dSJames Wright /// @file
99ed3d70dSJames Wright /// QFunctions for the `bc_freestream` and `bc_outflow` boundary conditions
109ed3d70dSJames Wright #include "bc_freestream_type.h"
119ed3d70dSJames Wright #include "newtonian_state.h"
129ed3d70dSJames Wright #include "newtonian_types.h"
139ed3d70dSJames Wright #include "riemann_solver.h"
149ed3d70dSJames Wright 
159ed3d70dSJames Wright // *****************************************************************************
169ed3d70dSJames Wright // Freestream Boundary Condition
179ed3d70dSJames Wright // *****************************************************************************
189ed3d70dSJames Wright CEED_QFUNCTION_HELPER int Freestream(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out, StateVariable state_var,
199ed3d70dSJames Wright                                      RiemannFluxType flux_type) {
204b96a86bSJames Wright   const FreestreamContext context  = (FreestreamContext)ctx;
219ed3d70dSJames Wright   const CeedScalar(*q)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0];
229ed3d70dSJames Wright   const CeedScalar(*q_data_sur)    = in[2];
239ed3d70dSJames Wright   CeedScalar(*v)[CEED_Q_VLA]       = (CeedScalar(*)[CEED_Q_VLA])out[0];
244b96a86bSJames Wright   CeedScalar(*jac_data_sur)        = context->newtonian_ctx.is_implicit ? out[1] : NULL;
259ed3d70dSJames Wright 
269ed3d70dSJames Wright   const NewtonianIdealGasContext newt_ctx    = &context->newtonian_ctx;
279ed3d70dSJames Wright   const bool                     is_implicit = newt_ctx->is_implicit;
289ed3d70dSJames Wright 
299ed3d70dSJames Wright   CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
309ed3d70dSJames Wright     const CeedScalar qi[5] = {q[0][i], q[1][i], q[2][i], q[3][i], q[4][i]};
314b96a86bSJames Wright     const State      s     = StateFromQ(newt_ctx, qi, state_var);
329ed3d70dSJames Wright 
33*feb491a7SJames Wright     CeedScalar wdetJb, normal[3];
34*feb491a7SJames Wright     QdataBoundaryUnpack_3D(Q, i, q_data_sur, &wdetJb, NULL, normal);
359ed3d70dSJames Wright     wdetJb *= is_implicit ? -1. : 1.;
369ed3d70dSJames Wright 
379ed3d70dSJames Wright     StateConservative flux;
389ed3d70dSJames Wright     switch (flux_type) {
399ed3d70dSJames Wright       case RIEMANN_HLL:
40*feb491a7SJames Wright         flux = RiemannFlux_HLL(newt_ctx, s, context->S_infty, normal);
419ed3d70dSJames Wright         break;
429ed3d70dSJames Wright       case RIEMANN_HLLC:
43*feb491a7SJames Wright         flux = RiemannFlux_HLLC(newt_ctx, s, context->S_infty, normal);
449ed3d70dSJames Wright         break;
459ed3d70dSJames Wright     }
469ed3d70dSJames Wright     CeedScalar Flux[5];
479ed3d70dSJames Wright     UnpackState_U(flux, Flux);
489ed3d70dSJames Wright     for (CeedInt j = 0; j < 5; j++) v[j][i] = -wdetJb * Flux[j];
499ed3d70dSJames Wright 
504b96a86bSJames Wright     if (is_implicit) {
514b96a86bSJames Wright       CeedScalar zeros[6] = {0.};
529ed3d70dSJames Wright       StoredValuesPack(Q, i, 0, 5, qi, jac_data_sur);
539ed3d70dSJames Wright       StoredValuesPack(Q, i, 5, 6, zeros, jac_data_sur);  // Every output value must be set
549ed3d70dSJames Wright     }
554b96a86bSJames Wright   }
569ed3d70dSJames Wright   return 0;
579ed3d70dSJames Wright }
589ed3d70dSJames Wright 
599ed3d70dSJames Wright CEED_QFUNCTION(Freestream_Conserv_HLL)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
609ed3d70dSJames Wright   return Freestream(ctx, Q, in, out, STATEVAR_CONSERVATIVE, RIEMANN_HLL);
619ed3d70dSJames Wright }
629ed3d70dSJames Wright 
639ed3d70dSJames Wright CEED_QFUNCTION(Freestream_Prim_HLL)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
649ed3d70dSJames Wright   return Freestream(ctx, Q, in, out, STATEVAR_PRIMITIVE, RIEMANN_HLL);
659ed3d70dSJames Wright }
669ed3d70dSJames Wright 
679b103f75SJames Wright CEED_QFUNCTION(Freestream_Entropy_HLL)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
689b103f75SJames Wright   return Freestream(ctx, Q, in, out, STATEVAR_ENTROPY, RIEMANN_HLL);
699b103f75SJames Wright }
709b103f75SJames Wright 
719ed3d70dSJames Wright CEED_QFUNCTION(Freestream_Conserv_HLLC)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
729ed3d70dSJames Wright   return Freestream(ctx, Q, in, out, STATEVAR_CONSERVATIVE, RIEMANN_HLLC);
739ed3d70dSJames Wright }
749ed3d70dSJames Wright 
759ed3d70dSJames Wright CEED_QFUNCTION(Freestream_Prim_HLLC)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
769ed3d70dSJames Wright   return Freestream(ctx, Q, in, out, STATEVAR_PRIMITIVE, RIEMANN_HLLC);
779ed3d70dSJames Wright }
789ed3d70dSJames Wright 
799b103f75SJames Wright CEED_QFUNCTION(Freestream_Entropy_HLLC)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
809b103f75SJames Wright   return Freestream(ctx, Q, in, out, STATEVAR_ENTROPY, RIEMANN_HLLC);
819b103f75SJames Wright }
829b103f75SJames Wright 
839ed3d70dSJames Wright CEED_QFUNCTION_HELPER int Freestream_Jacobian(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out, StateVariable state_var,
849ed3d70dSJames Wright                                               RiemannFluxType flux_type) {
859ed3d70dSJames Wright   const CeedScalar(*dq)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0];
869ed3d70dSJames Wright   const CeedScalar(*q_data_sur)     = in[2];
879ed3d70dSJames Wright   const CeedScalar(*jac_data_sur)   = in[4];
889ed3d70dSJames Wright 
899ed3d70dSJames Wright   CeedScalar(*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0];
909ed3d70dSJames Wright 
919ed3d70dSJames Wright   const FreestreamContext        context     = (FreestreamContext)ctx;
929ed3d70dSJames Wright   const NewtonianIdealGasContext newt_ctx    = &context->newtonian_ctx;
939ed3d70dSJames Wright   const bool                     is_implicit = newt_ctx->is_implicit;
949ed3d70dSJames Wright   const State                    dS_infty    = {0};
959ed3d70dSJames Wright 
969ed3d70dSJames Wright   CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
97*feb491a7SJames Wright     CeedScalar wdetJb, normal[3];
98*feb491a7SJames Wright     QdataBoundaryUnpack_3D(Q, i, q_data_sur, &wdetJb, NULL, normal);
999ed3d70dSJames Wright     wdetJb *= is_implicit ? -1. : 1.;
1009ed3d70dSJames Wright 
1019ed3d70dSJames Wright     CeedScalar qi[5], dqi[5];
1029ed3d70dSJames Wright     StoredValuesUnpack(Q, i, 0, 5, jac_data_sur, qi);
1039ed3d70dSJames Wright     for (int j = 0; j < 5; j++) dqi[j] = dq[j][i];
1049ed3d70dSJames Wright     State s  = StateFromQ(newt_ctx, qi, state_var);
1059ed3d70dSJames Wright     State ds = StateFromQ_fwd(newt_ctx, s, dqi, state_var);
1069ed3d70dSJames Wright 
1079ed3d70dSJames Wright     StateConservative dflux;
1089ed3d70dSJames Wright     switch (flux_type) {
1099ed3d70dSJames Wright       case RIEMANN_HLL:
110*feb491a7SJames Wright         dflux = RiemannFlux_HLL_fwd(newt_ctx, s, ds, context->S_infty, dS_infty, normal);
1119ed3d70dSJames Wright         break;
1129ed3d70dSJames Wright       case RIEMANN_HLLC:
113*feb491a7SJames Wright         dflux = RiemannFlux_HLLC_fwd(newt_ctx, s, ds, context->S_infty, dS_infty, normal);
1149ed3d70dSJames Wright         break;
1159ed3d70dSJames Wright     }
1169ed3d70dSJames Wright     CeedScalar dFlux[5];
1179ed3d70dSJames Wright     UnpackState_U(dflux, dFlux);
1189ed3d70dSJames Wright     for (CeedInt j = 0; j < 5; j++) v[j][i] = -wdetJb * dFlux[j];
1199ed3d70dSJames Wright   }
1209ed3d70dSJames Wright   return 0;
1219ed3d70dSJames Wright }
1229ed3d70dSJames Wright 
1239ed3d70dSJames Wright CEED_QFUNCTION(Freestream_Jacobian_Conserv_HLL)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
1249ed3d70dSJames Wright   return Freestream_Jacobian(ctx, Q, in, out, STATEVAR_CONSERVATIVE, RIEMANN_HLL);
1259ed3d70dSJames Wright }
1269ed3d70dSJames Wright 
1279ed3d70dSJames Wright CEED_QFUNCTION(Freestream_Jacobian_Prim_HLL)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
1289ed3d70dSJames Wright   return Freestream_Jacobian(ctx, Q, in, out, STATEVAR_PRIMITIVE, RIEMANN_HLL);
1299ed3d70dSJames Wright }
1309ed3d70dSJames Wright 
1319b103f75SJames Wright CEED_QFUNCTION(Freestream_Jacobian_Entropy_HLL)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
1329b103f75SJames Wright   return Freestream_Jacobian(ctx, Q, in, out, STATEVAR_ENTROPY, RIEMANN_HLL);
1339b103f75SJames Wright }
1349b103f75SJames Wright 
1359ed3d70dSJames Wright CEED_QFUNCTION(Freestream_Jacobian_Conserv_HLLC)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
1369ed3d70dSJames Wright   return Freestream_Jacobian(ctx, Q, in, out, STATEVAR_CONSERVATIVE, RIEMANN_HLLC);
1379ed3d70dSJames Wright }
1389ed3d70dSJames Wright 
1399ed3d70dSJames Wright CEED_QFUNCTION(Freestream_Jacobian_Prim_HLLC)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
1409ed3d70dSJames Wright   return Freestream_Jacobian(ctx, Q, in, out, STATEVAR_PRIMITIVE, RIEMANN_HLLC);
1419ed3d70dSJames Wright }
1429ed3d70dSJames Wright 
1439b103f75SJames Wright CEED_QFUNCTION(Freestream_Jacobian_Entropy_HLLC)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
1449b103f75SJames Wright   return Freestream_Jacobian(ctx, Q, in, out, STATEVAR_ENTROPY, RIEMANN_HLLC);
1459b103f75SJames Wright }
1469b103f75SJames Wright 
1479ed3d70dSJames Wright // Note the identity
1489ed3d70dSJames Wright //
1499ed3d70dSJames Wright // softplus(x) - x = log(1 + exp(x)) - x
1509ed3d70dSJames Wright //                 = log(1 + exp(x)) + log(exp(-x))
1519ed3d70dSJames Wright //                 = log((1 + exp(x)) * exp(-x))
1529ed3d70dSJames Wright //                 = log(exp(-x) + 1)
1539ed3d70dSJames Wright //                 = softplus(-x)
1549ed3d70dSJames Wright CEED_QFUNCTION_HELPER CeedScalar Softplus(CeedScalar x, CeedScalar width) {
1559ed3d70dSJames Wright   if (x > 40 * width) return x;
1569ed3d70dSJames Wright   return width * log1p(exp(x / width));
1579ed3d70dSJames Wright }
1589ed3d70dSJames Wright 
1599ed3d70dSJames Wright CEED_QFUNCTION_HELPER CeedScalar Softplus_fwd(CeedScalar x, CeedScalar dx, CeedScalar width) {
1609ed3d70dSJames Wright   if (x > 40 * width) return 1;
1619ed3d70dSJames Wright   const CeedScalar t = exp(x / width);
1629ed3d70dSJames Wright   return t / (1 + t);
1639ed3d70dSJames Wright }
1649ed3d70dSJames Wright 
1659ed3d70dSJames Wright // Viscous Outflow boundary condition, setting a constant exterior pressure and
1669ed3d70dSJames Wright // temperature as input for a Riemann solve. This condition is stable even in
1679ed3d70dSJames Wright // recirculating flow so long as the exterior temperature is sensible.
1689ed3d70dSJames Wright //
1699ed3d70dSJames Wright // The velocity in the exterior state has optional softplus regularization to
1709ed3d70dSJames Wright // keep it outflow. These parameters have been finnicky in practice and provide
1719ed3d70dSJames Wright // little or no benefit in the tests we've run thus far, thus we recommend
1729ed3d70dSJames Wright // skipping this feature and just allowing recirculation.
1739ed3d70dSJames Wright CEED_QFUNCTION_HELPER int RiemannOutflow(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out, StateVariable state_var) {
1744b96a86bSJames Wright   const OutflowContext outflow     = (OutflowContext)ctx;
1759ed3d70dSJames Wright   const CeedScalar(*q)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0];
1769ed3d70dSJames Wright   const CeedScalar(*Grad_q)        = in[1];
1779ed3d70dSJames Wright   const CeedScalar(*q_data_sur)    = in[2];
1789ed3d70dSJames Wright   CeedScalar(*v)[CEED_Q_VLA]       = (CeedScalar(*)[CEED_Q_VLA])out[0];
1794b96a86bSJames Wright   CeedScalar(*jac_data_sur)        = outflow->gas.is_implicit ? out[1] : NULL;
1809ed3d70dSJames Wright 
1819ed3d70dSJames Wright   const NewtonianIdealGasContext gas         = &outflow->gas;
1829ed3d70dSJames Wright   const bool                     is_implicit = gas->is_implicit;
1839ed3d70dSJames Wright 
1849ed3d70dSJames Wright   CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
185*feb491a7SJames Wright     CeedScalar wdetJb, dXdx[2][3], normal[3];
186*feb491a7SJames Wright     QdataBoundaryUnpack_3D(Q, i, q_data_sur, &wdetJb, dXdx, normal);
1879ed3d70dSJames Wright     wdetJb *= is_implicit ? -1. : 1.;
1889ed3d70dSJames Wright     const CeedScalar qi[5] = {q[0][i], q[1][i], q[2][i], q[3][i], q[4][i]};
1894b96a86bSJames Wright     const State      s_int = StateFromQ(gas, qi, state_var);
1909ed3d70dSJames Wright 
1919ed3d70dSJames Wright     StatePrimitive y_ext      = s_int.Y;
1929ed3d70dSJames Wright     y_ext.pressure            = outflow->pressure;
1939ed3d70dSJames Wright     y_ext.temperature         = outflow->temperature;
194*feb491a7SJames Wright     const CeedScalar u_normal = Dot3(y_ext.velocity, normal);
1959ed3d70dSJames Wright     const CeedScalar proj     = (1 - outflow->recirc) * Softplus(-u_normal, outflow->softplus_velocity);
1969ed3d70dSJames Wright     for (CeedInt j = 0; j < 3; j++) {
197*feb491a7SJames Wright       y_ext.velocity[j] += normal[j] * proj;  // (I - n n^T) projects into the plane tangent to the normal
1989ed3d70dSJames Wright     }
1999ed3d70dSJames Wright     State s_ext = StateFromPrimitive(gas, y_ext);
2009ed3d70dSJames Wright 
2019ed3d70dSJames Wright     State grad_s[3];
2029ed3d70dSJames Wright     StatePhysicalGradientFromReference_Boundary(Q, i, gas, s_int, state_var, Grad_q, dXdx, grad_s);
2039ed3d70dSJames Wright 
2049ed3d70dSJames Wright     CeedScalar strain_rate[6], kmstress[6], stress[3][3], Fe[3];
2059ed3d70dSJames Wright     KMStrainRate_State(grad_s, strain_rate);
2069ed3d70dSJames Wright     NewtonianStress(gas, strain_rate, kmstress);
2079ed3d70dSJames Wright     KMUnpack(kmstress, stress);
2089ed3d70dSJames Wright     ViscousEnergyFlux(gas, s_int.Y, grad_s, stress, Fe);
2099ed3d70dSJames Wright 
210*feb491a7SJames Wright     StateConservative F_inviscid_normal = RiemannFlux_HLLC(gas, s_int, s_ext, normal);
2119ed3d70dSJames Wright 
2129ed3d70dSJames Wright     CeedScalar Flux[5];
213*feb491a7SJames Wright     FluxTotal_RiemannBoundary(F_inviscid_normal, stress, Fe, normal, Flux);
2149ed3d70dSJames Wright 
2159ed3d70dSJames Wright     for (CeedInt j = 0; j < 5; j++) v[j][i] = -wdetJb * Flux[j];
2169ed3d70dSJames Wright 
2179ed3d70dSJames Wright     // Save values for Jacobian
2184b96a86bSJames Wright     if (is_implicit) {
2199ed3d70dSJames Wright       StoredValuesPack(Q, i, 0, 5, qi, jac_data_sur);
2209ed3d70dSJames Wright       StoredValuesPack(Q, i, 5, 6, kmstress, jac_data_sur);
2219ed3d70dSJames Wright     }
2224b96a86bSJames Wright   }
2239ed3d70dSJames Wright   return 0;
2249ed3d70dSJames Wright }
2259ed3d70dSJames Wright 
2269ed3d70dSJames Wright CEED_QFUNCTION(RiemannOutflow_Conserv)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
2279ed3d70dSJames Wright   return RiemannOutflow(ctx, Q, in, out, STATEVAR_CONSERVATIVE);
2289ed3d70dSJames Wright }
2299ed3d70dSJames Wright 
2309ed3d70dSJames Wright CEED_QFUNCTION(RiemannOutflow_Prim)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
2319ed3d70dSJames Wright   return RiemannOutflow(ctx, Q, in, out, STATEVAR_PRIMITIVE);
2329ed3d70dSJames Wright }
2339ed3d70dSJames Wright 
2349b103f75SJames Wright CEED_QFUNCTION(RiemannOutflow_Entropy)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
2359b103f75SJames Wright   return RiemannOutflow(ctx, Q, in, out, STATEVAR_ENTROPY);
2369b103f75SJames Wright }
2379b103f75SJames Wright 
2389ed3d70dSJames Wright // *****************************************************************************
2399ed3d70dSJames Wright // Jacobian for Riemann pressure/temperature outflow boundary condition
2409ed3d70dSJames Wright // *****************************************************************************
2419ed3d70dSJames Wright CEED_QFUNCTION_HELPER int RiemannOutflow_Jacobian(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out,
2429ed3d70dSJames Wright                                                   StateVariable state_var) {
2439ed3d70dSJames Wright   const CeedScalar(*dq)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0];
2449ed3d70dSJames Wright   const CeedScalar(*Grad_dq)        = in[1];
2459ed3d70dSJames Wright   const CeedScalar(*q_data_sur)     = in[2];
2469ed3d70dSJames Wright   const CeedScalar(*jac_data_sur)   = in[4];
2479ed3d70dSJames Wright   CeedScalar(*v)[CEED_Q_VLA]        = (CeedScalar(*)[CEED_Q_VLA])out[0];
2489ed3d70dSJames Wright 
2499ed3d70dSJames Wright   const OutflowContext           outflow     = (OutflowContext)ctx;
2509ed3d70dSJames Wright   const NewtonianIdealGasContext gas         = &outflow->gas;
2519ed3d70dSJames Wright   const bool                     is_implicit = gas->is_implicit;
2529ed3d70dSJames Wright 
2539ed3d70dSJames Wright   CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
254*feb491a7SJames Wright     CeedScalar wdetJb, dXdx[2][3], normal[3];
255*feb491a7SJames Wright     QdataBoundaryUnpack_3D(Q, i, q_data_sur, &wdetJb, dXdx, normal);
2569ed3d70dSJames Wright     wdetJb *= is_implicit ? -1. : 1.;
2579ed3d70dSJames Wright 
2589ed3d70dSJames Wright     CeedScalar qi[5], kmstress[6], dqi[5];
2599ed3d70dSJames Wright     StoredValuesUnpack(Q, i, 0, 5, jac_data_sur, qi);
2609ed3d70dSJames Wright     StoredValuesUnpack(Q, i, 5, 6, jac_data_sur, kmstress);
2619ed3d70dSJames Wright     for (int j = 0; j < 5; j++) dqi[j] = dq[j][i];
2629ed3d70dSJames Wright 
2639ed3d70dSJames Wright     State          s_int  = StateFromQ(gas, qi, state_var);
2649ed3d70dSJames Wright     const State    ds_int = StateFromQ_fwd(gas, s_int, dqi, state_var);
2659ed3d70dSJames Wright     StatePrimitive y_ext = s_int.Y, dy_ext = ds_int.Y;
2669ed3d70dSJames Wright     y_ext.pressure             = outflow->pressure;
2679ed3d70dSJames Wright     y_ext.temperature          = outflow->temperature;
2689ed3d70dSJames Wright     dy_ext.pressure            = 0;
2699ed3d70dSJames Wright     dy_ext.temperature         = 0;
270*feb491a7SJames Wright     const CeedScalar u_normal  = Dot3(s_int.Y.velocity, normal);
271*feb491a7SJames Wright     const CeedScalar du_normal = Dot3(ds_int.Y.velocity, normal);
2729ed3d70dSJames Wright     const CeedScalar proj      = (1 - outflow->recirc) * Softplus(-u_normal, outflow->softplus_velocity);
2739ed3d70dSJames Wright     const CeedScalar dproj     = (1 - outflow->recirc) * Softplus_fwd(-u_normal, -du_normal, outflow->softplus_velocity);
2749ed3d70dSJames Wright     for (CeedInt j = 0; j < 3; j++) {
275*feb491a7SJames Wright       y_ext.velocity[j] += normal[j] * proj;
276*feb491a7SJames Wright       dy_ext.velocity[j] += normal[j] * dproj;
2779ed3d70dSJames Wright     }
2789ed3d70dSJames Wright 
2799ed3d70dSJames Wright     State s_ext  = StateFromPrimitive(gas, y_ext);
2809ed3d70dSJames Wright     State ds_ext = StateFromPrimitive_fwd(gas, s_ext, dy_ext);
2819ed3d70dSJames Wright 
2829ed3d70dSJames Wright     State grad_ds[3];
2839ed3d70dSJames Wright     StatePhysicalGradientFromReference_Boundary(Q, i, gas, s_int, state_var, Grad_dq, dXdx, grad_ds);
2849ed3d70dSJames Wright 
2859ed3d70dSJames Wright     CeedScalar dstrain_rate[6], dkmstress[6], stress[3][3], dstress[3][3], dFe[3];
2869ed3d70dSJames Wright     KMStrainRate_State(grad_ds, dstrain_rate);
2879ed3d70dSJames Wright     NewtonianStress(gas, dstrain_rate, dkmstress);
2889ed3d70dSJames Wright     KMUnpack(dkmstress, dstress);
2899ed3d70dSJames Wright     KMUnpack(kmstress, stress);
2909ed3d70dSJames Wright     ViscousEnergyFlux_fwd(gas, s_int.Y, ds_int.Y, grad_ds, stress, dstress, dFe);
2919ed3d70dSJames Wright 
292*feb491a7SJames Wright     StateConservative dF_inviscid_normal = RiemannFlux_HLLC_fwd(gas, s_int, ds_int, s_ext, ds_ext, normal);
2939ed3d70dSJames Wright 
2949ed3d70dSJames Wright     CeedScalar dFlux[5];
295*feb491a7SJames Wright     FluxTotal_RiemannBoundary(dF_inviscid_normal, dstress, dFe, normal, dFlux);
2969ed3d70dSJames Wright 
2979ed3d70dSJames Wright     for (int j = 0; j < 5; j++) v[j][i] = -wdetJb * dFlux[j];
298b193fadcSJames Wright   }
2999ed3d70dSJames Wright   return 0;
3009ed3d70dSJames Wright }
3019ed3d70dSJames Wright 
3029ed3d70dSJames Wright CEED_QFUNCTION(RiemannOutflow_Jacobian_Conserv)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
3039ed3d70dSJames Wright   return RiemannOutflow_Jacobian(ctx, Q, in, out, STATEVAR_CONSERVATIVE);
3049ed3d70dSJames Wright }
3059ed3d70dSJames Wright 
3069ed3d70dSJames Wright CEED_QFUNCTION(RiemannOutflow_Jacobian_Prim)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
3079ed3d70dSJames Wright   return RiemannOutflow_Jacobian(ctx, Q, in, out, STATEVAR_PRIMITIVE);
3089ed3d70dSJames Wright }
3099ed3d70dSJames Wright 
3109b103f75SJames Wright CEED_QFUNCTION(RiemannOutflow_Jacobian_Entropy)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
3119b103f75SJames Wright   return RiemannOutflow_Jacobian(ctx, Q, in, out, STATEVAR_ENTROPY);
3129b103f75SJames Wright }
3139b103f75SJames Wright 
3149ed3d70dSJames Wright // *****************************************************************************
3159ed3d70dSJames Wright // Outflow boundary condition, weakly setting a constant pressure. This is the
3169ed3d70dSJames Wright // classic outflow condition used by PHASTA-C and retained largely for
3179ed3d70dSJames Wright // comparison. In our experiments, it is never better than RiemannOutflow, and
3189ed3d70dSJames Wright // will crash if outflow ever becomes an inflow, as occurs with strong
3199ed3d70dSJames Wright // acoustics, vortices, etc.
3209ed3d70dSJames Wright // *****************************************************************************
3219ed3d70dSJames Wright CEED_QFUNCTION_HELPER int PressureOutflow(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out, StateVariable state_var) {
3224b96a86bSJames Wright   const OutflowContext outflow     = (OutflowContext)ctx;
3239ed3d70dSJames Wright   const CeedScalar(*q)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0];
3249ed3d70dSJames Wright   const CeedScalar(*Grad_q)        = in[1];
3259ed3d70dSJames Wright   const CeedScalar(*q_data_sur)    = in[2];
3269ed3d70dSJames Wright   CeedScalar(*v)[CEED_Q_VLA]       = (CeedScalar(*)[CEED_Q_VLA])out[0];
3274b96a86bSJames Wright   CeedScalar(*jac_data_sur)        = outflow->gas.is_implicit ? out[1] : NULL;
3289ed3d70dSJames Wright 
3299ed3d70dSJames Wright   const NewtonianIdealGasContext gas         = &outflow->gas;
3309ed3d70dSJames Wright   const bool                     is_implicit = gas->is_implicit;
3319ed3d70dSJames Wright 
3329ed3d70dSJames Wright   CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
3339ed3d70dSJames Wright     const CeedScalar qi[5] = {q[0][i], q[1][i], q[2][i], q[3][i], q[4][i]};
3349ed3d70dSJames Wright     State            s     = StateFromQ(gas, qi, state_var);
3359ed3d70dSJames Wright     s.Y.pressure           = outflow->pressure;
3369ed3d70dSJames Wright 
337*feb491a7SJames Wright     CeedScalar wdetJb, dXdx[2][3], normal[3];
338*feb491a7SJames Wright     QdataBoundaryUnpack_3D(Q, i, q_data_sur, &wdetJb, dXdx, normal);
3399ed3d70dSJames Wright     wdetJb *= is_implicit ? -1. : 1.;
3409ed3d70dSJames Wright 
3419ed3d70dSJames Wright     State grad_s[3];
3429ed3d70dSJames Wright     StatePhysicalGradientFromReference_Boundary(Q, i, gas, s, state_var, Grad_q, dXdx, grad_s);
3439ed3d70dSJames Wright 
3449ed3d70dSJames Wright     CeedScalar strain_rate[6], kmstress[6], stress[3][3], Fe[3];
3459ed3d70dSJames Wright     KMStrainRate_State(grad_s, strain_rate);
3469ed3d70dSJames Wright     NewtonianStress(gas, strain_rate, kmstress);
3479ed3d70dSJames Wright     KMUnpack(kmstress, stress);
3489ed3d70dSJames Wright     ViscousEnergyFlux(gas, s.Y, grad_s, stress, Fe);
3499ed3d70dSJames Wright 
3509ed3d70dSJames Wright     StateConservative F_inviscid[3];
3519ed3d70dSJames Wright     FluxInviscid(gas, s, F_inviscid);
3529ed3d70dSJames Wright 
3539ed3d70dSJames Wright     CeedScalar Flux[5];
354*feb491a7SJames Wright     FluxTotal_Boundary(F_inviscid, stress, Fe, normal, Flux);
3559ed3d70dSJames Wright 
3569ed3d70dSJames Wright     for (CeedInt j = 0; j < 5; j++) v[j][i] = -wdetJb * Flux[j];
3579ed3d70dSJames Wright 
3589ed3d70dSJames Wright     // Save values for Jacobian
3594b96a86bSJames Wright     if (is_implicit) {
3609ed3d70dSJames Wright       StoredValuesPack(Q, i, 0, 5, qi, jac_data_sur);
3619ed3d70dSJames Wright       StoredValuesPack(Q, i, 5, 6, kmstress, jac_data_sur);
3624b96a86bSJames Wright     }
3634b96a86bSJames Wright   }
3649ed3d70dSJames Wright   return 0;
3659ed3d70dSJames Wright }
3669ed3d70dSJames Wright 
3679ed3d70dSJames Wright CEED_QFUNCTION(PressureOutflow_Conserv)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
3689ed3d70dSJames Wright   return PressureOutflow(ctx, Q, in, out, STATEVAR_CONSERVATIVE);
3699ed3d70dSJames Wright }
3709ed3d70dSJames Wright 
3719ed3d70dSJames Wright CEED_QFUNCTION(PressureOutflow_Prim)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
3729ed3d70dSJames Wright   return PressureOutflow(ctx, Q, in, out, STATEVAR_PRIMITIVE);
3739ed3d70dSJames Wright }
3749ed3d70dSJames Wright 
3759b103f75SJames Wright CEED_QFUNCTION(PressureOutflow_Entropy)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
3769b103f75SJames Wright   return PressureOutflow(ctx, Q, in, out, STATEVAR_ENTROPY);
3779b103f75SJames Wright }
3789b103f75SJames Wright 
3799ed3d70dSJames Wright // *****************************************************************************
3809ed3d70dSJames Wright // Jacobian for weak-pressure outflow boundary condition
3819ed3d70dSJames Wright // *****************************************************************************
3829ed3d70dSJames Wright CEED_QFUNCTION_HELPER int PressureOutflow_Jacobian(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out,
3839ed3d70dSJames Wright                                                    StateVariable state_var) {
3849ed3d70dSJames Wright   const CeedScalar(*dq)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0];
3859ed3d70dSJames Wright   const CeedScalar(*Grad_dq)        = in[1];
3869ed3d70dSJames Wright   const CeedScalar(*q_data_sur)     = in[2];
3879ed3d70dSJames Wright   const CeedScalar(*jac_data_sur)   = in[4];
3889ed3d70dSJames Wright   CeedScalar(*v)[CEED_Q_VLA]        = (CeedScalar(*)[CEED_Q_VLA])out[0];
3899ed3d70dSJames Wright 
3909ed3d70dSJames Wright   const OutflowContext           outflow     = (OutflowContext)ctx;
3919ed3d70dSJames Wright   const NewtonianIdealGasContext gas         = &outflow->gas;
3929ed3d70dSJames Wright   const bool                     is_implicit = gas->is_implicit;
3939ed3d70dSJames Wright 
3949ed3d70dSJames Wright   CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
395*feb491a7SJames Wright     CeedScalar wdetJb, dXdx[2][3], normal[3];
396*feb491a7SJames Wright     QdataBoundaryUnpack_3D(Q, i, q_data_sur, &wdetJb, dXdx, normal);
3979ed3d70dSJames Wright     wdetJb *= is_implicit ? -1. : 1.;
3989ed3d70dSJames Wright 
3999ed3d70dSJames Wright     CeedScalar qi[5], kmstress[6], dqi[5];
4009ed3d70dSJames Wright     StoredValuesUnpack(Q, i, 0, 5, jac_data_sur, qi);
4019ed3d70dSJames Wright     StoredValuesUnpack(Q, i, 5, 6, jac_data_sur, kmstress);
4029ed3d70dSJames Wright     for (int j = 0; j < 5; j++) dqi[j] = dq[j][i];
4039ed3d70dSJames Wright 
4049ed3d70dSJames Wright     State s       = StateFromQ(gas, qi, state_var);
4059ed3d70dSJames Wright     State ds      = StateFromQ_fwd(gas, s, dqi, state_var);
4069ed3d70dSJames Wright     s.Y.pressure  = outflow->pressure;
4079ed3d70dSJames Wright     ds.Y.pressure = 0.;
4089ed3d70dSJames Wright 
4099ed3d70dSJames Wright     State grad_ds[3];
4109ed3d70dSJames Wright     StatePhysicalGradientFromReference_Boundary(Q, i, gas, s, state_var, Grad_dq, dXdx, grad_ds);
4119ed3d70dSJames Wright 
4129ed3d70dSJames Wright     CeedScalar dstrain_rate[6], dkmstress[6], stress[3][3], dstress[3][3], dFe[3];
4139ed3d70dSJames Wright     KMStrainRate_State(grad_ds, dstrain_rate);
4149ed3d70dSJames Wright     NewtonianStress(gas, dstrain_rate, dkmstress);
4159ed3d70dSJames Wright     KMUnpack(dkmstress, dstress);
4169ed3d70dSJames Wright     KMUnpack(kmstress, stress);
4179ed3d70dSJames Wright     ViscousEnergyFlux_fwd(gas, s.Y, ds.Y, grad_ds, stress, dstress, dFe);
4189ed3d70dSJames Wright 
4199ed3d70dSJames Wright     StateConservative dF_inviscid[3];
4209ed3d70dSJames Wright     FluxInviscid_fwd(gas, s, ds, dF_inviscid);
4219ed3d70dSJames Wright 
4229ed3d70dSJames Wright     CeedScalar dFlux[5];
423*feb491a7SJames Wright     FluxTotal_Boundary(dF_inviscid, dstress, dFe, normal, dFlux);
4249ed3d70dSJames Wright 
4259ed3d70dSJames Wright     for (int j = 0; j < 5; j++) v[j][i] = -wdetJb * dFlux[j];
426b193fadcSJames Wright   }
4279ed3d70dSJames Wright   return 0;
4289ed3d70dSJames Wright }
4299ed3d70dSJames Wright 
4309ed3d70dSJames Wright CEED_QFUNCTION(PressureOutflow_Jacobian_Conserv)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
4319ed3d70dSJames Wright   return PressureOutflow_Jacobian(ctx, Q, in, out, STATEVAR_CONSERVATIVE);
4329ed3d70dSJames Wright }
4339ed3d70dSJames Wright 
4349ed3d70dSJames Wright CEED_QFUNCTION(PressureOutflow_Jacobian_Prim)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
4359ed3d70dSJames Wright   return PressureOutflow_Jacobian(ctx, Q, in, out, STATEVAR_PRIMITIVE);
4369ed3d70dSJames Wright }
4379b103f75SJames Wright 
4389b103f75SJames Wright CEED_QFUNCTION(PressureOutflow_Jacobian_Entropy)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
4399b103f75SJames Wright   return PressureOutflow_Jacobian(ctx, Q, in, out, STATEVAR_ENTROPY);
4409b103f75SJames Wright }
441