xref: /libCEED/examples/fluids/qfunctions/newtonian.h (revision 2b730f8b5a9c809740a0b3b302db43a719c636b1)
13d8e8822SJeremy L Thompson // Copyright (c) 2017-2022, Lawrence Livermore National Security, LLC and other CEED contributors.
23d8e8822SJeremy L Thompson // All Rights Reserved. See the top-level LICENSE and NOTICE files for details.
388b783a1SJames Wright //
43d8e8822SJeremy L Thompson // SPDX-License-Identifier: BSD-2-Clause
588b783a1SJames Wright //
63d8e8822SJeremy L Thompson // This file is part of CEED:  http://github.com/ceed
788b783a1SJames Wright 
888b783a1SJames Wright /// @file
988b783a1SJames Wright /// Operator for Navier-Stokes example using PETSc
1088b783a1SJames Wright 
1188b783a1SJames Wright #ifndef newtonian_h
1288b783a1SJames Wright #define newtonian_h
1388b783a1SJames Wright 
1488b783a1SJames Wright #include <ceed.h>
15c9c2c079SJeremy L Thompson #include <math.h>
16738af36cSAdelekeBankole #include <stdlib.h>
17*2b730f8bSJeremy L Thompson 
18c6e8c570SJames Wright #include "newtonian_state.h"
19c9c2c079SJeremy L Thompson #include "newtonian_types.h"
202b89d87eSLeila Ghaffari #include "stabilization.h"
21c9c2c079SJeremy L Thompson #include "utils.h"
2288626eedSJames Wright 
2388626eedSJames Wright // *****************************************************************************
2488b783a1SJames Wright // This QFunction sets a "still" initial condition for generic Newtonian IG problems
2588b783a1SJames Wright // *****************************************************************************
26*2b730f8bSJeremy L Thompson CEED_QFUNCTION(ICsNewtonianIG)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
2788b783a1SJames Wright   // Inputs
2888b783a1SJames Wright   const CeedScalar(*X)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0];
2988b783a1SJames Wright 
3088b783a1SJames Wright   // Outputs
3188b783a1SJames Wright   CeedScalar(*q0)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0];
3288b783a1SJames Wright 
3388626eedSJames Wright   // Context
3488626eedSJames Wright   const SetupContext context = (SetupContext)ctx;
3588626eedSJames Wright   const CeedScalar   theta0  = context->theta0;
3688626eedSJames Wright   const CeedScalar   P0      = context->P0;
3788626eedSJames Wright   const CeedScalar   cv      = context->cv;
3888626eedSJames Wright   const CeedScalar   cp      = context->cp;
3988626eedSJames Wright   const CeedScalar  *g       = context->g;
4088626eedSJames Wright   const CeedScalar   Rd      = cp - cv;
4188626eedSJames Wright 
4288b783a1SJames Wright   // Quadrature Point Loop
43*2b730f8bSJeremy L Thompson   CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
4488b783a1SJames Wright     CeedScalar q[5] = {0.};
4588b783a1SJames Wright 
4688b783a1SJames Wright     // Setup
4788b783a1SJames Wright     // -- Coordinates
4888626eedSJames Wright     const CeedScalar x[3]        = {X[0][i], X[1][i], X[2][i]};
492b89d87eSLeila Ghaffari     const CeedScalar e_potential = -Dot3(g, x);
5088b783a1SJames Wright 
5188b783a1SJames Wright     // -- Density
5288626eedSJames Wright     const CeedScalar rho = P0 / (Rd * theta0);
5388b783a1SJames Wright 
5488b783a1SJames Wright     // Initial Conditions
5588b783a1SJames Wright     q[0] = rho;
5688b783a1SJames Wright     q[1] = 0.0;
5788b783a1SJames Wright     q[2] = 0.0;
5888b783a1SJames Wright     q[3] = 0.0;
5988626eedSJames Wright     q[4] = rho * (cv * theta0 + e_potential);
6088b783a1SJames Wright 
61*2b730f8bSJeremy L Thompson     for (CeedInt j = 0; j < 5; j++) q0[j][i] = q[j];
622b89d87eSLeila Ghaffari 
6388b783a1SJames Wright   }  // End of Quadrature Point Loop
6488b783a1SJames Wright   return 0;
6588b783a1SJames Wright }
6688b783a1SJames Wright 
6788b783a1SJames Wright // *****************************************************************************
68dc805cc4SLeila Ghaffari // This QFunction sets a "still" initial condition for generic Newtonian IG
69dc805cc4SLeila Ghaffari //   problems in primitive variables
70dc805cc4SLeila Ghaffari // *****************************************************************************
71*2b730f8bSJeremy L Thompson CEED_QFUNCTION(ICsNewtonianIG_Prim)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
72dc805cc4SLeila Ghaffari   // Outputs
73dc805cc4SLeila Ghaffari   CeedScalar(*q0)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0];
74dc805cc4SLeila Ghaffari 
75dc805cc4SLeila Ghaffari   // Context
76dc805cc4SLeila Ghaffari   const SetupContext context = (SetupContext)ctx;
77dc805cc4SLeila Ghaffari   const CeedScalar   theta0  = context->theta0;
78dc805cc4SLeila Ghaffari   const CeedScalar   P0      = context->P0;
79dc805cc4SLeila Ghaffari 
80dc805cc4SLeila Ghaffari   // Quadrature Point Loop
81*2b730f8bSJeremy L Thompson   CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
82dc805cc4SLeila Ghaffari     CeedScalar q[5] = {0.};
83dc805cc4SLeila Ghaffari 
84dc805cc4SLeila Ghaffari     // Initial Conditions
85dc805cc4SLeila Ghaffari     q[0] = P0;
86dc805cc4SLeila Ghaffari     q[1] = 0.0;
87dc805cc4SLeila Ghaffari     q[2] = 0.0;
88dc805cc4SLeila Ghaffari     q[3] = 0.0;
89dc805cc4SLeila Ghaffari     q[4] = theta0;
90dc805cc4SLeila Ghaffari 
91*2b730f8bSJeremy L Thompson     for (CeedInt j = 0; j < 5; j++) q0[j][i] = q[j];
92dc805cc4SLeila Ghaffari 
93dc805cc4SLeila Ghaffari   }  // End of Quadrature Point Loop
94dc805cc4SLeila Ghaffari   return 0;
95dc805cc4SLeila Ghaffari }
96dc805cc4SLeila Ghaffari 
97dc805cc4SLeila Ghaffari // *****************************************************************************
9888b783a1SJames Wright // This QFunction implements the following formulation of Navier-Stokes with
9988b783a1SJames Wright //   explicit time stepping method
10088b783a1SJames Wright //
10188b783a1SJames Wright // This is 3D compressible Navier-Stokes in conservation form with state
10288b783a1SJames Wright //   variables of density, momentum density, and total energy density.
10388b783a1SJames Wright //
10488b783a1SJames Wright // State Variables: q = ( rho, U1, U2, U3, E )
10588b783a1SJames Wright //   rho - Mass Density
10688b783a1SJames Wright //   Ui  - Momentum Density,      Ui = rho ui
10788b783a1SJames Wright //   E   - Total Energy Density,  E  = rho (cv T + (u u)/2 + g z)
10888b783a1SJames Wright //
10988b783a1SJames Wright // Navier-Stokes Equations:
11088b783a1SJames Wright //   drho/dt + div( U )                               = 0
11188b783a1SJames Wright //   dU/dt   + div( rho (u x u) + P I3 ) + rho g khat = div( Fu )
11288b783a1SJames Wright //   dE/dt   + div( (E + P) u )                       = div( Fe )
11388b783a1SJames Wright //
11488b783a1SJames Wright // Viscous Stress:
11588b783a1SJames Wright //   Fu = mu (grad( u ) + grad( u )^T + lambda div ( u ) I3)
11688b783a1SJames Wright //
11788b783a1SJames Wright // Thermal Stress:
11888b783a1SJames Wright //   Fe = u Fu + k grad( T )
11988626eedSJames Wright // Equation of State
12088b783a1SJames Wright //   P = (gamma - 1) (E - rho (u u) / 2 - rho g z)
12188b783a1SJames Wright //
12288b783a1SJames Wright // Stabilization:
12388b783a1SJames Wright //   Tau = diag(TauC, TauM, TauM, TauM, TauE)
12488b783a1SJames Wright //     f1 = rho  sqrt(ui uj gij)
12588b783a1SJames Wright //     gij = dXi/dX * dXi/dX
12688b783a1SJames Wright //     TauC = Cc f1 / (8 gii)
12788b783a1SJames Wright //     TauM = min( 1 , 1 / f1 )
12888b783a1SJames Wright //     TauE = TauM / (Ce cv)
12988b783a1SJames Wright //
13088b783a1SJames Wright //  SU   = Galerkin + grad(v) . ( Ai^T * Tau * (Aj q,j) )
13188b783a1SJames Wright //
13288b783a1SJames Wright // Constants:
13388b783a1SJames Wright //   lambda = - 2 / 3,  From Stokes hypothesis
13488b783a1SJames Wright //   mu              ,  Dynamic viscosity
13588b783a1SJames Wright //   k               ,  Thermal conductivity
13688b783a1SJames Wright //   cv              ,  Specific heat, constant volume
13788b783a1SJames Wright //   cp              ,  Specific heat, constant pressure
13888b783a1SJames Wright //   g               ,  Gravity
13988b783a1SJames Wright //   gamma  = cp / cv,  Specific heat ratio
14088b783a1SJames Wright //
14188b783a1SJames Wright // We require the product of the inverse of the Jacobian (dXdx_j,k) and
14288b783a1SJames Wright // its transpose (dXdx_k,j) to properly compute integrals of the form:
14388b783a1SJames Wright // int( gradv gradu )
14488b783a1SJames Wright //
14588b783a1SJames Wright // *****************************************************************************
146*2b730f8bSJeremy L Thompson CEED_QFUNCTION(RHSFunction_Newtonian)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
14788b783a1SJames Wright   // *INDENT-OFF*
14888b783a1SJames Wright   // Inputs
149*2b730f8bSJeremy L Thompson   const CeedScalar(*q)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0], (*Grad_q)[5][CEED_Q_VLA] = (const CeedScalar(*)[5][CEED_Q_VLA])in[1],
150*2b730f8bSJeremy L Thompson         (*q_data)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[2], (*x)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[3];
15188b783a1SJames Wright   // Outputs
152*2b730f8bSJeremy L Thompson   CeedScalar(*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0], (*Grad_v)[5][CEED_Q_VLA] = (CeedScalar(*)[5][CEED_Q_VLA])out[1];
15388b783a1SJames Wright   // *INDENT-ON*
15488b783a1SJames Wright 
15588b783a1SJames Wright   // Context
15688b783a1SJames Wright   NewtonianIdealGasContext context = (NewtonianIdealGasContext)ctx;
15788626eedSJames Wright   const CeedScalar        *g       = context->g;
15888626eedSJames Wright   const CeedScalar         dt      = context->dt;
15988b783a1SJames Wright 
16088b783a1SJames Wright   CeedPragmaSIMD
16188b783a1SJames Wright       // Quadrature Point Loop
16288b783a1SJames Wright       for (CeedInt i = 0; i < Q; i++) {
1635c677226SJed Brown     CeedScalar U[5];
1645c677226SJed Brown     for (int j = 0; j < 5; j++) U[j] = q[j][i];
1655c677226SJed Brown     const CeedScalar x_i[3] = {x[0][i], x[1][i], x[2][i]};
1665c677226SJed Brown     State            s      = StateFromU(context, U, x_i);
1675c677226SJed Brown 
16888b783a1SJames Wright     // -- Interp-to-Interp q_data
16988b783a1SJames Wright     const CeedScalar wdetJ = q_data[0][i];
17088b783a1SJames Wright     // -- Interp-to-Grad q_data
17188b783a1SJames Wright     // ---- Inverse of change of coordinate matrix: X_i,j
17288b783a1SJames Wright     // *INDENT-OFF*
173*2b730f8bSJeremy L Thompson     const CeedScalar dXdx[3][3] = {
174*2b730f8bSJeremy L Thompson         {q_data[1][i], q_data[2][i], q_data[3][i]},
17523d6ba15SJames Wright         {q_data[4][i], q_data[5][i], q_data[6][i]},
17623d6ba15SJames Wright         {q_data[7][i], q_data[8][i], q_data[9][i]}
17788b783a1SJames Wright     };
17888b783a1SJames Wright     // *INDENT-ON*
1795c677226SJed Brown     State grad_s[3];
1803c4b7af6SJed Brown     for (CeedInt j = 0; j < 3; j++) {
1816f00d0e6SJed Brown       CeedScalar dx_i[3] = {0}, dU[5];
182*2b730f8bSJeremy L Thompson       for (CeedInt k = 0; k < 5; k++) dU[k] = Grad_q[0][k][i] * dXdx[0][j] + Grad_q[1][k][i] * dXdx[1][j] + Grad_q[2][k][i] * dXdx[2][j];
1835c677226SJed Brown       dx_i[j]   = 1.;
1846f00d0e6SJed Brown       grad_s[j] = StateFromU_fwd(context, s, dU, x_i, dx_i);
1855c677226SJed Brown     }
1865c677226SJed Brown 
1875c677226SJed Brown     CeedScalar strain_rate[6], kmstress[6], stress[3][3], Fe[3];
1885c677226SJed Brown     KMStrainRate(grad_s, strain_rate);
1895c677226SJed Brown     NewtonianStress(context, strain_rate, kmstress);
1905c677226SJed Brown     KMUnpack(kmstress, stress);
1915c677226SJed Brown     ViscousEnergyFlux(context, s.Y, grad_s, stress, Fe);
1925c677226SJed Brown 
1935c677226SJed Brown     StateConservative F_inviscid[3];
1945c677226SJed Brown     FluxInviscid(context, s, F_inviscid);
1955c677226SJed Brown 
1965c677226SJed Brown     // Total flux
1975c677226SJed Brown     CeedScalar Flux[5][3];
1982b89d87eSLeila Ghaffari     FluxTotal(F_inviscid, stress, Fe, Flux);
1995c677226SJed Brown 
200*2b730f8bSJeremy L Thompson     for (CeedInt j = 0; j < 3; j++) {
201*2b730f8bSJeremy L Thompson       for (CeedInt k = 0; k < 5; k++) Grad_v[j][k][i] = wdetJ * (dXdx[j][0] * Flux[k][0] + dXdx[j][1] * Flux[k][1] + dXdx[j][2] * Flux[k][2]);
202*2b730f8bSJeremy L Thompson     }
2035c677226SJed Brown 
2045c677226SJed Brown     const CeedScalar body_force[5] = {0, s.U.density * g[0], s.U.density * g[1], s.U.density * g[2], 0};
205*2b730f8bSJeremy L Thompson     for (int j = 0; j < 5; j++) v[j][i] = wdetJ * body_force[j];
20688b783a1SJames Wright 
2072b89d87eSLeila Ghaffari     // -- Stabilization method: none (Galerkin), SU, or SUPG
2082b89d87eSLeila Ghaffari     CeedScalar Tau_d[3], stab[5][3], U_dot[5] = {0};
2092b89d87eSLeila Ghaffari     Tau_diagPrim(context, s, dXdx, dt, Tau_d);
2102b89d87eSLeila Ghaffari     Stabilization(context, s, Tau_d, grad_s, U_dot, body_force, x_i, stab);
21188b783a1SJames Wright 
212*2b730f8bSJeremy L Thompson     for (CeedInt j = 0; j < 5; j++) {
213*2b730f8bSJeremy L Thompson       for (CeedInt k = 0; k < 3; k++) Grad_v[k][j][i] -= wdetJ * (stab[j][0] * dXdx[k][0] + stab[j][1] * dXdx[k][1] + stab[j][2] * dXdx[k][2]);
214*2b730f8bSJeremy L Thompson     }
21588b783a1SJames Wright   }  // End Quadrature Point Loop
21688b783a1SJames Wright 
21788b783a1SJames Wright   // Return
21888b783a1SJames Wright   return 0;
21988b783a1SJames Wright }
22088b783a1SJames Wright 
22188b783a1SJames Wright // *****************************************************************************
22288b783a1SJames Wright // This QFunction implements the Navier-Stokes equations (mentioned above) with
22388b783a1SJames Wright //   implicit time stepping method
22488b783a1SJames Wright //
22588b783a1SJames Wright //  SU   = Galerkin + grad(v) . ( Ai^T * Tau * (Aj q,j) )
22688b783a1SJames Wright //  SUPG = Galerkin + grad(v) . ( Ai^T * Tau * (q_dot + Aj q,j - body force) )
22788b783a1SJames Wright //                                       (diffussive terms will be added later)
22888b783a1SJames Wright //
22988b783a1SJames Wright // *****************************************************************************
230*2b730f8bSJeremy L Thompson CEED_QFUNCTION_HELPER int IFunction_Newtonian(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out, StateFromQi_t StateFromQi,
231*2b730f8bSJeremy L Thompson                                               StateFromQi_fwd_t StateFromQi_fwd) {
23288b783a1SJames Wright   // *INDENT-OFF*
23388b783a1SJames Wright   // Inputs
234*2b730f8bSJeremy L Thompson   const CeedScalar(*q)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0], (*Grad_q)[5][CEED_Q_VLA] = (const CeedScalar(*)[5][CEED_Q_VLA])in[1],
235*2b730f8bSJeremy L Thompson         (*q_dot)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[2], (*q_data)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[3],
23688b783a1SJames Wright         (*x)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[4];
23788b783a1SJames Wright   // Outputs
238*2b730f8bSJeremy L Thompson   CeedScalar(*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0], (*Grad_v)[5][CEED_Q_VLA] = (CeedScalar(*)[5][CEED_Q_VLA])out[1],
239a3ae0734SJed Brown   (*jac_data)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[2];
24088b783a1SJames Wright   // *INDENT-ON*
24188b783a1SJames Wright   // Context
24288b783a1SJames Wright   NewtonianIdealGasContext context = (NewtonianIdealGasContext)ctx;
24388626eedSJames Wright   const CeedScalar        *g       = context->g;
24488626eedSJames Wright   const CeedScalar         dt      = context->dt;
24588b783a1SJames Wright 
24688b783a1SJames Wright   CeedPragmaSIMD
24788b783a1SJames Wright       // Quadrature Point Loop
24888b783a1SJames Wright       for (CeedInt i = 0; i < Q; i++) {
2493d02368aSJames Wright     CeedScalar qi[5];
2503d02368aSJames Wright     for (CeedInt j = 0; j < 5; j++) qi[j] = q[j][i];
2515c677226SJed Brown     const CeedScalar x_i[3] = {x[0][i], x[1][i], x[2][i]};
2523d02368aSJames Wright     State            s      = StateFromQi(context, qi, x_i);
2535c677226SJed Brown 
25488b783a1SJames Wright     // -- Interp-to-Interp q_data
25588b783a1SJames Wright     const CeedScalar wdetJ = q_data[0][i];
25688b783a1SJames Wright     // -- Interp-to-Grad q_data
25788b783a1SJames Wright     // ---- Inverse of change of coordinate matrix: X_i,j
25888b783a1SJames Wright     // *INDENT-OFF*
259*2b730f8bSJeremy L Thompson     const CeedScalar dXdx[3][3] = {
260*2b730f8bSJeremy L Thompson         {q_data[1][i], q_data[2][i], q_data[3][i]},
26123d6ba15SJames Wright         {q_data[4][i], q_data[5][i], q_data[6][i]},
26223d6ba15SJames Wright         {q_data[7][i], q_data[8][i], q_data[9][i]}
26388b783a1SJames Wright     };
26488b783a1SJames Wright     // *INDENT-ON*
2655c677226SJed Brown     State grad_s[3];
266ba6664aeSJames Wright     for (CeedInt j = 0; j < 3; j++) {
2673d02368aSJames Wright       CeedScalar dx_i[3] = {0}, dqi[5];
268*2b730f8bSJeremy L Thompson       for (CeedInt k = 0; k < 5; k++) dqi[k] = Grad_q[0][k][i] * dXdx[0][j] + Grad_q[1][k][i] * dXdx[1][j] + Grad_q[2][k][i] * dXdx[2][j];
2695c677226SJed Brown       dx_i[j]   = 1.;
2703d02368aSJames Wright       grad_s[j] = StateFromQi_fwd(context, s, dqi, x_i, dx_i);
27188b783a1SJames Wright     }
2725c677226SJed Brown 
2735c677226SJed Brown     CeedScalar strain_rate[6], kmstress[6], stress[3][3], Fe[3];
2745c677226SJed Brown     KMStrainRate(grad_s, strain_rate);
2755c677226SJed Brown     NewtonianStress(context, strain_rate, kmstress);
2765c677226SJed Brown     KMUnpack(kmstress, stress);
2775c677226SJed Brown     ViscousEnergyFlux(context, s.Y, grad_s, stress, Fe);
2785c677226SJed Brown 
2795c677226SJed Brown     StateConservative F_inviscid[3];
2805c677226SJed Brown     FluxInviscid(context, s, F_inviscid);
2815c677226SJed Brown 
2825c677226SJed Brown     // Total flux
2835c677226SJed Brown     CeedScalar Flux[5][3];
2842b89d87eSLeila Ghaffari     FluxTotal(F_inviscid, stress, Fe, Flux);
2855c677226SJed Brown 
286*2b730f8bSJeremy L Thompson     for (CeedInt j = 0; j < 3; j++) {
287*2b730f8bSJeremy L Thompson       for (CeedInt k = 0; k < 5; k++) Grad_v[j][k][i] = -wdetJ * (dXdx[j][0] * Flux[k][0] + dXdx[j][1] * Flux[k][1] + dXdx[j][2] * Flux[k][2]);
288*2b730f8bSJeremy L Thompson     }
2895c677226SJed Brown 
2905c677226SJed Brown     const CeedScalar body_force[5] = {0, s.U.density * g[0], s.U.density * g[1], s.U.density * g[2], 0};
29188b783a1SJames Wright 
2922b89d87eSLeila Ghaffari     // -- Stabilization method: none (Galerkin), SU, or SUPG
2933d02368aSJames Wright     CeedScalar Tau_d[3], stab[5][3], U_dot[5] = {0}, qi_dot[5], dx0[3] = {0};
2943d02368aSJames Wright     for (int j = 0; j < 5; j++) qi_dot[j] = q_dot[j][i];
2953d02368aSJames Wright     State s_dot = StateFromQi_fwd(context, s, qi_dot, x_i, dx0);
2963d02368aSJames Wright     UnpackState_U(s_dot.U, U_dot);
2973d02368aSJames Wright 
298*2b730f8bSJeremy L Thompson     for (CeedInt j = 0; j < 5; j++) v[j][i] = wdetJ * (U_dot[j] - body_force[j]);
2992b89d87eSLeila Ghaffari     Tau_diagPrim(context, s, dXdx, dt, Tau_d);
3002b89d87eSLeila Ghaffari     Stabilization(context, s, Tau_d, grad_s, U_dot, body_force, x_i, stab);
30188b783a1SJames Wright 
302*2b730f8bSJeremy L Thompson     for (CeedInt j = 0; j < 5; j++) {
303*2b730f8bSJeremy L Thompson       for (CeedInt k = 0; k < 3; k++) Grad_v[k][j][i] += wdetJ * (stab[j][0] * dXdx[k][0] + stab[j][1] * dXdx[k][1] + stab[j][2] * dXdx[k][2]);
304*2b730f8bSJeremy L Thompson     }
3053d02368aSJames Wright     for (CeedInt j = 0; j < 5; j++) jac_data[j][i] = qi[j];
3063c4b7af6SJed Brown     for (CeedInt j = 0; j < 6; j++) jac_data[5 + j][i] = kmstress[j];
3073c4b7af6SJed Brown     for (CeedInt j = 0; j < 3; j++) jac_data[5 + 6 + j][i] = Tau_d[j];
30888b783a1SJames Wright 
30988b783a1SJames Wright   }  // End Quadrature Point Loop
31088b783a1SJames Wright 
31188b783a1SJames Wright   // Return
31288b783a1SJames Wright   return 0;
31388b783a1SJames Wright }
314e334ad8fSJed Brown 
315*2b730f8bSJeremy L Thompson CEED_QFUNCTION(IFunction_Newtonian_Conserv)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
3163d02368aSJames Wright   return IFunction_Newtonian(ctx, Q, in, out, StateFromU, StateFromU_fwd);
3173d02368aSJames Wright }
3183d02368aSJames Wright 
319*2b730f8bSJeremy L Thompson CEED_QFUNCTION(IFunction_Newtonian_Prim)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
3203d02368aSJames Wright   return IFunction_Newtonian(ctx, Q, in, out, StateFromY, StateFromY_fwd);
3213d02368aSJames Wright }
3223d02368aSJames Wright 
323dc805cc4SLeila Ghaffari // *****************************************************************************
3243d02368aSJames Wright // This QFunction implements the jacobian of the Navier-Stokes equations
325dc805cc4SLeila Ghaffari //   for implicit time stepping method.
326dc805cc4SLeila Ghaffari // *****************************************************************************
327*2b730f8bSJeremy L Thompson CEED_QFUNCTION_HELPER int IJacobian_Newtonian(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out, StateFromQi_t StateFromQi,
328*2b730f8bSJeremy L Thompson                                               StateFromQi_fwd_t StateFromQi_fwd) {
329e334ad8fSJed Brown   // *INDENT-OFF*
330e334ad8fSJed Brown   // Inputs
331*2b730f8bSJeremy L Thompson   const CeedScalar(*dq)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0], (*Grad_dq)[5][CEED_Q_VLA] = (const CeedScalar(*)[5][CEED_Q_VLA])in[1],
332*2b730f8bSJeremy L Thompson         (*q_data)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[2], (*x)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[3],
333e334ad8fSJed Brown         (*jac_data)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[4];
334e334ad8fSJed Brown   // Outputs
335*2b730f8bSJeremy L Thompson   CeedScalar(*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0], (*Grad_v)[5][CEED_Q_VLA] = (CeedScalar(*)[5][CEED_Q_VLA])out[1];
336e334ad8fSJed Brown   // *INDENT-ON*
337e334ad8fSJed Brown   // Context
338e334ad8fSJed Brown   NewtonianIdealGasContext context = (NewtonianIdealGasContext)ctx;
339e334ad8fSJed Brown   const CeedScalar        *g       = context->g;
340e334ad8fSJed Brown 
341e334ad8fSJed Brown   CeedPragmaSIMD
342e334ad8fSJed Brown       // Quadrature Point Loop
343e334ad8fSJed Brown       for (CeedInt i = 0; i < Q; i++) {
344e334ad8fSJed Brown     // -- Interp-to-Interp q_data
345e334ad8fSJed Brown     const CeedScalar wdetJ = q_data[0][i];
346e334ad8fSJed Brown     // -- Interp-to-Grad q_data
347e334ad8fSJed Brown     // ---- Inverse of change of coordinate matrix: X_i,j
348e334ad8fSJed Brown     // *INDENT-OFF*
349*2b730f8bSJeremy L Thompson     const CeedScalar dXdx[3][3] = {
350*2b730f8bSJeremy L Thompson         {q_data[1][i], q_data[2][i], q_data[3][i]},
35123d6ba15SJames Wright         {q_data[4][i], q_data[5][i], q_data[6][i]},
35223d6ba15SJames Wright         {q_data[7][i], q_data[8][i], q_data[9][i]}
353e334ad8fSJed Brown     };
354e334ad8fSJed Brown     // *INDENT-ON*
355e334ad8fSJed Brown 
356c98a0616SJames Wright     CeedScalar qi[5], kmstress[6], Tau_d[3];
3573d02368aSJames Wright     for (int j = 0; j < 5; j++) qi[j] = jac_data[j][i];
358e334ad8fSJed Brown     for (int j = 0; j < 6; j++) kmstress[j] = jac_data[5 + j][i];
359e334ad8fSJed Brown     for (int j = 0; j < 3; j++) Tau_d[j] = jac_data[5 + 6 + j][i];
360e334ad8fSJed Brown     const CeedScalar x_i[3] = {x[0][i], x[1][i], x[2][i]};
3613d02368aSJames Wright     State            s      = StateFromQi(context, qi, x_i);
362e334ad8fSJed Brown 
3633d02368aSJames Wright     CeedScalar dqi[5], dx0[3] = {0};
3643d02368aSJames Wright     for (int j = 0; j < 5; j++) dqi[j] = dq[j][i];
3653d02368aSJames Wright     State ds = StateFromQi_fwd(context, s, dqi, x_i, dx0);
366e334ad8fSJed Brown 
367e334ad8fSJed Brown     State grad_ds[3];
368e334ad8fSJed Brown     for (int j = 0; j < 3; j++) {
3693d02368aSJames Wright       CeedScalar dqi_j[5];
370*2b730f8bSJeremy L Thompson       for (int k = 0; k < 5; k++) dqi_j[k] = Grad_dq[0][k][i] * dXdx[0][j] + Grad_dq[1][k][i] * dXdx[1][j] + Grad_dq[2][k][i] * dXdx[2][j];
3713d02368aSJames Wright       grad_ds[j] = StateFromQi_fwd(context, s, dqi_j, x_i, dx0);
372e334ad8fSJed Brown     }
373e334ad8fSJed Brown 
374e334ad8fSJed Brown     CeedScalar dstrain_rate[6], dkmstress[6], stress[3][3], dstress[3][3], dFe[3];
375e334ad8fSJed Brown     KMStrainRate(grad_ds, dstrain_rate);
376e334ad8fSJed Brown     NewtonianStress(context, dstrain_rate, dkmstress);
377e334ad8fSJed Brown     KMUnpack(dkmstress, dstress);
378e334ad8fSJed Brown     KMUnpack(kmstress, stress);
379e334ad8fSJed Brown     ViscousEnergyFlux_fwd(context, s.Y, ds.Y, grad_ds, stress, dstress, dFe);
380e334ad8fSJed Brown 
381e334ad8fSJed Brown     StateConservative dF_inviscid[3];
382e334ad8fSJed Brown     FluxInviscid_fwd(context, s, ds, dF_inviscid);
383e334ad8fSJed Brown 
384e334ad8fSJed Brown     // Total flux
385e334ad8fSJed Brown     CeedScalar dFlux[5][3];
3862b89d87eSLeila Ghaffari     FluxTotal(dF_inviscid, dstress, dFe, dFlux);
387e334ad8fSJed Brown 
388*2b730f8bSJeremy L Thompson     for (int j = 0; j < 3; j++) {
389*2b730f8bSJeremy L Thompson       for (int k = 0; k < 5; k++) Grad_v[j][k][i] = -wdetJ * (dXdx[j][0] * dFlux[k][0] + dXdx[j][1] * dFlux[k][1] + dXdx[j][2] * dFlux[k][2]);
390*2b730f8bSJeremy L Thompson     }
391e334ad8fSJed Brown 
392e334ad8fSJed Brown     const CeedScalar dbody_force[5] = {0, ds.U.density * g[0], ds.U.density * g[1], ds.U.density * g[2], 0};
3933d02368aSJames Wright     CeedScalar       dU[5]          = {0.};
3943d02368aSJames Wright     UnpackState_U(ds.U, dU);
395*2b730f8bSJeremy L Thompson     for (int j = 0; j < 5; j++) v[j][i] = wdetJ * (context->ijacobian_time_shift * dU[j] - dbody_force[j]);
396e334ad8fSJed Brown 
3972b89d87eSLeila Ghaffari     // -- Stabilization method: none (Galerkin), SU, or SUPG
3982b89d87eSLeila Ghaffari     CeedScalar dstab[5][3], U_dot[5] = {0};
3992b89d87eSLeila Ghaffari     for (CeedInt j = 0; j < 5; j++) U_dot[j] = context->ijacobian_time_shift * dU[j];
4002b89d87eSLeila Ghaffari     Stabilization(context, s, Tau_d, grad_ds, U_dot, dbody_force, x_i, dstab);
4012b89d87eSLeila Ghaffari 
402*2b730f8bSJeremy L Thompson     for (int j = 0; j < 5; j++) {
403*2b730f8bSJeremy L Thompson       for (int k = 0; k < 3; k++) Grad_v[k][j][i] += wdetJ * (dstab[j][0] * dXdx[k][0] + dstab[j][1] * dXdx[k][1] + dstab[j][2] * dXdx[k][2]);
404*2b730f8bSJeremy L Thompson     }
405e334ad8fSJed Brown   }  // End Quadrature Point Loop
406e334ad8fSJed Brown   return 0;
407e334ad8fSJed Brown }
40865dd5cafSJames Wright 
409*2b730f8bSJeremy L Thompson CEED_QFUNCTION(IJacobian_Newtonian_Conserv)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
4103d02368aSJames Wright   return IJacobian_Newtonian(ctx, Q, in, out, StateFromU, StateFromU_fwd);
4113d02368aSJames Wright }
4123d02368aSJames Wright 
413*2b730f8bSJeremy L Thompson CEED_QFUNCTION(IJacobian_Newtonian_Prim)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
4143d02368aSJames Wright   return IJacobian_Newtonian(ctx, Q, in, out, StateFromY, StateFromY_fwd);
4153d02368aSJames Wright }
4163d02368aSJames Wright 
4172b89d87eSLeila Ghaffari // *****************************************************************************
41865dd5cafSJames Wright // Compute boundary integral (ie. for strongly set inflows)
4192b89d87eSLeila Ghaffari // *****************************************************************************
420*2b730f8bSJeremy L Thompson CEED_QFUNCTION_HELPER int BoundaryIntegral(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out, StateFromQi_t StateFromQi,
421*2b730f8bSJeremy L Thompson                                            StateFromQi_fwd_t StateFromQi_fwd) {
42265dd5cafSJames Wright   //*INDENT-OFF*
423*2b730f8bSJeremy L Thompson   const CeedScalar(*q)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0], (*Grad_q)[5][CEED_Q_VLA] = (const CeedScalar(*)[5][CEED_Q_VLA])in[1],
424*2b730f8bSJeremy L Thompson         (*q_data_sur)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[2], (*x)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[3];
42565dd5cafSJames Wright 
426*2b730f8bSJeremy L Thompson   CeedScalar(*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0], (*jac_data_sur)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[1];
42765dd5cafSJames Wright 
42865dd5cafSJames Wright   //*INDENT-ON*
42965dd5cafSJames Wright 
4302c4e60d7SJames Wright   const NewtonianIdealGasContext context     = (NewtonianIdealGasContext)ctx;
4312c4e60d7SJames Wright   const bool                     is_implicit = context->is_implicit;
43265dd5cafSJames Wright 
433*2b730f8bSJeremy L Thompson   CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
4342c4e60d7SJames Wright     const CeedScalar x_i[3] = {x[0][i], x[1][i], x[2][i]};
435efe9d856SJames Wright     const CeedScalar qi[5]  = {q[0][i], q[1][i], q[2][i], q[3][i], q[4][i]};
436efe9d856SJames Wright     State            s      = StateFromQi(context, qi, x_i);
43765dd5cafSJames Wright 
43865dd5cafSJames Wright     const CeedScalar wdetJb = (is_implicit ? -1. : 1.) * q_data_sur[0][i];
4395bce47c7SJames Wright     // ---- Normal vector
440*2b730f8bSJeremy L Thompson     const CeedScalar norm[3] = {q_data_sur[1][i], q_data_sur[2][i], q_data_sur[3][i]};
44165dd5cafSJames Wright 
4422c4e60d7SJames Wright     const CeedScalar dXdx[2][3] = {
4432c4e60d7SJames Wright         {q_data_sur[4][i], q_data_sur[5][i], q_data_sur[6][i]},
4442c4e60d7SJames Wright         {q_data_sur[7][i], q_data_sur[8][i], q_data_sur[9][i]}
4452c4e60d7SJames Wright     };
44665dd5cafSJames Wright 
4472c4e60d7SJames Wright     State grad_s[3];
4482c4e60d7SJames Wright     for (CeedInt j = 0; j < 3; j++) {
449efe9d856SJames Wright       CeedScalar dx_i[3] = {0}, dqi[5];
450*2b730f8bSJeremy L Thompson       for (CeedInt k = 0; k < 5; k++) dqi[k] = Grad_q[0][k][i] * dXdx[0][j] + Grad_q[1][k][i] * dXdx[1][j];
4512c4e60d7SJames Wright       dx_i[j]   = 1.;
452efe9d856SJames Wright       grad_s[j] = StateFromQi_fwd(context, s, dqi, x_i, dx_i);
4532c4e60d7SJames Wright     }
45465dd5cafSJames Wright 
4552c4e60d7SJames Wright     CeedScalar strain_rate[6], kmstress[6], stress[3][3], Fe[3];
4562c4e60d7SJames Wright     KMStrainRate(grad_s, strain_rate);
4572c4e60d7SJames Wright     NewtonianStress(context, strain_rate, kmstress);
4582c4e60d7SJames Wright     KMUnpack(kmstress, stress);
4592c4e60d7SJames Wright     ViscousEnergyFlux(context, s.Y, grad_s, stress, Fe);
4602c4e60d7SJames Wright 
4612c4e60d7SJames Wright     StateConservative F_inviscid[3];
4622c4e60d7SJames Wright     FluxInviscid(context, s, F_inviscid);
4632c4e60d7SJames Wright 
4645bce47c7SJames Wright     CeedScalar Flux[5];
4655bce47c7SJames Wright     FluxTotal_Boundary(F_inviscid, stress, Fe, norm, Flux);
4662c4e60d7SJames Wright 
4675bce47c7SJames Wright     for (CeedInt j = 0; j < 5; j++) v[j][i] = -wdetJb * Flux[j];
46865dd5cafSJames Wright 
4695bce47c7SJames Wright     for (int j = 0; j < 5; j++) jac_data_sur[j][i] = qi[j];
470b55ac660SJames Wright     for (int j = 0; j < 6; j++) jac_data_sur[5 + j][i] = kmstress[j];
47165dd5cafSJames Wright   }
47265dd5cafSJames Wright   return 0;
47365dd5cafSJames Wright }
47465dd5cafSJames Wright 
475*2b730f8bSJeremy L Thompson CEED_QFUNCTION(BoundaryIntegral_Conserv)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
47620840d50SJames Wright   return BoundaryIntegral(ctx, Q, in, out, StateFromU, StateFromU_fwd);
47720840d50SJames Wright }
47820840d50SJames Wright 
479*2b730f8bSJeremy L Thompson CEED_QFUNCTION(BoundaryIntegral_Prim)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
48020840d50SJames Wright   return BoundaryIntegral(ctx, Q, in, out, StateFromY, StateFromY_fwd);
48120840d50SJames Wright }
48220840d50SJames Wright 
4832b89d87eSLeila Ghaffari // *****************************************************************************
484b55ac660SJames Wright // Jacobian for "set nothing" boundary integral
4852b89d87eSLeila Ghaffari // *****************************************************************************
486*2b730f8bSJeremy L Thompson CEED_QFUNCTION_HELPER int BoundaryIntegral_Jacobian(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out,
48720840d50SJames Wright                                                     StateFromQi_t StateFromQi, StateFromQi_fwd_t StateFromQi_fwd) {
488b55ac660SJames Wright   // *INDENT-OFF*
489b55ac660SJames Wright   // Inputs
490*2b730f8bSJeremy L Thompson   const CeedScalar(*dq)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0], (*Grad_dq)[5][CEED_Q_VLA] = (const CeedScalar(*)[5][CEED_Q_VLA])in[1],
491*2b730f8bSJeremy L Thompson         (*q_data_sur)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[2], (*x)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[3],
492b55ac660SJames Wright         (*jac_data_sur)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[4];
493b55ac660SJames Wright   // Outputs
494b55ac660SJames Wright   CeedScalar(*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0];
495b55ac660SJames Wright   // *INDENT-ON*
496b55ac660SJames Wright 
497b55ac660SJames Wright   const NewtonianIdealGasContext context  = (NewtonianIdealGasContext)ctx;
498b55ac660SJames Wright   const bool                     implicit = context->is_implicit;
499b55ac660SJames Wright 
500b55ac660SJames Wright   CeedPragmaSIMD
501b55ac660SJames Wright       // Quadrature Point Loop
502b55ac660SJames Wright       for (CeedInt i = 0; i < Q; i++) {
503b55ac660SJames Wright     const CeedScalar x_i[3]     = {x[0][i], x[1][i], x[2][i]};
504b55ac660SJames Wright     const CeedScalar wdetJb     = (implicit ? -1. : 1.) * q_data_sur[0][i];
505*2b730f8bSJeremy L Thompson     const CeedScalar norm[3]    = {q_data_sur[1][i], q_data_sur[2][i], q_data_sur[3][i]};
506b55ac660SJames Wright     const CeedScalar dXdx[2][3] = {
507b55ac660SJames Wright         {q_data_sur[4][i], q_data_sur[5][i], q_data_sur[6][i]},
508b55ac660SJames Wright         {q_data_sur[7][i], q_data_sur[8][i], q_data_sur[9][i]}
509b55ac660SJames Wright     };
510b55ac660SJames Wright 
511efe9d856SJames Wright     CeedScalar qi[5], kmstress[6], dqi[5], dx_i[3] = {0.};
512efe9d856SJames Wright     for (int j = 0; j < 5; j++) qi[j] = jac_data_sur[j][i];
513b55ac660SJames Wright     for (int j = 0; j < 6; j++) kmstress[j] = jac_data_sur[5 + j][i];
514efe9d856SJames Wright     for (int j = 0; j < 5; j++) dqi[j] = dq[j][i];
51557e55a1cSJames Wright 
516efe9d856SJames Wright     State s  = StateFromQi(context, qi, x_i);
517efe9d856SJames Wright     State ds = StateFromQi_fwd(context, s, dqi, x_i, dx_i);
518b55ac660SJames Wright 
519b55ac660SJames Wright     State grad_ds[3];
520b55ac660SJames Wright     for (CeedInt j = 0; j < 3; j++) {
521efe9d856SJames Wright       CeedScalar dx_i[3] = {0}, dqi_j[5];
522*2b730f8bSJeremy L Thompson       for (CeedInt k = 0; k < 5; k++) dqi_j[k] = Grad_dq[0][k][i] * dXdx[0][j] + Grad_dq[1][k][i] * dXdx[1][j];
523b55ac660SJames Wright       dx_i[j]    = 1.;
524efe9d856SJames Wright       grad_ds[j] = StateFromQi_fwd(context, s, dqi_j, x_i, dx_i);
525b55ac660SJames Wright     }
526b55ac660SJames Wright 
527b55ac660SJames Wright     CeedScalar dstrain_rate[6], dkmstress[6], stress[3][3], dstress[3][3], dFe[3];
528b55ac660SJames Wright     KMStrainRate(grad_ds, dstrain_rate);
529b55ac660SJames Wright     NewtonianStress(context, dstrain_rate, dkmstress);
530b55ac660SJames Wright     KMUnpack(dkmstress, dstress);
531b55ac660SJames Wright     KMUnpack(kmstress, stress);
532b55ac660SJames Wright     ViscousEnergyFlux_fwd(context, s.Y, ds.Y, grad_ds, stress, dstress, dFe);
533b55ac660SJames Wright 
534b55ac660SJames Wright     StateConservative dF_inviscid[3];
535b55ac660SJames Wright     FluxInviscid_fwd(context, s, ds, dF_inviscid);
536b55ac660SJames Wright 
5375bce47c7SJames Wright     CeedScalar dFlux[5];
5385bce47c7SJames Wright     FluxTotal_Boundary(dF_inviscid, dstress, dFe, norm, dFlux);
539b55ac660SJames Wright 
5405bce47c7SJames Wright     for (int j = 0; j < 5; j++) v[j][i] = -wdetJb * dFlux[j];
541b55ac660SJames Wright   }  // End Quadrature Point Loop
542b55ac660SJames Wright   return 0;
543b55ac660SJames Wright }
544b55ac660SJames Wright 
545*2b730f8bSJeremy L Thompson CEED_QFUNCTION(BoundaryIntegral_Jacobian_Conserv)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
54620840d50SJames Wright   return BoundaryIntegral_Jacobian(ctx, Q, in, out, StateFromU, StateFromU_fwd);
54720840d50SJames Wright }
54820840d50SJames Wright 
549*2b730f8bSJeremy L Thompson CEED_QFUNCTION(BoundaryIntegral_Jacobian_Prim)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
55020840d50SJames Wright   return BoundaryIntegral_Jacobian(ctx, Q, in, out, StateFromY, StateFromY_fwd);
55120840d50SJames Wright }
55220840d50SJames Wright 
5532b89d87eSLeila Ghaffari // *****************************************************************************
55430e9fa81SJames Wright // Outflow boundary condition, weakly setting a constant pressure
5552b89d87eSLeila Ghaffari // *****************************************************************************
556*2b730f8bSJeremy L Thompson CEED_QFUNCTION_HELPER int PressureOutflow(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out, StateFromQi_t StateFromQi,
557*2b730f8bSJeremy L Thompson                                           StateFromQi_fwd_t StateFromQi_fwd) {
55830e9fa81SJames Wright   // *INDENT-OFF*
55930e9fa81SJames Wright   // Inputs
560*2b730f8bSJeremy L Thompson   const CeedScalar(*q)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0], (*Grad_q)[5][CEED_Q_VLA] = (const CeedScalar(*)[5][CEED_Q_VLA])in[1],
561*2b730f8bSJeremy L Thompson         (*q_data_sur)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[2], (*x)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[3];
56230e9fa81SJames Wright   // Outputs
563*2b730f8bSJeremy L Thompson   CeedScalar(*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0], (*jac_data_sur)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[1];
56430e9fa81SJames Wright   // *INDENT-ON*
56530e9fa81SJames Wright 
56630e9fa81SJames Wright   const NewtonianIdealGasContext context  = (NewtonianIdealGasContext)ctx;
56730e9fa81SJames Wright   const bool                     implicit = context->is_implicit;
56830e9fa81SJames Wright   const CeedScalar               P0       = context->P0;
56930e9fa81SJames Wright 
57030e9fa81SJames Wright   CeedPragmaSIMD
57130e9fa81SJames Wright       // Quadrature Point Loop
57230e9fa81SJames Wright       for (CeedInt i = 0; i < Q; i++) {
57330e9fa81SJames Wright     // Setup
57430e9fa81SJames Wright     // -- Interp in
575ce9b5c20SJames Wright     const CeedScalar x_i[3] = {x[0][i], x[1][i], x[2][i]};
576efe9d856SJames Wright     const CeedScalar qi[5]  = {q[0][i], q[1][i], q[2][i], q[3][i], q[4][i]};
577efe9d856SJames Wright     State            s      = StateFromQi(context, qi, x_i);
578ce9b5c20SJames Wright     s.Y.pressure            = P0;
57930e9fa81SJames Wright 
58030e9fa81SJames Wright     // -- Interp-to-Interp q_data
58130e9fa81SJames Wright     // For explicit mode, the surface integral is on the RHS of ODE q_dot = f(q).
58230e9fa81SJames Wright     // For implicit mode, it gets pulled to the LHS of implicit ODE/DAE g(q_dot, q).
58330e9fa81SJames Wright     // We can effect this by swapping the sign on this weight
58430e9fa81SJames Wright     const CeedScalar wdetJb = (implicit ? -1. : 1.) * q_data_sur[0][i];
58530e9fa81SJames Wright 
5865bce47c7SJames Wright     // ---- Normal vector
58720840d50SJames Wright     const CeedScalar norm[3] = {q_data_sur[1][i], q_data_sur[2][i], q_data_sur[3][i]};
58830e9fa81SJames Wright 
589ce9b5c20SJames Wright     const CeedScalar dXdx[2][3] = {
590ce9b5c20SJames Wright         {q_data_sur[4][i], q_data_sur[5][i], q_data_sur[6][i]},
591ce9b5c20SJames Wright         {q_data_sur[7][i], q_data_sur[8][i], q_data_sur[9][i]}
592ce9b5c20SJames Wright     };
59330e9fa81SJames Wright 
594ce9b5c20SJames Wright     State grad_s[3];
595ce9b5c20SJames Wright     for (CeedInt j = 0; j < 3; j++) {
596efe9d856SJames Wright       CeedScalar dx_i[3] = {0}, dqi[5];
597*2b730f8bSJeremy L Thompson       for (CeedInt k = 0; k < 5; k++) dqi[k] = Grad_q[0][k][i] * dXdx[0][j] + Grad_q[1][k][i] * dXdx[1][j];
598ce9b5c20SJames Wright       dx_i[j]   = 1.;
599efe9d856SJames Wright       grad_s[j] = StateFromQi_fwd(context, s, dqi, x_i, dx_i);
600ce9b5c20SJames Wright     }
601ce9b5c20SJames Wright 
602ce9b5c20SJames Wright     CeedScalar strain_rate[6], kmstress[6], stress[3][3], Fe[3];
603ce9b5c20SJames Wright     KMStrainRate(grad_s, strain_rate);
604ce9b5c20SJames Wright     NewtonianStress(context, strain_rate, kmstress);
605ce9b5c20SJames Wright     KMUnpack(kmstress, stress);
606ce9b5c20SJames Wright     ViscousEnergyFlux(context, s.Y, grad_s, stress, Fe);
607ce9b5c20SJames Wright 
608ce9b5c20SJames Wright     StateConservative F_inviscid[3];
609ce9b5c20SJames Wright     FluxInviscid(context, s, F_inviscid);
610ce9b5c20SJames Wright 
6115bce47c7SJames Wright     CeedScalar Flux[5];
6125bce47c7SJames Wright     FluxTotal_Boundary(F_inviscid, stress, Fe, norm, Flux);
61330e9fa81SJames Wright 
6145bce47c7SJames Wright     for (CeedInt j = 0; j < 5; j++) v[j][i] = -wdetJb * Flux[j];
61530e9fa81SJames Wright 
61630e9fa81SJames Wright     // Save values for Jacobian
6175bce47c7SJames Wright     for (int j = 0; j < 5; j++) jac_data_sur[j][i] = qi[j];
6180ec2498eSJames Wright     for (int j = 0; j < 6; j++) jac_data_sur[5 + j][i] = kmstress[j];
61930e9fa81SJames Wright   }  // End Quadrature Point Loop
62030e9fa81SJames Wright   return 0;
62130e9fa81SJames Wright }
62230e9fa81SJames Wright 
623*2b730f8bSJeremy L Thompson CEED_QFUNCTION(PressureOutflow_Conserv)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
62420840d50SJames Wright   return PressureOutflow(ctx, Q, in, out, StateFromU, StateFromU_fwd);
62520840d50SJames Wright }
62620840d50SJames Wright 
627*2b730f8bSJeremy L Thompson CEED_QFUNCTION(PressureOutflow_Prim)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
62820840d50SJames Wright   return PressureOutflow(ctx, Q, in, out, StateFromY, StateFromY_fwd);
62920840d50SJames Wright }
63020840d50SJames Wright 
6312b89d87eSLeila Ghaffari // *****************************************************************************
63230e9fa81SJames Wright // Jacobian for weak-pressure outflow boundary condition
6332b89d87eSLeila Ghaffari // *****************************************************************************
634*2b730f8bSJeremy L Thompson CEED_QFUNCTION_HELPER int PressureOutflow_Jacobian(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out,
63520840d50SJames Wright                                                    StateFromQi_t StateFromQi, StateFromQi_fwd_t StateFromQi_fwd) {
63630e9fa81SJames Wright   // *INDENT-OFF*
63730e9fa81SJames Wright   // Inputs
638*2b730f8bSJeremy L Thompson   const CeedScalar(*dq)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0], (*Grad_dq)[5][CEED_Q_VLA] = (const CeedScalar(*)[5][CEED_Q_VLA])in[1],
639*2b730f8bSJeremy L Thompson         (*q_data_sur)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[2], (*x)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[3],
6400ec2498eSJames Wright         (*jac_data_sur)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[4];
64130e9fa81SJames Wright   // Outputs
64230e9fa81SJames Wright   CeedScalar(*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0];
64330e9fa81SJames Wright   // *INDENT-ON*
64430e9fa81SJames Wright 
64530e9fa81SJames Wright   const NewtonianIdealGasContext context  = (NewtonianIdealGasContext)ctx;
64630e9fa81SJames Wright   const bool                     implicit = context->is_implicit;
64730e9fa81SJames Wright 
64830e9fa81SJames Wright   CeedPragmaSIMD
64930e9fa81SJames Wright       // Quadrature Point Loop
65030e9fa81SJames Wright       for (CeedInt i = 0; i < Q; i++) {
6510ec2498eSJames Wright     const CeedScalar x_i[3]     = {x[0][i], x[1][i], x[2][i]};
65230e9fa81SJames Wright     const CeedScalar wdetJb     = (implicit ? -1. : 1.) * q_data_sur[0][i];
65320840d50SJames Wright     const CeedScalar norm[3]    = {q_data_sur[1][i], q_data_sur[2][i], q_data_sur[3][i]};
6540ec2498eSJames Wright     const CeedScalar dXdx[2][3] = {
6550ec2498eSJames Wright         {q_data_sur[4][i], q_data_sur[5][i], q_data_sur[6][i]},
6560ec2498eSJames Wright         {q_data_sur[7][i], q_data_sur[8][i], q_data_sur[9][i]}
6570ec2498eSJames Wright     };
6580ec2498eSJames Wright 
659efe9d856SJames Wright     CeedScalar qi[5], kmstress[6], dqi[5], dx_i[3] = {0.};
660efe9d856SJames Wright     for (int j = 0; j < 5; j++) qi[j] = jac_data_sur[j][i];
6610ec2498eSJames Wright     for (int j = 0; j < 6; j++) kmstress[j] = jac_data_sur[5 + j][i];
662efe9d856SJames Wright     for (int j = 0; j < 5; j++) dqi[j] = dq[j][i];
66357e55a1cSJames Wright 
664efe9d856SJames Wright     State s       = StateFromQi(context, qi, x_i);
665efe9d856SJames Wright     State ds      = StateFromQi_fwd(context, s, dqi, x_i, dx_i);
6660ec2498eSJames Wright     s.Y.pressure  = context->P0;
6670ec2498eSJames Wright     ds.Y.pressure = 0.;
6680ec2498eSJames Wright 
6690ec2498eSJames Wright     State grad_ds[3];
6700ec2498eSJames Wright     for (CeedInt j = 0; j < 3; j++) {
671efe9d856SJames Wright       CeedScalar dx_i[3] = {0}, dqi_j[5];
672*2b730f8bSJeremy L Thompson       for (CeedInt k = 0; k < 5; k++) dqi_j[k] = Grad_dq[0][k][i] * dXdx[0][j] + Grad_dq[1][k][i] * dXdx[1][j];
6730ec2498eSJames Wright       dx_i[j]    = 1.;
674efe9d856SJames Wright       grad_ds[j] = StateFromQi_fwd(context, s, dqi_j, x_i, dx_i);
6750ec2498eSJames Wright     }
6760ec2498eSJames Wright 
6770ec2498eSJames Wright     CeedScalar dstrain_rate[6], dkmstress[6], stress[3][3], dstress[3][3], dFe[3];
6780ec2498eSJames Wright     KMStrainRate(grad_ds, dstrain_rate);
6790ec2498eSJames Wright     NewtonianStress(context, dstrain_rate, dkmstress);
6800ec2498eSJames Wright     KMUnpack(dkmstress, dstress);
6810ec2498eSJames Wright     KMUnpack(kmstress, stress);
6820ec2498eSJames Wright     ViscousEnergyFlux_fwd(context, s.Y, ds.Y, grad_ds, stress, dstress, dFe);
68330e9fa81SJames Wright 
684b5d317f8SJames Wright     StateConservative dF_inviscid[3];
685b5d317f8SJames Wright     FluxInviscid_fwd(context, s, ds, dF_inviscid);
68630e9fa81SJames Wright 
6875bce47c7SJames Wright     CeedScalar dFlux[5];
6885bce47c7SJames Wright     FluxTotal_Boundary(dF_inviscid, dstress, dFe, norm, dFlux);
689b5d317f8SJames Wright 
6905bce47c7SJames Wright     for (int j = 0; j < 5; j++) v[j][i] = -wdetJb * dFlux[j];
69130e9fa81SJames Wright   }  // End Quadrature Point Loop
69230e9fa81SJames Wright   return 0;
69330e9fa81SJames Wright }
69430e9fa81SJames Wright 
695*2b730f8bSJeremy L Thompson CEED_QFUNCTION(PressureOutflow_Jacobian_Conserv)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
69620840d50SJames Wright   return PressureOutflow_Jacobian(ctx, Q, in, out, StateFromU, StateFromU_fwd);
69720840d50SJames Wright }
69820840d50SJames Wright 
699*2b730f8bSJeremy L Thompson CEED_QFUNCTION(PressureOutflow_Jacobian_Prim)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
70020840d50SJames Wright   return PressureOutflow_Jacobian(ctx, Q, in, out, StateFromY, StateFromY_fwd);
70120840d50SJames Wright }
702*2b730f8bSJeremy L Thompson 
70388b783a1SJames Wright #endif  // newtonian_h
704