xref: /honee/qfunctions/advection.h (revision 9529d6367d692f3aa60507d37085afbb09358976)
1727da7e7SJeremy L Thompson // Copyright (c) 2017-2022, Lawrence Livermore National Security, LLC and other CEED contributors.
2727da7e7SJeremy L Thompson // All Rights Reserved. See the top-level LICENSE and NOTICE files for details.
3a515125bSLeila Ghaffari //
4727da7e7SJeremy L Thompson // SPDX-License-Identifier: BSD-2-Clause
5a515125bSLeila Ghaffari //
6727da7e7SJeremy L Thompson // This file is part of CEED:  http://github.com/ceed
7a515125bSLeila Ghaffari 
8a515125bSLeila Ghaffari /// @file
9a515125bSLeila Ghaffari /// Advection initial condition and operator for Navier-Stokes example using PETSc
10a515125bSLeila Ghaffari 
11a515125bSLeila Ghaffari #ifndef advection_h
12a515125bSLeila Ghaffari #define advection_h
13a515125bSLeila Ghaffari 
14493642f1SJames Wright #include <ceed.h>
15d0cce58aSJeremy L Thompson #include <math.h>
16a515125bSLeila Ghaffari 
17e88b842aSJames Wright #include "advection_types.h"
18ce192147SJames Wright #include "newtonian_state.h"
19ce192147SJames Wright #include "newtonian_types.h"
20e88b842aSJames Wright #include "stabilization_types.h"
211a74fa30SJames Wright #include "utils.h"
221a74fa30SJames Wright 
23a515125bSLeila Ghaffari // *****************************************************************************
24*9529d636SJames Wright // This QFunction sets the initial conditions and the boundary conditions
25*9529d636SJames Wright //   for two test cases: ROTATION and TRANSLATION
26*9529d636SJames Wright //
27*9529d636SJames Wright // -- ROTATION (default)
28*9529d636SJames Wright //      Initial Conditions:
29*9529d636SJames Wright //        Mass Density:
30*9529d636SJames Wright //          Constant mass density of 1.0
31*9529d636SJames Wright //        Momentum Density:
32*9529d636SJames Wright //          Rotational field in x,y
33*9529d636SJames Wright //        Energy Density:
34*9529d636SJames Wright //          Maximum of 1. x0 decreasing linearly to 0. as radial distance
35*9529d636SJames Wright //            increases to (1.-r/rc), then 0. everywhere else
36*9529d636SJames Wright //
37*9529d636SJames Wright //      Boundary Conditions:
38*9529d636SJames Wright //        Mass Density:
39*9529d636SJames Wright //          0.0 flux
40*9529d636SJames Wright //        Momentum Density:
41*9529d636SJames Wright //          0.0
42*9529d636SJames Wright //        Energy Density:
43*9529d636SJames Wright //          0.0 flux
44*9529d636SJames Wright //
45*9529d636SJames Wright // -- TRANSLATION
46*9529d636SJames Wright //      Initial Conditions:
47*9529d636SJames Wright //        Mass Density:
48*9529d636SJames Wright //          Constant mass density of 1.0
49*9529d636SJames Wright //        Momentum Density:
50*9529d636SJames Wright //           Constant rectilinear field in x,y
51*9529d636SJames Wright //        Energy Density:
52*9529d636SJames Wright //          Maximum of 1. x0 decreasing linearly to 0. as radial distance
53*9529d636SJames Wright //            increases to (1.-r/rc), then 0. everywhere else
54*9529d636SJames Wright //
55*9529d636SJames Wright //      Boundary Conditions:
56*9529d636SJames Wright //        Mass Density:
57*9529d636SJames Wright //          0.0 flux
58*9529d636SJames Wright //        Momentum Density:
59*9529d636SJames Wright //          0.0
60*9529d636SJames Wright //        Energy Density:
61*9529d636SJames Wright //          Inflow BCs:
62*9529d636SJames Wright //            E = E_wind
63*9529d636SJames Wright //          Outflow BCs:
64*9529d636SJames Wright //            E = E(boundary)
65*9529d636SJames Wright //          Both In/Outflow BCs for E are applied weakly in the
66*9529d636SJames Wright //            QFunction "Advection2d_Sur"
67*9529d636SJames Wright //
68*9529d636SJames Wright // *****************************************************************************
69*9529d636SJames Wright 
70*9529d636SJames Wright // *****************************************************************************
71*9529d636SJames Wright // This helper function provides the exact, time-dependent solution and IC formulation for 2D advection
72*9529d636SJames Wright // *****************************************************************************
73*9529d636SJames Wright CEED_QFUNCTION_HELPER CeedInt Exact_AdvectionGeneric(CeedInt dim, CeedScalar time, const CeedScalar X[], CeedInt Nf, CeedScalar q[], void *ctx) {
74*9529d636SJames Wright   const SetupContextAdv context = (SetupContextAdv)ctx;
75*9529d636SJames Wright   const CeedScalar      rc      = context->rc;
76*9529d636SJames Wright   const CeedScalar      lx      = context->lx;
77*9529d636SJames Wright   const CeedScalar      ly      = context->ly;
78*9529d636SJames Wright   const CeedScalar      lz      = dim == 2 ? 0. : context->lz;
79*9529d636SJames Wright   const CeedScalar     *wind    = context->wind;
80*9529d636SJames Wright 
81*9529d636SJames Wright   const CeedScalar center[3] = {0.5 * lx, 0.5 * ly, 0.5 * lz};
82*9529d636SJames Wright   const CeedScalar theta     = dim == 2 ? M_PI / 3 : M_PI;
83*9529d636SJames Wright   const CeedScalar x0[3]     = {center[0] + .25 * lx * cos(theta + time), center[1] + .25 * ly * sin(theta + time), 0.5 * lz};
84*9529d636SJames Wright 
85*9529d636SJames Wright   const CeedScalar x = X[0], y = X[1], z = dim == 2 ? 0. : X[2];
86*9529d636SJames Wright 
87*9529d636SJames Wright   CeedScalar r = 0.;
88*9529d636SJames Wright   switch (context->initial_condition_type) {
89*9529d636SJames Wright     case ADVECTIONIC_BUBBLE_SPHERE:
90*9529d636SJames Wright     case ADVECTIONIC_BUBBLE_CYLINDER:
91*9529d636SJames Wright       r = sqrt(Square(x - x0[0]) + Square(y - x0[1]) + Square(z - x0[2]));
92*9529d636SJames Wright       break;
93*9529d636SJames Wright     case ADVECTIONIC_COSINE_HILL:
94*9529d636SJames Wright       r = sqrt(Square(x - center[0]) + Square(y - center[1]));
95*9529d636SJames Wright       break;
96*9529d636SJames Wright     case ADVECTIONIC_SKEW:
97*9529d636SJames Wright       break;
98*9529d636SJames Wright   }
99*9529d636SJames Wright 
100*9529d636SJames Wright   switch (context->wind_type) {
101*9529d636SJames Wright     case WIND_ROTATION:
102*9529d636SJames Wright       q[0] = 1.;
103*9529d636SJames Wright       q[1] = -(y - center[1]);
104*9529d636SJames Wright       q[2] = (x - center[0]);
105*9529d636SJames Wright       q[3] = 0;
106*9529d636SJames Wright       break;
107*9529d636SJames Wright     case WIND_TRANSLATION:
108*9529d636SJames Wright       q[0] = 1.;
109*9529d636SJames Wright       q[1] = wind[0];
110*9529d636SJames Wright       q[2] = wind[1];
111*9529d636SJames Wright       q[3] = dim == 2 ? 0. : wind[2];
112*9529d636SJames Wright       break;
113*9529d636SJames Wright     default:
114*9529d636SJames Wright       return 1;
115*9529d636SJames Wright   }
116*9529d636SJames Wright 
117*9529d636SJames Wright   switch (context->initial_condition_type) {
118*9529d636SJames Wright     case ADVECTIONIC_BUBBLE_SPHERE:
119*9529d636SJames Wright     case ADVECTIONIC_BUBBLE_CYLINDER:
120*9529d636SJames Wright       switch (context->bubble_continuity_type) {
121*9529d636SJames Wright         // original continuous, smooth shape
122*9529d636SJames Wright         case BUBBLE_CONTINUITY_SMOOTH:
123*9529d636SJames Wright           q[4] = r <= rc ? (1. - r / rc) : 0.;
124*9529d636SJames Wright           break;
125*9529d636SJames Wright         // discontinuous, sharp back half shape
126*9529d636SJames Wright         case BUBBLE_CONTINUITY_BACK_SHARP:
127*9529d636SJames Wright           q[4] = ((r <= rc) && (y < center[1])) ? (1. - r / rc) : 0.;
128*9529d636SJames Wright           break;
129*9529d636SJames Wright         // attempt to define a finite thickness that will get resolved under grid refinement
130*9529d636SJames Wright         case BUBBLE_CONTINUITY_THICK:
131*9529d636SJames Wright           q[4] = ((r <= rc) && (y < center[1])) ? (1. - r / rc) * fmin(1.0, (center[1] - y) / 1.25) : 0.;
132*9529d636SJames Wright           break;
133*9529d636SJames Wright         case BUBBLE_CONTINUITY_COSINE:
134*9529d636SJames Wright           q[4] = r <= rc ? .5 + .5 * cos(r * M_PI / rc) : 0;
135*9529d636SJames Wright           break;
136*9529d636SJames Wright       }
137*9529d636SJames Wright       break;
138*9529d636SJames Wright     case ADVECTIONIC_COSINE_HILL: {
139*9529d636SJames Wright       CeedScalar half_width = context->lx / 2;
140*9529d636SJames Wright       q[4]                  = r > half_width ? 0. : cos(2 * M_PI * r / half_width + M_PI) + 1.;
141*9529d636SJames Wright     } break;
142*9529d636SJames Wright     case ADVECTIONIC_SKEW: {
143*9529d636SJames Wright       CeedScalar       skewed_barrier[3]  = {wind[0], wind[1], 0};
144*9529d636SJames Wright       CeedScalar       inflow_to_point[3] = {x - context->lx / 2, y, 0};
145*9529d636SJames Wright       CeedScalar       cross_product[3]   = {0};
146*9529d636SJames Wright       const CeedScalar boundary_threshold = 20 * CEED_EPSILON;
147*9529d636SJames Wright       Cross3(skewed_barrier, inflow_to_point, cross_product);
148*9529d636SJames Wright 
149*9529d636SJames Wright       q[4] = cross_product[2] > boundary_threshold ? 0 : 1;
150*9529d636SJames Wright       if ((x < boundary_threshold && wind[0] < boundary_threshold) ||                // outflow at -x boundary
151*9529d636SJames Wright           (y < boundary_threshold && wind[1] < boundary_threshold) ||                // outflow at -y boundary
152*9529d636SJames Wright           (x > context->lx - boundary_threshold && wind[0] > boundary_threshold) ||  // outflow at +x boundary
153*9529d636SJames Wright           (y > context->ly - boundary_threshold && wind[1] > boundary_threshold)     // outflow at +y boundary
154*9529d636SJames Wright       ) {
155*9529d636SJames Wright         q[4] = 0;
156*9529d636SJames Wright       }
157*9529d636SJames Wright     } break;
158*9529d636SJames Wright   }
159*9529d636SJames Wright   return 0;
160*9529d636SJames Wright }
161*9529d636SJames Wright 
162*9529d636SJames Wright // *****************************************************************************
163a515125bSLeila Ghaffari // This QFunction sets the initial conditions for 3D advection
164a515125bSLeila Ghaffari // *****************************************************************************
1652b916ea7SJeremy L Thompson CEED_QFUNCTION(ICsAdvection)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
166a515125bSLeila Ghaffari   const CeedScalar(*X)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0];
167a515125bSLeila Ghaffari   CeedScalar(*q0)[CEED_Q_VLA]      = (CeedScalar(*)[CEED_Q_VLA])out[0];
168a515125bSLeila Ghaffari 
1693d65b166SJames Wright   CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
170a515125bSLeila Ghaffari     const CeedScalar x[]  = {X[0][i], X[1][i], X[2][i]};
171139613f2SLeila Ghaffari     CeedScalar       q[5] = {0.};
172a515125bSLeila Ghaffari 
1730b3a1fabSJames Wright     Exact_AdvectionGeneric(3, 0., x, 5, q, ctx);
174a515125bSLeila Ghaffari     for (CeedInt j = 0; j < 5; j++) q0[j][i] = q[j];
1750b3a1fabSJames Wright   }
176a515125bSLeila Ghaffari   return 0;
177a515125bSLeila Ghaffari }
178a515125bSLeila Ghaffari 
179a515125bSLeila Ghaffari // *****************************************************************************
180*9529d636SJames Wright // This QFunction sets the initial conditions for 2D advection
181a515125bSLeila Ghaffari // *****************************************************************************
182*9529d636SJames Wright CEED_QFUNCTION(ICsAdvection2d)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
183*9529d636SJames Wright   const CeedScalar(*X)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0];
184*9529d636SJames Wright   CeedScalar(*q0)[CEED_Q_VLA]      = (CeedScalar(*)[CEED_Q_VLA])out[0];
185*9529d636SJames Wright   const SetupContextAdv context    = (SetupContextAdv)ctx;
186*9529d636SJames Wright 
187*9529d636SJames Wright   CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
188*9529d636SJames Wright     const CeedScalar x[]  = {X[0][i], X[1][i]};
189*9529d636SJames Wright     CeedScalar       q[5] = {0.};
190*9529d636SJames Wright 
191*9529d636SJames Wright     Exact_AdvectionGeneric(2, context->time, x, 5, q, ctx);
192*9529d636SJames Wright     for (CeedInt j = 0; j < 5; j++) q0[j][i] = q[j];
193*9529d636SJames Wright   }
194a515125bSLeila Ghaffari   return 0;
195a515125bSLeila Ghaffari }
196a515125bSLeila Ghaffari 
197*9529d636SJames Wright CEED_QFUNCTION_HELPER void QdataUnpack_ND(CeedInt N, CeedInt Q, CeedInt i, const CeedScalar *q_data, CeedScalar *wdetJ, CeedScalar *dXdx) {
198*9529d636SJames Wright   switch (N) {
199*9529d636SJames Wright     case 2:
200*9529d636SJames Wright       QdataUnpack_2D(Q, i, q_data, wdetJ, (CeedScalar(*)[2])dXdx);
201*9529d636SJames Wright       break;
202*9529d636SJames Wright     case 3:
203*9529d636SJames Wright       QdataUnpack_3D(Q, i, q_data, wdetJ, (CeedScalar(*)[3])dXdx);
204*9529d636SJames Wright       break;
205*9529d636SJames Wright   }
206*9529d636SJames Wright }
207*9529d636SJames Wright 
208*9529d636SJames Wright CEED_QFUNCTION_HELPER int QdataBoundaryUnpack_ND(CeedInt N, CeedInt Q, CeedInt i, const CeedScalar *q_data, CeedScalar *wdetJ, CeedScalar *dXdx,
209*9529d636SJames Wright                                                  CeedScalar *normal) {
210*9529d636SJames Wright   switch (N) {
211*9529d636SJames Wright     case 2:
212*9529d636SJames Wright       QdataBoundaryUnpack_2D(Q, i, q_data, wdetJ, normal);
213*9529d636SJames Wright       break;
214*9529d636SJames Wright     case 3:
215*9529d636SJames Wright       QdataBoundaryUnpack_3D(Q, i, q_data, wdetJ, (CeedScalar(*)[3])dXdx, normal);
216*9529d636SJames Wright       break;
217*9529d636SJames Wright   }
218*9529d636SJames Wright   return CEED_ERROR_SUCCESS;
219*9529d636SJames Wright }
220*9529d636SJames Wright 
221*9529d636SJames Wright CEED_QFUNCTION_HELPER void StatePhysicalGradientFromReference_ND(CeedInt N, CeedInt Q, CeedInt i, NewtonianIdealGasContext gas, State s,
222*9529d636SJames Wright                                                                  StateVariable state_var, const CeedScalar *grad_q, const CeedScalar *dXdx,
223*9529d636SJames Wright                                                                  State *grad_s) {
224*9529d636SJames Wright   switch (N) {
225*9529d636SJames Wright     case 2: {
226*9529d636SJames Wright       for (CeedInt k = 0; k < 2; k++) {
227*9529d636SJames Wright         CeedScalar dqi[5];
228*9529d636SJames Wright         for (CeedInt j = 0; j < 5; j++) {
229*9529d636SJames Wright           dqi[j] = grad_q[(Q * 5) * 0 + Q * j + i] * dXdx[0 * N + k] + grad_q[(Q * 5) * 1 + Q * j + i] * dXdx[1 * N + k];
230*9529d636SJames Wright         }
231*9529d636SJames Wright         grad_s[k] = StateFromQ_fwd(gas, s, dqi, state_var);
232*9529d636SJames Wright       }
233*9529d636SJames Wright       CeedScalar U[5] = {0.};
234*9529d636SJames Wright       grad_s[2]       = StateFromU(gas, U);
235*9529d636SJames Wright     } break;
236*9529d636SJames Wright     case 3:
237*9529d636SJames Wright       StatePhysicalGradientFromReference(Q, i, gas, s, state_var, grad_q, (CeedScalar(*)[3])dXdx, grad_s);
238*9529d636SJames Wright       break;
239*9529d636SJames Wright   }
240*9529d636SJames Wright }
241*9529d636SJames Wright 
242*9529d636SJames Wright // *****************************************************************************
243*9529d636SJames Wright // This QFunction implements Advection for implicit time stepping method
244*9529d636SJames Wright // *****************************************************************************
245*9529d636SJames Wright CEED_QFUNCTION_HELPER void IFunction_AdvectionGeneric(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out, CeedInt dim) {
246*9529d636SJames Wright   const CeedScalar(*q)[CEED_Q_VLA]     = (const CeedScalar(*)[CEED_Q_VLA])in[0];
247*9529d636SJames Wright   const CeedScalar(*grad_q)            = in[1];
248*9529d636SJames Wright   const CeedScalar(*q_dot)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[2];
249*9529d636SJames Wright   const CeedScalar(*q_data)            = in[3];
250*9529d636SJames Wright 
251*9529d636SJames Wright   CeedScalar(*v)[CEED_Q_VLA]         = (CeedScalar(*)[CEED_Q_VLA])out[0];
252*9529d636SJames Wright   CeedScalar(*grad_v)[5][CEED_Q_VLA] = (CeedScalar(*)[5][CEED_Q_VLA])out[1];
253*9529d636SJames Wright   CeedScalar *jac_data               = out[2];
254*9529d636SJames Wright 
255*9529d636SJames Wright   AdvectionContext                 context   = (AdvectionContext)ctx;
256*9529d636SJames Wright   const CeedScalar                 CtauS     = context->CtauS;
257*9529d636SJames Wright   const CeedScalar                 zeros[14] = {0.};
258*9529d636SJames Wright   NewtonianIdealGasContext         gas;
259*9529d636SJames Wright   struct NewtonianIdealGasContext_ gas_struct = {0};
260*9529d636SJames Wright   gas                                         = &gas_struct;
261*9529d636SJames Wright 
262*9529d636SJames Wright   CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
263*9529d636SJames Wright     const CeedScalar qi[5] = {q[0][i], q[1][i], q[2][i], q[3][i], q[4][i]};
264*9529d636SJames Wright     const State      s     = StateFromU(gas, qi);
265*9529d636SJames Wright 
266*9529d636SJames Wright     CeedScalar wdetJ, dXdx[9];
267*9529d636SJames Wright     QdataUnpack_ND(dim, Q, i, q_data, &wdetJ, dXdx);
268*9529d636SJames Wright     State grad_s[3];
269*9529d636SJames Wright     StatePhysicalGradientFromReference_ND(dim, Q, i, gas, s, STATEVAR_CONSERVATIVE, grad_q, dXdx, grad_s);
270*9529d636SJames Wright 
271*9529d636SJames Wright     const CeedScalar Grad_E[3] = {grad_s[0].U.E_total, grad_s[1].U.E_total, grad_s[2].U.E_total};
272*9529d636SJames Wright 
273*9529d636SJames Wright     for (CeedInt f = 0; f < 4; f++) {
274*9529d636SJames Wright       for (CeedInt j = 0; j < dim; j++) grad_v[j][f][i] = 0;  // No Change in density or momentum
275*9529d636SJames Wright       v[f][i] = wdetJ * q_dot[f][i];                          // K Mass/transient term
276*9529d636SJames Wright     }
277*9529d636SJames Wright 
278*9529d636SJames Wright     CeedScalar div_u = 0;
279*9529d636SJames Wright     for (CeedInt j = 0; j < dim; j++) {
280*9529d636SJames Wright       for (CeedInt k = 0; k < dim; k++) {
281*9529d636SJames Wright         div_u += grad_s[k].Y.velocity[j];
282*9529d636SJames Wright       }
283*9529d636SJames Wright     }
284*9529d636SJames Wright     CeedScalar strong_conv = s.U.E_total * div_u + DotN(s.Y.velocity, Grad_E, dim);
285*9529d636SJames Wright     CeedScalar strong_res  = q_dot[4][i] + strong_conv;
286*9529d636SJames Wright 
287*9529d636SJames Wright     v[4][i] = wdetJ * q_dot[4][i];  // transient part (ALWAYS)
288*9529d636SJames Wright 
289*9529d636SJames Wright     CeedScalar uX[3] = {0.};
290*9529d636SJames Wright     MatVecNM(dXdx, s.Y.velocity, dim, dim, CEED_NOTRANSPOSE, uX);
291*9529d636SJames Wright 
292*9529d636SJames Wright     if (context->strong_form) {  // Strong Galerkin convection term: v div(E u)
293*9529d636SJames Wright       v[4][i] += wdetJ * strong_conv;
294*9529d636SJames Wright     } else {  // Weak Galerkin convection term: -dv \cdot (E u)
295*9529d636SJames Wright       for (CeedInt j = 0; j < dim; j++) grad_v[j][4][i] = -wdetJ * s.U.E_total * uX[j];
296*9529d636SJames Wright     }
297*9529d636SJames Wright 
298*9529d636SJames Wright     const CeedScalar TauS = CtauS / sqrt(Dot3(uX, uX));
299*9529d636SJames Wright     for (CeedInt j = 0; j < dim; j++) switch (context->stabilization) {
300*9529d636SJames Wright         case STAB_NONE:
301*9529d636SJames Wright           break;
302*9529d636SJames Wright         case STAB_SU:
303*9529d636SJames Wright           grad_v[j][4][i] += wdetJ * TauS * strong_conv * uX[j];
304*9529d636SJames Wright           break;
305*9529d636SJames Wright         case STAB_SUPG:
306*9529d636SJames Wright           grad_v[j][4][i] += wdetJ * TauS * strong_res * uX[j];
307*9529d636SJames Wright           break;
308*9529d636SJames Wright       }
309*9529d636SJames Wright     StoredValuesPack(Q, i, 0, 14, zeros, jac_data);
310*9529d636SJames Wright   }
311*9529d636SJames Wright }
312*9529d636SJames Wright 
3132b916ea7SJeremy L Thompson CEED_QFUNCTION(IFunction_Advection)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
314bd4b5413SJames Wright   IFunction_AdvectionGeneric(ctx, Q, in, out, 3);
315a515125bSLeila Ghaffari   return 0;
316a515125bSLeila Ghaffari }
317a515125bSLeila Ghaffari 
318*9529d636SJames Wright CEED_QFUNCTION(IFunction_Advection2d)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
319*9529d636SJames Wright   IFunction_AdvectionGeneric(ctx, Q, in, out, 2);
320*9529d636SJames Wright   return 0;
321*9529d636SJames Wright }
322*9529d636SJames Wright 
323*9529d636SJames Wright // *****************************************************************************
324*9529d636SJames Wright // This QFunction implements Advection for explicit time stepping method
325*9529d636SJames Wright // *****************************************************************************
326*9529d636SJames Wright CEED_QFUNCTION_HELPER void RHSFunction_AdvectionGeneric(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out, CeedInt dim) {
327*9529d636SJames Wright   const CeedScalar(*q)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0];
328*9529d636SJames Wright   const CeedScalar(*grad_q)        = in[1];
329*9529d636SJames Wright   const CeedScalar(*q_data)        = in[2];
330*9529d636SJames Wright 
331*9529d636SJames Wright   CeedScalar(*v)[CEED_Q_VLA]         = (CeedScalar(*)[CEED_Q_VLA])out[0];
332*9529d636SJames Wright   CeedScalar(*grad_v)[5][CEED_Q_VLA] = (CeedScalar(*)[5][CEED_Q_VLA])out[1];
333*9529d636SJames Wright 
334*9529d636SJames Wright   AdvectionContext                 context = (AdvectionContext)ctx;
335*9529d636SJames Wright   const CeedScalar                 CtauS   = context->CtauS;
336*9529d636SJames Wright   NewtonianIdealGasContext         gas;
337*9529d636SJames Wright   struct NewtonianIdealGasContext_ gas_struct = {0};
338*9529d636SJames Wright   gas                                         = &gas_struct;
339*9529d636SJames Wright 
340*9529d636SJames Wright   CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
341*9529d636SJames Wright     const CeedScalar qi[5] = {q[0][i], q[1][i], q[2][i], q[3][i], q[4][i]};
342*9529d636SJames Wright     const State      s     = StateFromU(gas, qi);
343*9529d636SJames Wright 
344*9529d636SJames Wright     CeedScalar wdetJ, dXdx[9];
345*9529d636SJames Wright     QdataUnpack_ND(dim, Q, i, q_data, &wdetJ, dXdx);
346*9529d636SJames Wright     State grad_s[3];
347*9529d636SJames Wright     StatePhysicalGradientFromReference_ND(dim, Q, i, gas, s, STATEVAR_CONSERVATIVE, grad_q, dXdx, grad_s);
348*9529d636SJames Wright 
349*9529d636SJames Wright     const CeedScalar Grad_E[3] = {grad_s[0].U.E_total, grad_s[1].U.E_total, grad_s[2].U.E_total};
350*9529d636SJames Wright 
351*9529d636SJames Wright     for (CeedInt f = 0; f < 4; f++) {
352*9529d636SJames Wright       for (CeedInt j = 0; j < dim; j++) grad_v[j][f][i] = 0;  // No Change in density or momentum
353*9529d636SJames Wright       v[f][i] = 0.;
354*9529d636SJames Wright     }
355*9529d636SJames Wright 
356*9529d636SJames Wright     CeedScalar div_u = 0;
357*9529d636SJames Wright     for (CeedInt j = 0; j < dim; j++) {
358*9529d636SJames Wright       for (CeedInt k = 0; k < dim; k++) {
359*9529d636SJames Wright         div_u += grad_s[k].Y.velocity[j];
360*9529d636SJames Wright       }
361*9529d636SJames Wright     }
362*9529d636SJames Wright     CeedScalar strong_conv = s.U.E_total * div_u + DotN(s.Y.velocity, Grad_E, dim);
363*9529d636SJames Wright 
364*9529d636SJames Wright     CeedScalar uX[3] = {0.};
365*9529d636SJames Wright     MatVecNM(dXdx, s.Y.velocity, dim, dim, CEED_NOTRANSPOSE, uX);
366*9529d636SJames Wright 
367*9529d636SJames Wright     if (context->strong_form) {  // Strong Galerkin convection term: v div(E u)
368*9529d636SJames Wright       v[4][i] = -wdetJ * strong_conv;
369*9529d636SJames Wright       for (CeedInt j = 0; j < dim; j++) grad_v[j][4][i] = 0;
370*9529d636SJames Wright     } else {  // Weak Galerkin convection term: -dv \cdot (E u)
371*9529d636SJames Wright       for (CeedInt j = 0; j < dim; j++) grad_v[j][4][i] = wdetJ * s.U.E_total * uX[j];
372*9529d636SJames Wright       v[4][i] = 0.;
373*9529d636SJames Wright     }
374*9529d636SJames Wright 
375*9529d636SJames Wright     const CeedScalar TauS = CtauS / sqrt(Dot3(uX, uX));
376*9529d636SJames Wright     for (CeedInt j = 0; j < dim; j++) switch (context->stabilization) {
377*9529d636SJames Wright         case STAB_NONE:
378*9529d636SJames Wright           break;
379*9529d636SJames Wright         case STAB_SU:
380*9529d636SJames Wright         case STAB_SUPG:
381*9529d636SJames Wright           grad_v[j][4][i] += wdetJ * TauS * strong_conv * uX[j];
382*9529d636SJames Wright           break;
383*9529d636SJames Wright       }
384*9529d636SJames Wright   }
385*9529d636SJames Wright }
386*9529d636SJames Wright 
387*9529d636SJames Wright CEED_QFUNCTION(RHS_Advection)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
388*9529d636SJames Wright   RHSFunction_AdvectionGeneric(ctx, Q, in, out, 3);
389*9529d636SJames Wright   return 0;
390*9529d636SJames Wright }
391*9529d636SJames Wright 
392*9529d636SJames Wright CEED_QFUNCTION(RHS_Advection2d)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
393*9529d636SJames Wright   RHSFunction_AdvectionGeneric(ctx, Q, in, out, 2);
394*9529d636SJames Wright   return 0;
395*9529d636SJames Wright }
396*9529d636SJames Wright 
397*9529d636SJames Wright // *****************************************************************************
398*9529d636SJames Wright // This QFunction implements consistent outflow and inflow BCs
399*9529d636SJames Wright //      for advection
400*9529d636SJames Wright //
401*9529d636SJames Wright //  Inflow and outflow faces are determined based on sign(dot(wind, normal)):
402*9529d636SJames Wright //    sign(dot(wind, normal)) > 0 : outflow BCs
403*9529d636SJames Wright //    sign(dot(wind, normal)) < 0 : inflow BCs
404*9529d636SJames Wright //
405*9529d636SJames Wright //  Outflow BCs:
406*9529d636SJames Wright //    The validity of the weak form of the governing equations is extended to the outflow and the current values of E are applied.
407*9529d636SJames Wright //
408*9529d636SJames Wright //  Inflow BCs:
409*9529d636SJames Wright //    A prescribed Total Energy (E_wind) is applied weakly.
410*9529d636SJames Wright // *****************************************************************************
411*9529d636SJames Wright CEED_QFUNCTION(Advection_InOutFlowGeneric)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out, CeedInt dim) {
412*9529d636SJames Wright   const CeedScalar(*q)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0];
413*9529d636SJames Wright   const CeedScalar(*q_data_sur)    = in[2];
414*9529d636SJames Wright 
415*9529d636SJames Wright   CeedScalar(*v)[CEED_Q_VLA]   = (CeedScalar(*)[CEED_Q_VLA])out[0];
416*9529d636SJames Wright   AdvectionContext context     = (AdvectionContext)ctx;
417*9529d636SJames Wright   const CeedScalar E_wind      = context->E_wind;
418*9529d636SJames Wright   const CeedScalar strong_form = context->strong_form;
419*9529d636SJames Wright   const bool       is_implicit = context->implicit;
420*9529d636SJames Wright 
421*9529d636SJames Wright   CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
422*9529d636SJames Wright     const CeedScalar rho  = q[0][i];
423*9529d636SJames Wright     const CeedScalar u[3] = {q[1][i] / rho, q[2][i] / rho, q[3][i] / rho};
424*9529d636SJames Wright     const CeedScalar E    = q[4][i];
425*9529d636SJames Wright 
426*9529d636SJames Wright     CeedScalar wdetJb, norm[3];
427*9529d636SJames Wright     QdataBoundaryUnpack_ND(dim, Q, i, q_data_sur, &wdetJb, NULL, norm);
428*9529d636SJames Wright     wdetJb *= is_implicit ? -1. : 1.;
429*9529d636SJames Wright 
430*9529d636SJames Wright     const CeedScalar u_normal = DotN(norm, u, dim);
431*9529d636SJames Wright 
432*9529d636SJames Wright     // No Change in density or momentum
433*9529d636SJames Wright     for (CeedInt j = 0; j < 4; j++) {
434*9529d636SJames Wright       v[j][i] = 0;
435*9529d636SJames Wright     }
436*9529d636SJames Wright     // Implementing in/outflow BCs
437*9529d636SJames Wright     if (u_normal > 0) {  // outflow
438*9529d636SJames Wright       v[4][i] = -(1 - strong_form) * wdetJb * E * u_normal;
439*9529d636SJames Wright     } else {  // inflow
440*9529d636SJames Wright       v[4][i] = -(1 - strong_form) * wdetJb * E_wind * u_normal;
441*9529d636SJames Wright     }
442*9529d636SJames Wright   }
443*9529d636SJames Wright   return 0;
444*9529d636SJames Wright }
445*9529d636SJames Wright 
4462b916ea7SJeremy L Thompson CEED_QFUNCTION(Advection_InOutFlow)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
4478dba1efaSJames Wright   Advection_InOutFlowGeneric(ctx, Q, in, out, 3);
448a515125bSLeila Ghaffari   return 0;
449a515125bSLeila Ghaffari }
450a515125bSLeila Ghaffari 
451*9529d636SJames Wright CEED_QFUNCTION(Advection2d_InOutFlow)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
452*9529d636SJames Wright   Advection_InOutFlowGeneric(ctx, Q, in, out, 2);
453*9529d636SJames Wright   return 0;
454*9529d636SJames Wright }
455*9529d636SJames Wright 
456a515125bSLeila Ghaffari #endif  // advection_h
457