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