xref: /libCEED/examples/fluids/qfunctions/newtonian.h (revision 6222cb59140a973754caeb8d899c33bfbd3dcb5f)
1 // Copyright (c) 2017-2022, Lawrence Livermore National Security, LLC and other CEED contributors.
2 // All Rights Reserved. See the top-level LICENSE and NOTICE files for details.
3 //
4 // SPDX-License-Identifier: BSD-2-Clause
5 //
6 // This file is part of CEED:  http://github.com/ceed
7 
8 /// @file
9 /// Operator for Navier-Stokes example using PETSc
10 
11 
12 #ifndef newtonian_h
13 #define newtonian_h
14 
15 #include <math.h>
16 #include <ceed.h>
17 #include "newtonian_types.h"
18 #include "newtonian_state.h"
19 #include "utils.h"
20 #include "stabilization.h"
21 
22 // *****************************************************************************
23 // This QFunction sets a "still" initial condition for generic Newtonian IG problems
24 // *****************************************************************************
25 CEED_QFUNCTION(ICsNewtonianIG)(void *ctx, CeedInt Q,
26                                const CeedScalar *const *in, CeedScalar *const *out) {
27   // Inputs
28   const CeedScalar (*X)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0];
29 
30   // Outputs
31   CeedScalar (*q0)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0];
32 
33   // Context
34   const SetupContext context = (SetupContext)ctx;
35   const CeedScalar theta0    = context->theta0;
36   const CeedScalar P0        = context->P0;
37   const CeedScalar cv        = context->cv;
38   const CeedScalar cp        = context->cp;
39   const CeedScalar *g        = context->g;
40   const CeedScalar Rd        = cp - cv;
41 
42   // Quadrature Point Loop
43   CeedPragmaSIMD
44   for (CeedInt i=0; i<Q; i++) {
45     CeedScalar q[5] = {0.};
46 
47     // Setup
48     // -- Coordinates
49     const CeedScalar x[3] = {X[0][i], X[1][i], X[2][i]};
50     const CeedScalar e_potential = -Dot3(g, x);
51 
52     // -- Density
53     const CeedScalar rho = P0 / (Rd*theta0);
54 
55     // Initial Conditions
56     q[0] = rho;
57     q[1] = 0.0;
58     q[2] = 0.0;
59     q[3] = 0.0;
60     q[4] = rho * (cv*theta0 + e_potential);
61 
62     for (CeedInt j=0; j<5; j++)
63       q0[j][i] = q[j];
64 
65   } // End of Quadrature Point Loop
66   return 0;
67 }
68 
69 // *****************************************************************************
70 // This QFunction sets a "still" initial condition for generic Newtonian IG
71 //   problems in primitive variables
72 // *****************************************************************************
73 CEED_QFUNCTION(ICsNewtonianIG_Prim)(void *ctx, CeedInt Q,
74                                     const CeedScalar *const *in, CeedScalar *const *out) {
75   // Outputs
76   CeedScalar (*q0)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0];
77 
78   // Context
79   const SetupContext context = (SetupContext)ctx;
80   const CeedScalar theta0    = context->theta0;
81   const CeedScalar P0        = context->P0;
82 
83   // Quadrature Point Loop
84   CeedPragmaSIMD
85   for (CeedInt i=0; i<Q; i++) {
86     CeedScalar q[5] = {0.};
87 
88     // Initial Conditions
89     q[0] = P0;
90     q[1] = 0.0;
91     q[2] = 0.0;
92     q[3] = 0.0;
93     q[4] = theta0;
94 
95     for (CeedInt j=0; j<5; j++)
96       q0[j][i] = q[j];
97 
98   } // End of Quadrature Point Loop
99   return 0;
100 }
101 
102 // *****************************************************************************
103 // This QFunction implements the following formulation of Navier-Stokes with
104 //   explicit time stepping method
105 //
106 // This is 3D compressible Navier-Stokes in conservation form with state
107 //   variables of density, momentum density, and total energy density.
108 //
109 // State Variables: q = ( rho, U1, U2, U3, E )
110 //   rho - Mass Density
111 //   Ui  - Momentum Density,      Ui = rho ui
112 //   E   - Total Energy Density,  E  = rho (cv T + (u u)/2 + g z)
113 //
114 // Navier-Stokes Equations:
115 //   drho/dt + div( U )                               = 0
116 //   dU/dt   + div( rho (u x u) + P I3 ) + rho g khat = div( Fu )
117 //   dE/dt   + div( (E + P) u )                       = div( Fe )
118 //
119 // Viscous Stress:
120 //   Fu = mu (grad( u ) + grad( u )^T + lambda div ( u ) I3)
121 //
122 // Thermal Stress:
123 //   Fe = u Fu + k grad( T )
124 // Equation of State
125 //   P = (gamma - 1) (E - rho (u u) / 2 - rho g z)
126 //
127 // Stabilization:
128 //   Tau = diag(TauC, TauM, TauM, TauM, TauE)
129 //     f1 = rho  sqrt(ui uj gij)
130 //     gij = dXi/dX * dXi/dX
131 //     TauC = Cc f1 / (8 gii)
132 //     TauM = min( 1 , 1 / f1 )
133 //     TauE = TauM / (Ce cv)
134 //
135 //  SU   = Galerkin + grad(v) . ( Ai^T * Tau * (Aj q,j) )
136 //
137 // Constants:
138 //   lambda = - 2 / 3,  From Stokes hypothesis
139 //   mu              ,  Dynamic viscosity
140 //   k               ,  Thermal conductivity
141 //   cv              ,  Specific heat, constant volume
142 //   cp              ,  Specific heat, constant pressure
143 //   g               ,  Gravity
144 //   gamma  = cp / cv,  Specific heat ratio
145 //
146 // We require the product of the inverse of the Jacobian (dXdx_j,k) and
147 // its transpose (dXdx_k,j) to properly compute integrals of the form:
148 // int( gradv gradu )
149 //
150 // *****************************************************************************
151 CEED_QFUNCTION(RHSFunction_Newtonian)(void *ctx, CeedInt Q,
152                                       const CeedScalar *const *in, CeedScalar *const *out) {
153   // *INDENT-OFF*
154   // Inputs
155   const CeedScalar (*q)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0],
156                    (*Grad_q)[5][CEED_Q_VLA] = (const CeedScalar(*)[5][CEED_Q_VLA])in[1],
157                    (*q_data)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[2],
158                    (*x)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[3];
159   // Outputs
160   CeedScalar (*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0],
161              (*Grad_v)[5][CEED_Q_VLA] = (CeedScalar(*)[5][CEED_Q_VLA])out[1];
162   // *INDENT-ON*
163 
164   // Context
165   NewtonianIdealGasContext context = (NewtonianIdealGasContext)ctx;
166   const CeedScalar *g = context->g;
167   const CeedScalar dt = context->dt;
168 
169   CeedPragmaSIMD
170   // Quadrature Point Loop
171   for (CeedInt i=0; i<Q; i++) {
172     CeedScalar U[5];
173     for (int j=0; j<5; j++) U[j] = q[j][i];
174     const CeedScalar x_i[3] = {x[0][i], x[1][i], x[2][i]};
175     State s = StateFromU(context, U, x_i);
176 
177     // -- Interp-to-Interp q_data
178     const CeedScalar wdetJ      =   q_data[0][i];
179     // -- Interp-to-Grad q_data
180     // ---- Inverse of change of coordinate matrix: X_i,j
181     // *INDENT-OFF*
182     const CeedScalar dXdx[3][3] = {{q_data[1][i],
183                                     q_data[2][i],
184                                     q_data[3][i]},
185                                    {q_data[4][i],
186                                     q_data[5][i],
187                                     q_data[6][i]},
188                                    {q_data[7][i],
189                                     q_data[8][i],
190                                     q_data[9][i]}
191                                   };
192     // *INDENT-ON*
193     State grad_s[3];
194     for (CeedInt j=0; j<3; j++) {
195       CeedScalar dx_i[3] = {0}, dU[5];
196       for (CeedInt k=0; k<5; k++)
197         dU[k] = Grad_q[0][k][i] * dXdx[0][j] +
198                 Grad_q[1][k][i] * dXdx[1][j] +
199                 Grad_q[2][k][i] * dXdx[2][j];
200       dx_i[j] = 1.;
201       grad_s[j] = StateFromU_fwd(context, s, dU, x_i, dx_i);
202     }
203 
204     CeedScalar strain_rate[6], kmstress[6], stress[3][3], Fe[3];
205     KMStrainRate(grad_s, strain_rate);
206     NewtonianStress(context, strain_rate, kmstress);
207     KMUnpack(kmstress, stress);
208     ViscousEnergyFlux(context, s.Y, grad_s, stress, Fe);
209 
210     StateConservative F_inviscid[3];
211     FluxInviscid(context, s, F_inviscid);
212 
213     // Total flux
214     CeedScalar Flux[5][3];
215     FluxTotal(F_inviscid, stress, Fe, Flux);
216 
217     for (CeedInt j=0; j<3; j++)
218       for (CeedInt k=0; k<5; k++)
219         Grad_v[j][k][i] = wdetJ * (dXdx[j][0] * Flux[k][0] +
220                                    dXdx[j][1] * Flux[k][1] +
221                                    dXdx[j][2] * Flux[k][2]);
222 
223     const CeedScalar body_force[5] = {0, s.U.density *g[0], s.U.density *g[1], s.U.density *g[2], 0};
224     for (int j=0; j<5; j++)
225       v[j][i] = wdetJ * body_force[j];
226 
227     // -- Stabilization method: none (Galerkin), SU, or SUPG
228     CeedScalar Tau_d[3], stab[5][3], U_dot[5] = {0};
229     Tau_diagPrim(context, s, dXdx, dt, Tau_d);
230     Stabilization(context, s, Tau_d, grad_s, U_dot, body_force, x_i, stab);
231 
232     for (CeedInt j=0; j<5; j++)
233       for (CeedInt k=0; k<3; k++)
234         Grad_v[k][j][i] -= wdetJ*(stab[j][0] * dXdx[k][0] +
235                                   stab[j][1] * dXdx[k][1] +
236                                   stab[j][2] * dXdx[k][2]);
237 
238   } // End Quadrature Point Loop
239 
240   // Return
241   return 0;
242 }
243 
244 // *****************************************************************************
245 // This QFunction implements the Navier-Stokes equations (mentioned above) with
246 //   implicit time stepping method
247 //
248 //  SU   = Galerkin + grad(v) . ( Ai^T * Tau * (Aj q,j) )
249 //  SUPG = Galerkin + grad(v) . ( Ai^T * Tau * (q_dot + Aj q,j - body force) )
250 //                                       (diffussive terms will be added later)
251 //
252 // *****************************************************************************
253 CEED_QFUNCTION(IFunction_Newtonian)(void *ctx, CeedInt Q,
254                                     const CeedScalar *const *in, CeedScalar *const *out) {
255   // *INDENT-OFF*
256   // Inputs
257   const CeedScalar (*q)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0],
258                    (*Grad_q)[5][CEED_Q_VLA] = (const CeedScalar(*)[5][CEED_Q_VLA])in[1],
259                    (*q_dot)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[2],
260                    (*q_data)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[3],
261                    (*x)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[4];
262   // Outputs
263   CeedScalar (*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0],
264              (*Grad_v)[5][CEED_Q_VLA] = (CeedScalar(*)[5][CEED_Q_VLA])out[1],
265              (*jac_data)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[2];
266   // *INDENT-ON*
267   // Context
268   NewtonianIdealGasContext context = (NewtonianIdealGasContext)ctx;
269   const CeedScalar *g     = context->g;
270   const CeedScalar dt     = context->dt;
271 
272   CeedPragmaSIMD
273   // Quadrature Point Loop
274   for (CeedInt i=0; i<Q; i++) {
275     CeedScalar U[5];
276     for (CeedInt j=0; j<5; j++) U[j] = q[j][i];
277     const CeedScalar x_i[3] = {x[0][i], x[1][i], x[2][i]};
278     State s = StateFromU(context, U, x_i);
279 
280     // -- Interp-to-Interp q_data
281     const CeedScalar wdetJ      =   q_data[0][i];
282     // -- Interp-to-Grad q_data
283     // ---- Inverse of change of coordinate matrix: X_i,j
284     // *INDENT-OFF*
285     const CeedScalar dXdx[3][3] = {{q_data[1][i],
286                                     q_data[2][i],
287                                     q_data[3][i]},
288                                    {q_data[4][i],
289                                     q_data[5][i],
290                                     q_data[6][i]},
291                                    {q_data[7][i],
292                                     q_data[8][i],
293                                     q_data[9][i]}
294                                   };
295     // *INDENT-ON*
296     State grad_s[3];
297     for (CeedInt j=0; j<3; j++) {
298       CeedScalar dx_i[3] = {0}, dU[5];
299       for (CeedInt k=0; k<5; k++)
300         dU[k] = Grad_q[0][k][i] * dXdx[0][j] +
301                 Grad_q[1][k][i] * dXdx[1][j] +
302                 Grad_q[2][k][i] * dXdx[2][j];
303       dx_i[j] = 1.;
304       grad_s[j] = StateFromU_fwd(context, s, dU, x_i, dx_i);
305     }
306 
307     CeedScalar strain_rate[6], kmstress[6], stress[3][3], Fe[3];
308     KMStrainRate(grad_s, strain_rate);
309     NewtonianStress(context, strain_rate, kmstress);
310     KMUnpack(kmstress, stress);
311     ViscousEnergyFlux(context, s.Y, grad_s, stress, Fe);
312 
313     StateConservative F_inviscid[3];
314     FluxInviscid(context, s, F_inviscid);
315 
316     // Total flux
317     CeedScalar Flux[5][3];
318     FluxTotal(F_inviscid, stress, Fe, Flux);
319 
320     for (CeedInt j=0; j<3; j++)
321       for (CeedInt k=0; k<5; k++)
322         Grad_v[j][k][i] = -wdetJ * (dXdx[j][0] * Flux[k][0] +
323                                     dXdx[j][1] * Flux[k][1] +
324                                     dXdx[j][2] * Flux[k][2]);
325 
326     const CeedScalar body_force[5] = {0, s.U.density *g[0], s.U.density *g[1], s.U.density *g[2], 0};
327     for (CeedInt j=0; j<5; j++)
328       v[j][i] = wdetJ * (q_dot[j][i] - body_force[j]);
329 
330     // -- Stabilization method: none (Galerkin), SU, or SUPG
331     CeedScalar Tau_d[3], stab[5][3], U_dot[5] = {0};
332     for (CeedInt j=0; j<5; j++) U_dot[j] = q_dot[j][i];
333     Tau_diagPrim(context, s, dXdx, dt, Tau_d);
334     Stabilization(context, s, Tau_d, grad_s, U_dot, body_force, x_i, stab);
335 
336     for (CeedInt j=0; j<5; j++)
337       for (CeedInt k=0; k<3; k++)
338         Grad_v[k][j][i] += wdetJ*(stab[j][0] * dXdx[k][0] +
339                                   stab[j][1] * dXdx[k][1] +
340                                   stab[j][2] * dXdx[k][2]);
341 
342     for (CeedInt j=0; j<5; j++) jac_data[j][i] = U[j];
343     for (CeedInt j=0; j<6; j++) jac_data[5+j][i] = kmstress[j];
344     for (CeedInt j=0; j<3; j++) jac_data[5+6+j][i] = Tau_d[j];
345 
346   } // End Quadrature Point Loop
347 
348   // Return
349   return 0;
350 }
351 
352 // *****************************************************************************
353 // This QFunction implements the jacobean of the Navier-Stokes equations
354 //   for implicit time stepping method.
355 //
356 // *****************************************************************************
357 CEED_QFUNCTION(IJacobian_Newtonian)(void *ctx, CeedInt Q,
358                                     const CeedScalar *const *in,
359                                     CeedScalar *const *out) {
360   // *INDENT-OFF*
361   // Inputs
362   const CeedScalar (*dq)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0],
363                    (*Grad_dq)[5][CEED_Q_VLA] = (const CeedScalar(*)[5][CEED_Q_VLA])in[1],
364                    (*q_data)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[2],
365                    (*x)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[3],
366                    (*jac_data)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[4];
367   // Outputs
368   CeedScalar (*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0],
369              (*Grad_v)[5][CEED_Q_VLA] = (CeedScalar(*)[5][CEED_Q_VLA])out[1];
370   // *INDENT-ON*
371   // Context
372   NewtonianIdealGasContext context = (NewtonianIdealGasContext)ctx;
373   const CeedScalar *g = context->g;
374 
375   CeedPragmaSIMD
376   // Quadrature Point Loop
377   for (CeedInt i=0; i<Q; i++) {
378     // -- Interp-to-Interp q_data
379     const CeedScalar wdetJ      =   q_data[0][i];
380     // -- Interp-to-Grad q_data
381     // ---- Inverse of change of coordinate matrix: X_i,j
382     // *INDENT-OFF*
383     const CeedScalar dXdx[3][3] = {{q_data[1][i],
384                                     q_data[2][i],
385                                     q_data[3][i]},
386                                    {q_data[4][i],
387                                     q_data[5][i],
388                                     q_data[6][i]},
389                                    {q_data[7][i],
390                                     q_data[8][i],
391                                     q_data[9][i]}
392                                   };
393     // *INDENT-ON*
394 
395     CeedScalar U[5], kmstress[6], Tau_d[3] __attribute((unused));
396     for (int j=0; j<5; j++) U[j] = jac_data[j][i];
397     for (int j=0; j<6; j++) kmstress[j] = jac_data[5+j][i];
398     for (int j=0; j<3; j++) Tau_d[j] = jac_data[5+6+j][i];
399     const CeedScalar x_i[3] = {x[0][i], x[1][i], x[2][i]};
400     State s = StateFromU(context, U, x_i);
401 
402     CeedScalar dU[5], dx0[3] = {0};
403     for (int j=0; j<5; j++) dU[j] = dq[j][i];
404     State ds = StateFromU_fwd(context, s, dU, x_i, dx0);
405 
406     State grad_ds[3];
407     for (int j=0; j<3; j++) {
408       CeedScalar dUj[5];
409       for (int k=0; k<5; k++)
410         dUj[k] = Grad_dq[0][k][i] * dXdx[0][j] +
411                  Grad_dq[1][k][i] * dXdx[1][j] +
412                  Grad_dq[2][k][i] * dXdx[2][j];
413       grad_ds[j] = StateFromU_fwd(context, s, dUj, x_i, dx0);
414     }
415 
416     CeedScalar dstrain_rate[6], dkmstress[6], stress[3][3], dstress[3][3], dFe[3];
417     KMStrainRate(grad_ds, dstrain_rate);
418     NewtonianStress(context, dstrain_rate, dkmstress);
419     KMUnpack(dkmstress, dstress);
420     KMUnpack(kmstress, stress);
421     ViscousEnergyFlux_fwd(context, s.Y, ds.Y, grad_ds, stress, dstress, dFe);
422 
423     StateConservative dF_inviscid[3];
424     FluxInviscid_fwd(context, s, ds, dF_inviscid);
425 
426     // Total flux
427     CeedScalar dFlux[5][3];
428     FluxTotal(dF_inviscid, dstress, dFe, dFlux);
429 
430     for (int j=0; j<3; j++)
431       for (int k=0; k<5; k++)
432         Grad_v[j][k][i] = -wdetJ * (dXdx[j][0] * dFlux[k][0] +
433                                     dXdx[j][1] * dFlux[k][1] +
434                                     dXdx[j][2] * dFlux[k][2]);
435 
436     const CeedScalar dbody_force[5] = {0, ds.U.density *g[0], ds.U.density *g[1], ds.U.density *g[2], 0};
437     for (int j=0; j<5; j++)
438       v[j][i] = wdetJ * (context->ijacobian_time_shift * dU[j] - dbody_force[j]);
439 
440     // -- Stabilization method: none (Galerkin), SU, or SUPG
441     CeedScalar dstab[5][3], U_dot[5] = {0};
442     for (CeedInt j=0; j<5; j++) U_dot[j] = context->ijacobian_time_shift * dU[j];
443     Stabilization(context, s, Tau_d, grad_ds, U_dot, dbody_force, x_i, dstab);
444 
445     for (int j=0; j<5; j++)
446       for (int k=0; k<3; k++)
447         Grad_v[k][j][i] += wdetJ*(dstab[j][0] * dXdx[k][0] +
448                                   dstab[j][1] * dXdx[k][1] +
449                                   dstab[j][2] * dXdx[k][2]);
450 
451   } // End Quadrature Point Loop
452   return 0;
453 }
454 
455 // *****************************************************************************
456 // Compute boundary integral (ie. for strongly set inflows)
457 // *****************************************************************************
458 CEED_QFUNCTION(BoundaryIntegral)(void *ctx, CeedInt Q,
459                                  const CeedScalar *const *in,
460                                  CeedScalar *const *out) {
461 
462   //*INDENT-OFF*
463   const CeedScalar (*q)[CEED_Q_VLA]          = (const CeedScalar(*)[CEED_Q_VLA])in[0],
464                    (*Grad_q)[5][CEED_Q_VLA]  = (const CeedScalar(*)[5][CEED_Q_VLA])in[1],
465                    (*q_data_sur)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[2],
466                    (*x)[CEED_Q_VLA]          = (const CeedScalar(*)[CEED_Q_VLA])in[3];
467 
468   CeedScalar (*v)[CEED_Q_VLA]            = (CeedScalar(*)[CEED_Q_VLA]) out[0],
469              (*jac_data_sur)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA]) out[1];
470 
471   //*INDENT-ON*
472 
473   const NewtonianIdealGasContext context = (NewtonianIdealGasContext) ctx;
474   const bool is_implicit  = context->is_implicit;
475   State (*StateFromQi)(NewtonianIdealGasContext gas,
476                        const CeedScalar qi[5], const CeedScalar x[3]);
477   State (*StateFromQi_fwd)(NewtonianIdealGasContext gas,
478                            State s, const CeedScalar dqi[5],
479                            const CeedScalar x[3], const CeedScalar dx[3]);
480   StateFromQi     = context->is_primitive ? &StateFromY     : &StateFromU;
481   StateFromQi_fwd = context->is_primitive ? &StateFromY_fwd : &StateFromU_fwd;
482 
483 
484   CeedPragmaSIMD
485   for(CeedInt i=0; i<Q; i++) {
486     const CeedScalar x_i[3] = {x[0][i], x[1][i], x[2][i]};
487     const CeedScalar qi[5]  = {q[0][i], q[1][i], q[2][i], q[3][i], q[4][i]};
488     State s = StateFromQi(context, qi, x_i);
489 
490     const CeedScalar wdetJb  = (is_implicit ? -1. : 1.) * q_data_sur[0][i];
491     // ---- Normal vect
492     const CeedScalar norm[3] = {q_data_sur[1][i],
493                                 q_data_sur[2][i],
494                                 q_data_sur[3][i]
495                                };
496 
497     const CeedScalar dXdx[2][3] = {
498       {q_data_sur[4][i], q_data_sur[5][i], q_data_sur[6][i]},
499       {q_data_sur[7][i], q_data_sur[8][i], q_data_sur[9][i]}
500     };
501 
502     State grad_s[3];
503     for (CeedInt j=0; j<3; j++) {
504       CeedScalar dx_i[3] = {0}, dqi[5];
505       for (CeedInt k=0; k<5; k++)
506         dqi[k] = Grad_q[0][k][i] * dXdx[0][j] +
507                  Grad_q[1][k][i] * dXdx[1][j];
508       dx_i[j] = 1.;
509       grad_s[j] = StateFromQi_fwd(context, s, dqi, x_i, dx_i);
510     }
511 
512     CeedScalar strain_rate[6], kmstress[6], stress[3][3], Fe[3];
513     KMStrainRate(grad_s, strain_rate);
514     NewtonianStress(context, strain_rate, kmstress);
515     KMUnpack(kmstress, stress);
516     ViscousEnergyFlux(context, s.Y, grad_s, stress, Fe);
517 
518     StateConservative F_inviscid[3];
519     FluxInviscid(context, s, F_inviscid);
520 
521     CeedScalar Flux[5] = {0.};
522     for (int j=0; j<3; j++) {
523       Flux[0] += F_inviscid[j].density * norm[j];
524       for (int k=0; k<3; k++)
525         Flux[k+1] += (F_inviscid[j].momentum[k] - stress[k][j]) * norm[j];
526       Flux[4] += (F_inviscid[j].E_total + Fe[j]) * norm[j];
527     }
528 
529     // -- Density
530     v[0][i] = -wdetJb * Flux[0];
531 
532     // -- Momentum
533     for (CeedInt j=0; j<3; j++)
534       v[j+1][i] = -wdetJb * Flux[j+1];
535 
536     // -- Total Energy Density
537     v[4][i] = -wdetJb * Flux[4];
538 
539     if (context->is_primitive) {
540       jac_data_sur[0][i] = s.Y.pressure;
541       for (int j=0; j<3; j++) jac_data_sur[j+1][i] = s.Y.velocity[j];
542       jac_data_sur[4][i] = s.Y.temperature;
543     } else {
544       jac_data_sur[0][i] = s.U.density;
545       for (int j=0; j<3; j++) jac_data_sur[j+1][i] = s.U.momentum[j];
546       jac_data_sur[4][i] = s.U.E_total;
547     }
548     for (int j=0; j<6; j++) jac_data_sur[5+j][i] = kmstress[j];
549   }
550   return 0;
551 }
552 
553 // *****************************************************************************
554 // Jacobian for "set nothing" boundary integral
555 // *****************************************************************************
556 CEED_QFUNCTION(BoundaryIntegral_Jacobian)(void *ctx, CeedInt Q,
557     const CeedScalar *const *in,
558     CeedScalar *const *out) {
559   // *INDENT-OFF*
560   // Inputs
561   const CeedScalar (*dq)[CEED_Q_VLA]           = (const CeedScalar(*)[CEED_Q_VLA])in[0],
562                    (*Grad_dq)[5][CEED_Q_VLA]   = (const CeedScalar(*)[5][CEED_Q_VLA])in[1],
563                    (*q_data_sur)[CEED_Q_VLA]   = (const CeedScalar(*)[CEED_Q_VLA])in[2],
564                    (*x)[CEED_Q_VLA]            = (const CeedScalar(*)[CEED_Q_VLA])in[3],
565                    (*jac_data_sur)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[4];
566   // Outputs
567   CeedScalar (*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0];
568   // *INDENT-ON*
569 
570   const NewtonianIdealGasContext context = (NewtonianIdealGasContext)ctx;
571   const bool implicit     = context->is_implicit;
572   State (*StateFromQi)(NewtonianIdealGasContext gas,
573                        const CeedScalar qi[5], const CeedScalar x[3]);
574   State (*StateFromQi_fwd)(NewtonianIdealGasContext gas,
575                            State s, const CeedScalar dqi[5],
576                            const CeedScalar x[3], const CeedScalar dx[3]);
577   StateFromQi     = context->is_primitive ? &StateFromY     : &StateFromU;
578   StateFromQi_fwd = context->is_primitive ? &StateFromY_fwd : &StateFromU_fwd;
579 
580   CeedPragmaSIMD
581   // Quadrature Point Loop
582   for (CeedInt i=0; i<Q; i++) {
583     const CeedScalar x_i[3]  = {x[0][i], x[1][i], x[2][i]};
584     const CeedScalar wdetJb  = (implicit ? -1. : 1.) * q_data_sur[0][i];
585     const CeedScalar norm[3] = {q_data_sur[1][i],
586                                 q_data_sur[2][i],
587                                 q_data_sur[3][i]
588                                };
589     const CeedScalar dXdx[2][3] = {
590       {q_data_sur[4][i], q_data_sur[5][i], q_data_sur[6][i]},
591       {q_data_sur[7][i], q_data_sur[8][i], q_data_sur[9][i]}
592     };
593 
594     CeedScalar qi[5], kmstress[6], dqi[5], dx_i[3] = {0.};
595     for (int j=0; j<5; j++) qi[j]    = jac_data_sur[j][i];
596     for (int j=0; j<6; j++) kmstress[j] = jac_data_sur[5+j][i];
597     for (int j=0; j<5; j++) dqi[j]   = dq[j][i];
598 
599     State s  = StateFromQi(context, qi, x_i);
600     State ds = StateFromQi_fwd(context, s, dqi, x_i, dx_i);
601 
602     State grad_ds[3];
603     for (CeedInt j=0; j<3; j++) {
604       CeedScalar dx_i[3] = {0}, dqi_j[5];
605       for (CeedInt k=0; k<5; k++)
606         dqi_j[k] = Grad_dq[0][k][i] * dXdx[0][j] +
607                    Grad_dq[1][k][i] * dXdx[1][j];
608       dx_i[j] = 1.;
609       grad_ds[j] = StateFromQi_fwd(context, s, dqi_j, x_i, dx_i);
610     }
611 
612     CeedScalar dstrain_rate[6], dkmstress[6], stress[3][3], dstress[3][3], dFe[3];
613     KMStrainRate(grad_ds, dstrain_rate);
614     NewtonianStress(context, dstrain_rate, dkmstress);
615     KMUnpack(dkmstress, dstress);
616     KMUnpack(kmstress, stress);
617     ViscousEnergyFlux_fwd(context, s.Y, ds.Y, grad_ds, stress, dstress, dFe);
618 
619     StateConservative dF_inviscid[3];
620     FluxInviscid_fwd(context, s, ds, dF_inviscid);
621 
622     CeedScalar dFlux[5] = {0.};
623     for (int j=0; j<3; j++) {
624       dFlux[0] += dF_inviscid[j].density * norm[j];
625       for (int k=0; k<3; k++)
626         dFlux[k+1] += (dF_inviscid[j].momentum[k] - dstress[k][j]) * norm[j];
627       dFlux[4] += (dF_inviscid[j].E_total + dFe[j]) * norm[j];
628     }
629 
630     for (int j=0; j<5; j++)
631       v[j][i] = -wdetJb * dFlux[j];
632   } // End Quadrature Point Loop
633   return 0;
634 }
635 
636 // *****************************************************************************
637 // Outflow boundary condition, weakly setting a constant pressure
638 // *****************************************************************************
639 CEED_QFUNCTION(PressureOutflow)(void *ctx, CeedInt Q,
640                                 const CeedScalar *const *in,
641                                 CeedScalar *const *out) {
642   // *INDENT-OFF*
643   // Inputs
644   const CeedScalar (*q)[CEED_Q_VLA]          = (const CeedScalar(*)[CEED_Q_VLA])in[0],
645                    (*Grad_q)[5][CEED_Q_VLA]  = (const CeedScalar(*)[5][CEED_Q_VLA])in[1],
646                    (*q_data_sur)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[2],
647                    (*x)[CEED_Q_VLA]          = (const CeedScalar(*)[CEED_Q_VLA])in[3];
648   // Outputs
649   CeedScalar (*v)[CEED_Q_VLA]            = (CeedScalar(*)[CEED_Q_VLA])out[0],
650              (*jac_data_sur)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[1];
651   // *INDENT-ON*
652 
653   const NewtonianIdealGasContext context = (NewtonianIdealGasContext)ctx;
654   const bool       implicit = context->is_implicit;
655   const CeedScalar P0       = context->P0;
656 
657   State (*StateFromQi)(NewtonianIdealGasContext gas,
658                        const CeedScalar qi[5], const CeedScalar x[3]);
659   State (*StateFromQi_fwd)(NewtonianIdealGasContext gas,
660                            State s, const CeedScalar dqi[5],
661                            const CeedScalar x[3], const CeedScalar dx[3]);
662   StateFromQi     = context->is_primitive ? &StateFromY     : &StateFromU;
663   StateFromQi_fwd = context->is_primitive ? &StateFromY_fwd : &StateFromU_fwd;
664 
665   CeedPragmaSIMD
666   // Quadrature Point Loop
667   for (CeedInt i=0; i<Q; i++) {
668     // Setup
669     // -- Interp in
670     const CeedScalar x_i[3] = {x[0][i], x[1][i], x[2][i]};
671     const CeedScalar qi[5]  = {q[0][i], q[1][i], q[2][i], q[3][i], q[4][i]};
672     State s = StateFromQi(context, qi, x_i);
673     s.Y.pressure = P0;
674 
675     // -- Interp-to-Interp q_data
676     // For explicit mode, the surface integral is on the RHS of ODE q_dot = f(q).
677     // For implicit mode, it gets pulled to the LHS of implicit ODE/DAE g(q_dot, q).
678     // We can effect this by swapping the sign on this weight
679     const CeedScalar wdetJb  = (implicit ? -1. : 1.) * q_data_sur[0][i];
680 
681     // ---- Normal vect
682     const CeedScalar norm[3] = {q_data_sur[1][i],
683                                 q_data_sur[2][i],
684                                 q_data_sur[3][i]
685                                };
686 
687     const CeedScalar dXdx[2][3] = {
688       {q_data_sur[4][i], q_data_sur[5][i], q_data_sur[6][i]},
689       {q_data_sur[7][i], q_data_sur[8][i], q_data_sur[9][i]}
690     };
691 
692     State grad_s[3];
693     for (CeedInt j=0; j<3; j++) {
694       CeedScalar dx_i[3] = {0}, dqi[5];
695       for (CeedInt k=0; k<5; k++)
696         dqi[k] = Grad_q[0][k][i] * dXdx[0][j] +
697                  Grad_q[1][k][i] * dXdx[1][j];
698       dx_i[j] = 1.;
699       grad_s[j] = StateFromQi_fwd(context, s, dqi, x_i, dx_i);
700     }
701 
702     CeedScalar strain_rate[6], kmstress[6], stress[3][3], Fe[3];
703     KMStrainRate(grad_s, strain_rate);
704     NewtonianStress(context, strain_rate, kmstress);
705     KMUnpack(kmstress, stress);
706     ViscousEnergyFlux(context, s.Y, grad_s, stress, Fe);
707 
708     StateConservative F_inviscid[3];
709     FluxInviscid(context, s, F_inviscid);
710 
711     CeedScalar Flux[5] = {0.};
712     for (int j=0; j<3; j++) {
713       Flux[0] += F_inviscid[j].density * norm[j];
714       for (int k=0; k<3; k++)
715         Flux[k+1] += (F_inviscid[j].momentum[k] - stress[k][j]) * norm[j];
716       Flux[4] += (F_inviscid[j].E_total + Fe[j])*norm[j];
717     }
718 
719     // -- Density
720     v[0][i] = -wdetJb * Flux[0];
721 
722     // -- Momentum
723     for (CeedInt j=0; j<3; j++)
724       v[j+1][i] = -wdetJb * Flux[j+1];
725 
726     // -- Total Energy Density
727     v[4][i] = -wdetJb * Flux[4];
728 
729     // Save values for Jacobian
730     if (context->is_primitive) {
731       jac_data_sur[0][i] = s.Y.pressure;
732       for (int j=0; j<3; j++) jac_data_sur[j+1][i] = s.Y.velocity[j];
733       jac_data_sur[4][i] = s.Y.temperature;
734     } else {
735       jac_data_sur[0][i] = s.U.density;
736       for (int j=0; j<3; j++) jac_data_sur[j+1][i] = s.U.momentum[j];
737       jac_data_sur[4][i] = s.U.E_total;
738     }
739     for (int j=0; j<6; j++) jac_data_sur[5+j][i] = kmstress[j];
740   } // End Quadrature Point Loop
741   return 0;
742 }
743 
744 // *****************************************************************************
745 // Jacobian for weak-pressure outflow boundary condition
746 // *****************************************************************************
747 CEED_QFUNCTION(PressureOutflow_Jacobian)(void *ctx, CeedInt Q,
748     const CeedScalar *const *in, CeedScalar *const *out) {
749   // *INDENT-OFF*
750   // Inputs
751   const CeedScalar (*dq)[CEED_Q_VLA]           = (const CeedScalar(*)[CEED_Q_VLA])in[0],
752                    (*Grad_dq)[5][CEED_Q_VLA]   = (const CeedScalar(*)[5][CEED_Q_VLA])in[1],
753                    (*q_data_sur)[CEED_Q_VLA]   = (const CeedScalar(*)[CEED_Q_VLA])in[2],
754                    (*x)[CEED_Q_VLA]            = (const CeedScalar(*)[CEED_Q_VLA])in[3],
755                    (*jac_data_sur)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[4];
756   // Outputs
757   CeedScalar (*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0];
758   // *INDENT-ON*
759 
760   const NewtonianIdealGasContext context = (NewtonianIdealGasContext)ctx;
761   const bool implicit     = context->is_implicit;
762 
763   State (*StateFromQi)(NewtonianIdealGasContext gas,
764                        const CeedScalar qi[5], const CeedScalar x[3]);
765   State (*StateFromQi_fwd)(NewtonianIdealGasContext gas,
766                            State s, const CeedScalar dQi[5],
767                            const CeedScalar x[3], const CeedScalar dx[3]);
768   StateFromQi     = context->is_primitive ? &StateFromY     : &StateFromU;
769   StateFromQi_fwd = context->is_primitive ? &StateFromY_fwd : &StateFromU_fwd;
770 
771   CeedPragmaSIMD
772   // Quadrature Point Loop
773   for (CeedInt i=0; i<Q; i++) {
774     const CeedScalar x_i[3]  = {x[0][i], x[1][i], x[2][i]};
775     const CeedScalar wdetJb  = (implicit ? -1. : 1.) * q_data_sur[0][i];
776     const CeedScalar norm[3] = {q_data_sur[1][i],
777                                 q_data_sur[2][i],
778                                 q_data_sur[3][i]
779                                };
780     const CeedScalar dXdx[2][3] = {
781       {q_data_sur[4][i], q_data_sur[5][i], q_data_sur[6][i]},
782       {q_data_sur[7][i], q_data_sur[8][i], q_data_sur[9][i]}
783     };
784 
785     CeedScalar qi[5], kmstress[6], dqi[5], dx_i[3] = {0.};
786     for (int j=0; j<5; j++) qi[j]    = jac_data_sur[j][i];
787     for (int j=0; j<6; j++) kmstress[j] = jac_data_sur[5+j][i];
788     for (int j=0; j<5; j++) dqi[j]   = dq[j][i];
789 
790     State s  = StateFromQi(context, qi, x_i);
791     State ds = StateFromQi_fwd(context, s, dqi, x_i, dx_i);
792     s.Y.pressure  = context->P0;
793     ds.Y.pressure = 0.;
794 
795     State grad_ds[3];
796     for (CeedInt j=0; j<3; j++) {
797       CeedScalar dx_i[3] = {0}, dqi_j[5];
798       for (CeedInt k=0; k<5; k++)
799         dqi_j[k] = Grad_dq[0][k][i] * dXdx[0][j] +
800                    Grad_dq[1][k][i] * dXdx[1][j];
801       dx_i[j] = 1.;
802       grad_ds[j] = StateFromQi_fwd(context, s, dqi_j, x_i, dx_i);
803     }
804 
805     CeedScalar dstrain_rate[6], dkmstress[6], stress[3][3], dstress[3][3], dFe[3];
806     KMStrainRate(grad_ds, dstrain_rate);
807     NewtonianStress(context, dstrain_rate, dkmstress);
808     KMUnpack(dkmstress, dstress);
809     KMUnpack(kmstress, stress);
810     ViscousEnergyFlux_fwd(context, s.Y, ds.Y, grad_ds, stress, dstress, dFe);
811 
812     StateConservative dF_inviscid[3];
813     FluxInviscid_fwd(context, s, ds, dF_inviscid);
814 
815     CeedScalar dFlux[5] = {0.};
816     for (int j=0; j<3; j++) {
817       dFlux[0] += dF_inviscid[j].density * norm[j];
818       for (int k=0; k<3; k++)
819         dFlux[k+1] += (dF_inviscid[j].momentum[k] - dstress[k][j]) * norm[j];
820       dFlux[4] += (dF_inviscid[j].E_total + dFe[j]) * norm[j];
821     }
822 
823     for (int j=0; j<5; j++)
824       v[j][i] = -wdetJb * dFlux[j];
825   } // End Quadrature Point Loop
826   return 0;
827 }
828 
829 // *****************************************************************************
830 // This QFunction implements the Navier-Stokes equations (mentioned above) in
831 //   primitive variables and with implicit time stepping method
832 //
833 // *****************************************************************************
834 CEED_QFUNCTION(IFunction_Newtonian_Prim)(void *ctx, CeedInt Q,
835     const CeedScalar *const *in, CeedScalar *const *out) {
836   // *INDENT-OFF*
837   // Inputs
838   const CeedScalar (*q)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0],
839                    (*Grad_q)[5][CEED_Q_VLA] = (const CeedScalar(*)[5][CEED_Q_VLA])in[1],
840                    (*q_dot)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[2],
841                    (*q_data)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[3],
842                    (*x)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[4];
843   // Outputs
844   CeedScalar (*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0],
845              (*Grad_v)[5][CEED_Q_VLA] = (CeedScalar(*)[5][CEED_Q_VLA])out[1],
846              (*jac_data)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[2];
847   // *INDENT-ON*
848   // Context
849   NewtonianIdealGasContext context = (NewtonianIdealGasContext)ctx;
850   const CeedScalar *g     = context->g;
851   const CeedScalar dt     = context->dt;
852 
853   CeedPragmaSIMD
854   // Quadrature Point Loop
855   for (CeedInt i=0; i<Q; i++) {
856     CeedScalar Y[5];
857     for (CeedInt j=0; j<5; j++) Y[j] = q[j][i];
858     const CeedScalar x_i[3] = {x[0][i], x[1][i], x[2][i]};
859     State s = StateFromY(context, Y, x_i);
860 
861     // -- Interp-to-Interp q_data
862     const CeedScalar wdetJ      =   q_data[0][i];
863     // -- Interp-to-Grad q_data
864     // ---- Inverse of change of coordinate matrix: X_i,j
865     // *INDENT-OFF*
866     const CeedScalar dXdx[3][3] = {{q_data[1][i],
867                                     q_data[2][i],
868                                     q_data[3][i]},
869                                    {q_data[4][i],
870                                     q_data[5][i],
871                                     q_data[6][i]},
872                                    {q_data[7][i],
873                                     q_data[8][i],
874                                     q_data[9][i]}
875                                   };
876     // *INDENT-ON*
877     State grad_s[3];
878     for (CeedInt j=0; j<3; j++) {
879       CeedScalar dx_i[3] = {0}, dY[5];
880       for (CeedInt k=0; k<5; k++)
881         dY[k] = Grad_q[0][k][i] * dXdx[0][j] +
882                 Grad_q[1][k][i] * dXdx[1][j] +
883                 Grad_q[2][k][i] * dXdx[2][j];
884       dx_i[j] = 1.;
885       grad_s[j] = StateFromY_fwd(context, s, dY, x_i, dx_i);
886     }
887 
888     CeedScalar strain_rate[6], kmstress[6], stress[3][3], Fe[3];
889     KMStrainRate(grad_s, strain_rate);
890     NewtonianStress(context, strain_rate, kmstress);
891     KMUnpack(kmstress, stress);
892     ViscousEnergyFlux(context, s.Y, grad_s, stress, Fe);
893 
894     StateConservative F_inviscid[3];
895     FluxInviscid(context, s, F_inviscid);
896 
897     // Total flux
898     CeedScalar Flux[5][3];
899     FluxTotal(F_inviscid, stress, Fe, Flux);
900 
901     for (CeedInt j=0; j<3; j++)
902       for (CeedInt k=0; k<5; k++)
903         Grad_v[j][k][i] = -wdetJ * (dXdx[j][0] * Flux[k][0] +
904                                     dXdx[j][1] * Flux[k][1] +
905                                     dXdx[j][2] * Flux[k][2]);
906 
907     const CeedScalar body_force[5] = {0, s.U.density *g[0], s.U.density *g[1], s.U.density *g[2], 0};
908 
909     CeedScalar Y_dot[5], dx0[3] = {0};
910     for (int j=0; j<5; j++) Y_dot[j] = q_dot[j][i];
911     State s_dot = StateFromY_fwd(context, s, Y_dot, x_i, dx0);
912 
913     CeedScalar U_dot[5] = {0.};
914     UnpackState_U(s_dot.U, U_dot);
915 
916     for (CeedInt j=0; j<5; j++)
917       v[j][i] = wdetJ * (U_dot[j] - body_force[j]);
918 
919     // -- Stabilization method: none (Galerkin), SU, or SUPG
920     CeedScalar Tau_d[3], stab[5][3];
921     Tau_diagPrim(context, s, dXdx, dt, Tau_d);
922     Stabilization(context, s, Tau_d, grad_s, U_dot, body_force, x_i, stab);
923 
924     for (CeedInt j=0; j<5; j++)
925       for (CeedInt k=0; k<3; k++)
926         Grad_v[k][j][i] += wdetJ*(stab[j][0] * dXdx[k][0] +
927                                   stab[j][1] * dXdx[k][1] +
928                                   stab[j][2] * dXdx[k][2]);
929 
930     for (CeedInt j=0; j<5; j++) jac_data[j][i] = Y[j];
931     for (CeedInt j=0; j<6; j++) jac_data[5+j][i] = kmstress[j];
932     for (CeedInt j=0; j<3; j++) jac_data[5+6+j][i] = Tau_d[j];
933 
934   } // End Quadrature Point Loop
935 
936   // Return
937   return 0;
938 }
939 
940 // *****************************************************************************
941 // This QFunction implements the jacobean of the Navier-Stokes equations
942 //   in primitive variables for implicit time stepping method.
943 //
944 // *****************************************************************************
945 CEED_QFUNCTION(IJacobian_Newtonian_Prim)(void *ctx, CeedInt Q,
946     const CeedScalar *const *in, CeedScalar *const *out) {
947   // *INDENT-OFF*
948   // Inputs
949   const CeedScalar (*dq)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0],
950                    (*Grad_dq)[5][CEED_Q_VLA] = (const CeedScalar(*)[5][CEED_Q_VLA])in[1],
951                    (*q_data)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[2],
952                    (*x)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[3],
953                    (*jac_data)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[4];
954   // Outputs
955   CeedScalar (*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0],
956              (*Grad_v)[5][CEED_Q_VLA] = (CeedScalar(*)[5][CEED_Q_VLA])out[1];
957   // *INDENT-ON*
958   // Context
959   NewtonianIdealGasContext context = (NewtonianIdealGasContext)ctx;
960   const CeedScalar *g = context->g;
961 
962   CeedPragmaSIMD
963   // Quadrature Point Loop
964   for (CeedInt i=0; i<Q; i++) {
965     // -- Interp-to-Interp q_data
966     const CeedScalar wdetJ      =   q_data[0][i];
967     // -- Interp-to-Grad q_data
968     // ---- Inverse of change of coordinate matrix: X_i,j
969     // *INDENT-OFF*
970     const CeedScalar dXdx[3][3] = {{q_data[1][i],
971                                     q_data[2][i],
972                                     q_data[3][i]},
973                                    {q_data[4][i],
974                                     q_data[5][i],
975                                     q_data[6][i]},
976                                    {q_data[7][i],
977                                     q_data[8][i],
978                                     q_data[9][i]}
979                                   };
980     // *INDENT-ON*
981 
982     CeedScalar Y[5], kmstress[6], Tau_d[3] __attribute((unused));
983     for (int j=0; j<5; j++) Y[j] = jac_data[j][i];
984     for (int j=0; j<6; j++) kmstress[j] = jac_data[5+j][i];
985     for (int j=0; j<3; j++) Tau_d[j] = jac_data[5+6+j][i];
986     const CeedScalar x_i[3] = {x[0][i], x[1][i], x[2][i]};
987     State s = StateFromY(context, Y, x_i);
988 
989     CeedScalar dY[5], dx0[3] = {0};
990     for (int j=0; j<5; j++) dY[j] = dq[j][i];
991     State ds = StateFromY_fwd(context, s, dY, x_i, dx0);
992 
993     State grad_ds[3];
994     for (int j=0; j<3; j++) {
995       CeedScalar dYj[5];
996       for (int k=0; k<5; k++)
997         dYj[k] = Grad_dq[0][k][i] * dXdx[0][j] +
998                  Grad_dq[1][k][i] * dXdx[1][j] +
999                  Grad_dq[2][k][i] * dXdx[2][j];
1000       grad_ds[j] = StateFromY_fwd(context, s, dYj, x_i, dx0);
1001     }
1002 
1003     CeedScalar dstrain_rate[6], dkmstress[6], stress[3][3], dstress[3][3], dFe[3];
1004     KMStrainRate(grad_ds, dstrain_rate);
1005     NewtonianStress(context, dstrain_rate, dkmstress);
1006     KMUnpack(dkmstress, dstress);
1007     KMUnpack(kmstress, stress);
1008     ViscousEnergyFlux_fwd(context, s.Y, ds.Y, grad_ds, stress, dstress, dFe);
1009 
1010     StateConservative dF_inviscid[3];
1011     FluxInviscid_fwd(context, s, ds, dF_inviscid);
1012 
1013     // Total flux
1014     CeedScalar dFlux[5][3];
1015     FluxTotal(dF_inviscid, dstress, dFe, dFlux);
1016 
1017     for (int j=0; j<3; j++)
1018       for (int k=0; k<5; k++)
1019         Grad_v[j][k][i] = -wdetJ * (dXdx[j][0] * dFlux[k][0] +
1020                                     dXdx[j][1] * dFlux[k][1] +
1021                                     dXdx[j][2] * dFlux[k][2]);
1022 
1023     const CeedScalar dbody_force[5] = {0, ds.U.density *g[0], ds.U.density *g[1], ds.U.density *g[2], 0};
1024     CeedScalar dU[5] = {0.};
1025     UnpackState_U(ds.U, dU);
1026 
1027     for (int j=0; j<5; j++)
1028       v[j][i] = wdetJ * (context->ijacobian_time_shift * dU[j] - dbody_force[j]);
1029 
1030     // -- Stabilization method: none (Galerkin), SU, or SUPG
1031     CeedScalar dstab[5][3], U_dot[5] = {0};
1032     for (CeedInt j=0; j<5; j++) U_dot[j] = context->ijacobian_time_shift * dU[j];
1033     Stabilization(context, s, Tau_d, grad_ds, U_dot, dbody_force, x_i, dstab);
1034 
1035     for (int j=0; j<5; j++)
1036       for (int k=0; k<3; k++)
1037         Grad_v[k][j][i] += wdetJ*(dstab[j][0] * dXdx[k][0] +
1038                                   dstab[j][1] * dXdx[k][1] +
1039                                   dstab[j][2] * dXdx[k][2]);
1040 
1041   } // End Quadrature Point Loop
1042   return 0;
1043 }
1044 // *****************************************************************************
1045 
1046 #endif // newtonian_h
1047