1 // Copyright (c) 2017-2026, 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 /// Advection initial condition and operator for Navier-Stokes example using PETSc
10 #include <ceed/types.h>
11 #ifndef CEED_RUNNING_JIT_PASS
12 #include <math.h>
13 #include <stdbool.h>
14 #endif
15
16 #include "advection_types.h"
17 #include "newtonian_state.h"
18 #include "newtonian_types.h"
19 #include "stabilization_types.h"
20 #include "utils.h"
21
22 // *****************************************************************************
23 // This QFunction sets the initial conditions and the boundary conditions
24 // for two test cases: ROTATION and TRANSLATION
25 //
26 // -- ROTATION (default)
27 // Initial Conditions:
28 // Mass Density:
29 // Constant mass density of 1.0
30 // Momentum Density:
31 // Rotational field in x,y
32 // Energy Density:
33 // Maximum of 1. x0 decreasing linearly to 0. as radial distance
34 // increases to (1.-r/rc), then 0. everywhere else
35 //
36 // Boundary Conditions:
37 // Mass Density:
38 // 0.0 flux
39 // Momentum Density:
40 // 0.0
41 // Energy Density:
42 // 0.0 flux
43 //
44 // -- TRANSLATION
45 // Initial Conditions:
46 // Mass Density:
47 // Constant mass density of 1.0
48 // Momentum Density:
49 // Constant rectilinear field in x,y
50 // Energy Density:
51 // Maximum of 1. x0 decreasing linearly to 0. as radial distance
52 // increases to (1.-r/rc), then 0. everywhere else
53 //
54 // Boundary Conditions:
55 // Mass Density:
56 // 0.0 flux
57 // Momentum Density:
58 // 0.0
59 // Energy Density:
60 // Inflow BCs:
61 // E = E_wind
62 // Outflow BCs:
63 // E = E(boundary)
64 // Both In/Outflow BCs for E are applied weakly in the
65 // QFunction "Advection2d_Sur"
66 //
67 // *****************************************************************************
68
69 // *****************************************************************************
70 // This helper function provides the exact, time-dependent solution and IC formulation for 2D advection
71 // *****************************************************************************
Exact_AdvectionGeneric(CeedInt dim,CeedScalar time,const CeedScalar X[],CeedInt Nf,CeedScalar q[],void * ctx)72 CEED_QFUNCTION_HELPER CeedInt Exact_AdvectionGeneric(CeedInt dim, CeedScalar time, const CeedScalar X[], CeedInt Nf, CeedScalar q[], void *ctx) {
73 const SetupContextAdv context = (SetupContextAdv)ctx;
74 const CeedScalar rc = context->rc;
75 const CeedScalar lx = context->lx;
76 const CeedScalar ly = context->ly;
77 const CeedScalar lz = dim == 2 ? 0. : context->lz;
78 const CeedScalar *wind = context->wind;
79
80 const CeedScalar center[3] = {0.5 * lx, 0.5 * ly, 0.5 * lz};
81 const CeedScalar theta = dim == 2 ? M_PI / 3 : M_PI;
82 const CeedScalar x0[3] = {center[0] + .25 * lx * cos(theta + time), center[1] + .25 * ly * sin(theta + time), 0.5 * lz};
83
84 const CeedScalar x = X[0], y = X[1], z = dim == 2 ? 0. : X[2];
85
86 CeedScalar r = 0.;
87 switch (context->initial_condition_type) {
88 case ADVECTIONIC_BUBBLE_SPHERE:
89 case ADVECTIONIC_BUBBLE_CYLINDER:
90 r = sqrt(Square(x - x0[0]) + Square(y - x0[1]) + Square(z - x0[2]));
91 break;
92 case ADVECTIONIC_COSINE_HILL:
93 r = sqrt(Square(x - center[0]) + Square(y - center[1]));
94 break;
95 case ADVECTIONIC_SKEW:
96 break;
97 }
98
99 switch (context->wind_type) {
100 case WIND_ROTATION:
101 q[0] = 1.;
102 q[1] = -(y - center[1]);
103 q[2] = (x - center[0]);
104 q[3] = 0;
105 break;
106 case WIND_TRANSLATION:
107 q[0] = 1.;
108 q[1] = wind[0];
109 q[2] = wind[1];
110 q[3] = dim == 2 ? 0. : wind[2];
111 break;
112 default:
113 return 1;
114 }
115
116 switch (context->initial_condition_type) {
117 case ADVECTIONIC_BUBBLE_SPHERE:
118 case ADVECTIONIC_BUBBLE_CYLINDER:
119 switch (context->bubble_continuity_type) {
120 // original continuous, smooth shape
121 case BUBBLE_CONTINUITY_SMOOTH:
122 q[4] = r <= rc ? (1. - r / rc) : 0.;
123 break;
124 // discontinuous, sharp back half shape
125 case BUBBLE_CONTINUITY_BACK_SHARP:
126 q[4] = ((r <= rc) && (y < center[1])) ? (1. - r / rc) : 0.;
127 break;
128 // attempt to define a finite thickness that will get resolved under grid refinement
129 case BUBBLE_CONTINUITY_THICK:
130 q[4] = ((r <= rc) && (y < center[1])) ? (1. - r / rc) * fmin(1.0, (center[1] - y) / 1.25) : 0.;
131 break;
132 case BUBBLE_CONTINUITY_COSINE:
133 q[4] = r <= rc ? .5 + .5 * cos(r * M_PI / rc) : 0;
134 break;
135 }
136 break;
137 case ADVECTIONIC_COSINE_HILL: {
138 CeedScalar half_width = context->lx / 2;
139 q[4] = r > half_width ? 0. : cos(2 * M_PI * r / half_width + M_PI) + 1.;
140 } break;
141 case ADVECTIONIC_SKEW: {
142 CeedScalar skewed_barrier[3] = {wind[0], wind[1], 0};
143 CeedScalar inflow_to_point[3] = {x - context->lx / 2, y, 0};
144 CeedScalar cross_product[3] = {0};
145 const CeedScalar boundary_threshold = 20 * CEED_EPSILON;
146 Cross3(skewed_barrier, inflow_to_point, cross_product);
147
148 q[4] = cross_product[2] > boundary_threshold ? 0 : 1;
149 if ((x < boundary_threshold && wind[0] < boundary_threshold) || // outflow at -x boundary
150 (y < boundary_threshold && wind[1] < boundary_threshold) || // outflow at -y boundary
151 (x > context->lx - boundary_threshold && wind[0] > boundary_threshold) || // outflow at +x boundary
152 (y > context->ly - boundary_threshold && wind[1] > boundary_threshold) // outflow at +y boundary
153 ) {
154 q[4] = 0;
155 }
156 } break;
157 }
158 return 0;
159 }
160
161 // *****************************************************************************
162 // This QFunction sets the initial conditions for 3D advection
163 // *****************************************************************************
ICsAdvection(void * ctx,CeedInt Q,const CeedScalar * const * in,CeedScalar * const * out)164 CEED_QFUNCTION(ICsAdvection)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
165 const CeedScalar(*X)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0];
166 CeedScalar(*q0)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0];
167
168 CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
169 const CeedScalar x[] = {X[0][i], X[1][i], X[2][i]};
170 CeedScalar q[5] = {0.};
171
172 Exact_AdvectionGeneric(3, 0., x, 5, q, ctx);
173 for (CeedInt j = 0; j < 5; j++) q0[j][i] = q[j];
174 }
175 return 0;
176 }
177
178 // *****************************************************************************
179 // This QFunction sets the initial conditions for 2D advection
180 // *****************************************************************************
ICsAdvection2d(void * ctx,CeedInt Q,const CeedScalar * const * in,CeedScalar * const * out)181 CEED_QFUNCTION(ICsAdvection2d)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
182 const CeedScalar(*X)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0];
183 CeedScalar(*q0)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0];
184 const SetupContextAdv context = (SetupContextAdv)ctx;
185
186 CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
187 const CeedScalar x[] = {X[0][i], X[1][i]};
188 CeedScalar q[5] = {0.};
189
190 Exact_AdvectionGeneric(2, context->time, x, 5, q, ctx);
191 for (CeedInt j = 0; j < 5; j++) q0[j][i] = q[j];
192 }
193 return 0;
194 }
195
QdataUnpack_ND(CeedInt N,CeedInt Q,CeedInt i,const CeedScalar * q_data,CeedScalar * wdetJ,CeedScalar * dXdx)196 CEED_QFUNCTION_HELPER void QdataUnpack_ND(CeedInt N, CeedInt Q, CeedInt i, const CeedScalar *q_data, CeedScalar *wdetJ, CeedScalar *dXdx) {
197 // Cannot directly use QdataUnpack* helper functions due to SYCL online compiler incompatabilities
198 switch (N) {
199 case 2:
200 StoredValuesUnpack(Q, i, 0, 1, q_data, wdetJ);
201 StoredValuesUnpack(Q, i, 1, 4, q_data, dXdx);
202 break;
203 case 3:
204 StoredValuesUnpack(Q, i, 0, 1, q_data, wdetJ);
205 StoredValuesUnpack(Q, i, 1, 9, q_data, dXdx);
206 break;
207 }
208 }
209
QdataBoundaryUnpack_ND(CeedInt N,CeedInt Q,CeedInt i,const CeedScalar * q_data,CeedScalar * wdetJ,CeedScalar * dXdx,CeedScalar * normal)210 CEED_QFUNCTION_HELPER int QdataBoundaryUnpack_ND(CeedInt N, CeedInt Q, CeedInt i, const CeedScalar *q_data, CeedScalar *wdetJ, CeedScalar *dXdx,
211 CeedScalar *normal) {
212 // Cannot directly use QdataBoundaryUnpack* helper functions due to SYCL online compiler incompatabilities
213 switch (N) {
214 case 2:
215 if (wdetJ) StoredValuesUnpack(Q, i, 0, 1, q_data, wdetJ);
216 if (normal) StoredValuesUnpack(Q, i, 1, 2, q_data, normal);
217 break;
218 case 3:
219 if (wdetJ) StoredValuesUnpack(Q, i, 0, 1, q_data, wdetJ);
220 if (normal) StoredValuesUnpack(Q, i, 1, 3, q_data, normal);
221 if (dXdx) StoredValuesUnpack(Q, i, 4, 6, q_data, (CeedScalar *)dXdx);
222 break;
223 }
224 return CEED_ERROR_SUCCESS;
225 }
226
StatePhysicalGradientFromReference_ND(CeedInt N,CeedInt Q,CeedInt i,NewtonianIdealGasContext gas,State s,StateVariable state_var,const CeedScalar * grad_q,const CeedScalar * dXdx,State * grad_s)227 CEED_QFUNCTION_HELPER void StatePhysicalGradientFromReference_ND(CeedInt N, CeedInt Q, CeedInt i, NewtonianIdealGasContext gas, State s,
228 StateVariable state_var, const CeedScalar *grad_q, const CeedScalar *dXdx,
229 State *grad_s) {
230 switch (N) {
231 case 2: {
232 for (CeedInt k = 0; k < 2; k++) {
233 CeedScalar dqi[5];
234 for (CeedInt j = 0; j < 5; j++) {
235 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];
236 }
237 grad_s[k] = StateFromQ_fwd(gas, s, dqi, state_var);
238 }
239 CeedScalar U[5] = {0.};
240 grad_s[2] = StateFromU(gas, U);
241 } break;
242 case 3:
243 // Cannot directly use StatePhysicalGradientFromReference helper functions due to SYCL online compiler incompatabilities
244 for (CeedInt k = 0; k < 3; k++) {
245 CeedScalar dqi[5];
246 for (CeedInt j = 0; j < 5; j++) {
247 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] +
248 grad_q[(Q * 5) * 2 + Q * j + i] * dXdx[2 * N + k];
249 }
250 grad_s[k] = StateFromQ_fwd(gas, s, dqi, state_var);
251 }
252 break;
253 }
254 }
255
256 // @brief Calculate the stabilization constant \tau
Tau(AdvectionContext context,const State s,const CeedScalar * dXdx,CeedInt dim)257 CEED_QFUNCTION_HELPER CeedScalar Tau(AdvectionContext context, const State s, const CeedScalar *dXdx, CeedInt dim) {
258 switch (context->stabilization_tau) {
259 case STAB_TAU_CTAU: {
260 CeedScalar uX[3] = {0.};
261
262 MatVecNM(dXdx, s.Y.velocity, dim, dim, CEED_NOTRANSPOSE, uX);
263 return context->CtauS / sqrt(DotN(uX, uX, dim));
264 } break;
265 case STAB_TAU_ADVDIFF_SHAKIB: {
266 CeedScalar gijd_mat[9] = {0.}, gij_uj[3] = {0.};
267
268 MatMatN(dXdx, dXdx, dim, CEED_TRANSPOSE, CEED_NOTRANSPOSE, gijd_mat);
269 MatVecNM(gijd_mat, s.Y.velocity, dim, dim, CEED_NOTRANSPOSE, gij_uj);
270 return 1 / sqrt(Square(2 * context->Ctau_t / context->dt) + DotN(s.Y.velocity, gij_uj, dim) * context->Ctau_a);
271 } break;
272 default:
273 return 0.;
274 }
275 }
276
277 // *****************************************************************************
278 // This QFunction implements Advection for implicit time stepping method
279 // *****************************************************************************
IFunction_AdvectionGeneric(void * ctx,CeedInt Q,const CeedScalar * const * in,CeedScalar * const * out,CeedInt dim)280 CEED_QFUNCTION_HELPER void IFunction_AdvectionGeneric(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out, CeedInt dim) {
281 const CeedScalar(*q)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0];
282 const CeedScalar(*grad_q) = in[1];
283 const CeedScalar(*q_dot)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[2];
284 const CeedScalar(*q_data) = in[3];
285
286 CeedScalar(*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0];
287 CeedScalar(*grad_v)[5][CEED_Q_VLA] = (CeedScalar(*)[5][CEED_Q_VLA])out[1];
288 CeedScalar *jac_data = out[2];
289
290 AdvectionContext context = (AdvectionContext)ctx;
291 const CeedScalar zeros[14] = {0.};
292 NewtonianIdealGasContext gas;
293 struct NewtonianIdealGasContext_ gas_struct = {0};
294 gas = &gas_struct;
295
296 CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
297 const CeedScalar qi[5] = {q[0][i], q[1][i], q[2][i], q[3][i], q[4][i]};
298 const State s = StateFromU(gas, qi);
299
300 CeedScalar wdetJ, dXdx[9];
301 QdataUnpack_ND(dim, Q, i, q_data, &wdetJ, dXdx);
302 State grad_s[3];
303 StatePhysicalGradientFromReference_ND(dim, Q, i, gas, s, STATEVAR_CONSERVATIVE, grad_q, dXdx, grad_s);
304
305 const CeedScalar Grad_E[3] = {grad_s[0].U.E_total, grad_s[1].U.E_total, grad_s[2].U.E_total};
306
307 for (CeedInt f = 0; f < 4; f++) {
308 for (CeedInt j = 0; j < dim; j++) grad_v[j][f][i] = 0; // No Change in density or momentum
309 v[f][i] = wdetJ * q_dot[f][i]; // K Mass/transient term
310 }
311
312 CeedScalar div_u = 0;
313 for (CeedInt j = 0; j < dim; j++) {
314 for (CeedInt k = 0; k < dim; k++) {
315 div_u += grad_s[k].Y.velocity[j];
316 }
317 }
318 CeedScalar strong_conv = s.U.E_total * div_u + DotN(s.Y.velocity, Grad_E, dim);
319 CeedScalar strong_res = q_dot[4][i] + strong_conv;
320
321 v[4][i] = wdetJ * q_dot[4][i]; // transient part (ALWAYS)
322
323 CeedScalar uX[3] = {0.};
324 MatVecNM(dXdx, s.Y.velocity, dim, dim, CEED_NOTRANSPOSE, uX);
325
326 if (context->strong_form) { // Strong Galerkin convection term: v div(E u)
327 v[4][i] += wdetJ * strong_conv;
328 } else { // Weak Galerkin convection term: -dv \cdot (E u)
329 for (CeedInt j = 0; j < dim; j++) grad_v[j][4][i] = -wdetJ * s.U.E_total * uX[j];
330 }
331
332 { // Diffusion
333 CeedScalar Fe[3], Fe_dXdx[3] = {0.};
334
335 for (CeedInt i = 0; i < dim; i++) Fe[i] = -context->diffusion_coeff * grad_s[i].U.E_total;
336 MatVecNM(dXdx, Fe, dim, dim, CEED_NOTRANSPOSE, Fe_dXdx);
337 for (CeedInt k = 0; k < dim; k++) grad_v[k][4][i] -= wdetJ * Fe_dXdx[k];
338 }
339
340 const CeedScalar TauS = Tau(context, s, dXdx, dim);
341 for (CeedInt j = 0; j < dim; j++) switch (context->stabilization) {
342 case STAB_NONE:
343 break;
344 case STAB_SU:
345 grad_v[j][4][i] += wdetJ * TauS * strong_conv * uX[j];
346 break;
347 case STAB_SUPG:
348 grad_v[j][4][i] += wdetJ * TauS * strong_res * uX[j];
349 break;
350 }
351 StoredValuesPack(Q, i, 0, 14, zeros, jac_data);
352 }
353 }
354
IFunction_Advection(void * ctx,CeedInt Q,const CeedScalar * const * in,CeedScalar * const * out)355 CEED_QFUNCTION(IFunction_Advection)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
356 IFunction_AdvectionGeneric(ctx, Q, in, out, 3);
357 return 0;
358 }
359
IFunction_Advection2d(void * ctx,CeedInt Q,const CeedScalar * const * in,CeedScalar * const * out)360 CEED_QFUNCTION(IFunction_Advection2d)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
361 IFunction_AdvectionGeneric(ctx, Q, in, out, 2);
362 return 0;
363 }
364
MassFunction_AdvectionGeneric(void * ctx,CeedInt Q,const CeedScalar * const * in,CeedScalar * const * out,CeedInt dim)365 CEED_QFUNCTION_HELPER void MassFunction_AdvectionGeneric(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out, CeedInt dim) {
366 const CeedScalar(*q_dot)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0];
367 const CeedScalar(*q)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[1];
368 const CeedScalar(*q_data) = in[2];
369
370 CeedScalar(*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0];
371 CeedScalar(*grad_v)[5][CEED_Q_VLA] = (CeedScalar(*)[5][CEED_Q_VLA])out[1];
372
373 AdvectionContext context = (AdvectionContext)ctx;
374 struct NewtonianIdealGasContext_ gas_struct = {0};
375 NewtonianIdealGasContext gas = &gas_struct;
376
377 CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
378 const CeedScalar qi[5] = {q[0][i], q[1][i], q[2][i], q[3][i], q[4][i]};
379 const State s = StateFromU(gas, qi);
380 CeedScalar wdetJ, dXdx[9];
381 QdataUnpack_ND(dim, Q, i, q_data, &wdetJ, dXdx);
382
383 for (CeedInt f = 0; f < 4; f++) {
384 for (CeedInt j = 0; j < dim; j++) grad_v[j][f][i] = 0; // No Change in density or momentum
385 v[f][i] = wdetJ * q_dot[f][i]; // K Mass/transient term
386 }
387
388 // Unstabilized mass term
389 v[4][i] = wdetJ * q_dot[4][i];
390
391 // Stabilized mass term
392 CeedScalar uX[3] = {0.};
393 MatVecNM(dXdx, s.Y.velocity, dim, dim, CEED_NOTRANSPOSE, uX);
394 const CeedScalar TauS = Tau(context, s, dXdx, dim);
395 for (CeedInt j = 0; j < dim; j++) switch (context->stabilization) {
396 case STAB_NONE:
397 case STAB_SU:
398 grad_v[j][4][i] = 0;
399 break; // These should be run with the unstabilized mass matrix anyways
400 case STAB_SUPG:
401 grad_v[j][4][i] = wdetJ * TauS * q_dot[4][i] * uX[j];
402 break;
403 }
404 }
405 }
406
MassFunction_Advection(void * ctx,CeedInt Q,const CeedScalar * const * in,CeedScalar * const * out)407 CEED_QFUNCTION(MassFunction_Advection)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
408 MassFunction_AdvectionGeneric(ctx, Q, in, out, 3);
409 return 0;
410 }
411
MassFunction_Advection2D(void * ctx,CeedInt Q,const CeedScalar * const * in,CeedScalar * const * out)412 CEED_QFUNCTION(MassFunction_Advection2D)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
413 MassFunction_AdvectionGeneric(ctx, Q, in, out, 2);
414 return 0;
415 }
416
417 // *****************************************************************************
418 // This QFunction implements Advection for explicit time stepping method
419 // *****************************************************************************
RHSFunction_AdvectionGeneric(void * ctx,CeedInt Q,const CeedScalar * const * in,CeedScalar * const * out,CeedInt dim)420 CEED_QFUNCTION_HELPER void RHSFunction_AdvectionGeneric(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out, CeedInt dim) {
421 const CeedScalar(*q)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0];
422 const CeedScalar(*grad_q) = in[1];
423 const CeedScalar(*q_data) = in[2];
424
425 CeedScalar(*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0];
426 CeedScalar(*grad_v)[5][CEED_Q_VLA] = (CeedScalar(*)[5][CEED_Q_VLA])out[1];
427
428 AdvectionContext context = (AdvectionContext)ctx;
429 struct NewtonianIdealGasContext_ gas_struct = {0};
430 NewtonianIdealGasContext gas = &gas_struct;
431
432 CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
433 const CeedScalar qi[5] = {q[0][i], q[1][i], q[2][i], q[3][i], q[4][i]};
434 const State s = StateFromU(gas, qi);
435
436 CeedScalar wdetJ, dXdx[9];
437 QdataUnpack_ND(dim, Q, i, q_data, &wdetJ, dXdx);
438 State grad_s[3];
439 StatePhysicalGradientFromReference_ND(dim, Q, i, gas, s, STATEVAR_CONSERVATIVE, grad_q, dXdx, grad_s);
440
441 const CeedScalar Grad_E[3] = {grad_s[0].U.E_total, grad_s[1].U.E_total, grad_s[2].U.E_total};
442
443 for (CeedInt f = 0; f < 4; f++) {
444 for (CeedInt j = 0; j < dim; j++) grad_v[j][f][i] = 0; // No Change in density or momentum
445 v[f][i] = 0.;
446 }
447
448 CeedScalar div_u = 0;
449 for (CeedInt j = 0; j < dim; j++) {
450 for (CeedInt k = 0; k < dim; k++) {
451 div_u += grad_s[k].Y.velocity[j];
452 }
453 }
454 CeedScalar strong_conv = s.U.E_total * div_u + DotN(s.Y.velocity, Grad_E, dim);
455
456 CeedScalar uX[3] = {0.};
457 MatVecNM(dXdx, s.Y.velocity, dim, dim, CEED_NOTRANSPOSE, uX);
458
459 if (context->strong_form) { // Strong Galerkin convection term: v div(E u)
460 v[4][i] = -wdetJ * strong_conv;
461 for (CeedInt j = 0; j < dim; j++) grad_v[j][4][i] = 0;
462 } else { // Weak Galerkin convection term: -dv \cdot (E u)
463 for (CeedInt j = 0; j < dim; j++) grad_v[j][4][i] = wdetJ * s.U.E_total * uX[j];
464 v[4][i] = 0.;
465 }
466
467 { // Diffusion
468 CeedScalar Fe[3], Fe_dXdx[3] = {0.};
469
470 for (CeedInt i = 0; i < dim; i++) Fe[i] = -context->diffusion_coeff * grad_s[i].U.E_total;
471 MatVecNM(dXdx, Fe, dim, dim, CEED_NOTRANSPOSE, Fe_dXdx);
472 for (CeedInt k = 0; k < dim; k++) grad_v[k][4][i] += wdetJ * Fe_dXdx[k];
473 }
474
475 const CeedScalar TauS = Tau(context, s, dXdx, dim);
476 for (CeedInt j = 0; j < dim; j++) switch (context->stabilization) {
477 case STAB_NONE:
478 break;
479 case STAB_SU:
480 case STAB_SUPG:
481 grad_v[j][4][i] -= wdetJ * TauS * strong_conv * uX[j];
482 break;
483 }
484 }
485 }
486
RHS_Advection(void * ctx,CeedInt Q,const CeedScalar * const * in,CeedScalar * const * out)487 CEED_QFUNCTION(RHS_Advection)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
488 RHSFunction_AdvectionGeneric(ctx, Q, in, out, 3);
489 return 0;
490 }
491
RHS_Advection2d(void * ctx,CeedInt Q,const CeedScalar * const * in,CeedScalar * const * out)492 CEED_QFUNCTION(RHS_Advection2d)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
493 RHSFunction_AdvectionGeneric(ctx, Q, in, out, 2);
494 return 0;
495 }
496
497 // *****************************************************************************
498 // This QFunction implements consistent outflow and inflow BCs
499 // for advection
500 //
501 // Inflow and outflow faces are determined based on sign(dot(wind, normal)):
502 // sign(dot(wind, normal)) > 0 : outflow BCs
503 // sign(dot(wind, normal)) < 0 : inflow BCs
504 //
505 // Outflow BCs:
506 // The validity of the weak form of the governing equations is extended to the outflow and the current values of E are applied.
507 //
508 // Inflow BCs:
509 // A prescribed Total Energy (E_wind) is applied weakly.
510 // *****************************************************************************
Advection_InOutFlowGeneric(void * ctx,CeedInt Q,const CeedScalar * const * in,CeedScalar * const * out,CeedInt dim)511 CEED_QFUNCTION(Advection_InOutFlowGeneric)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out, CeedInt dim) {
512 const CeedScalar(*q)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0];
513 const CeedScalar(*q_data_sur) = in[2];
514
515 CeedScalar(*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0];
516 AdvectionContext context = (AdvectionContext)ctx;
517 const CeedScalar E_wind = context->E_wind;
518 const CeedScalar strong_form = context->strong_form;
519 const bool is_implicit = context->implicit;
520
521 CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
522 const CeedScalar rho = q[0][i];
523 const CeedScalar u[3] = {q[1][i] / rho, q[2][i] / rho, q[3][i] / rho};
524 const CeedScalar E = q[4][i];
525
526 CeedScalar wdetJb, norm[3];
527 QdataBoundaryUnpack_ND(dim, Q, i, q_data_sur, &wdetJb, NULL, norm);
528 wdetJb *= is_implicit ? -1. : 1.;
529
530 const CeedScalar u_normal = DotN(norm, u, dim);
531
532 // No Change in density or momentum
533 for (CeedInt j = 0; j < 4; j++) {
534 v[j][i] = 0;
535 }
536 // Implementing in/outflow BCs
537 if (u_normal > 0) { // outflow
538 v[4][i] = -(1 - strong_form) * wdetJb * E * u_normal;
539 } else { // inflow
540 v[4][i] = -(1 - strong_form) * wdetJb * E_wind * u_normal;
541 }
542 }
543 return 0;
544 }
545
Advection_InOutFlow(void * ctx,CeedInt Q,const CeedScalar * const * in,CeedScalar * const * out)546 CEED_QFUNCTION(Advection_InOutFlow)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
547 Advection_InOutFlowGeneric(ctx, Q, in, out, 3);
548 return 0;
549 }
550
Advection2d_InOutFlow(void * ctx,CeedInt Q,const CeedScalar * const * in,CeedScalar * const * out)551 CEED_QFUNCTION(Advection2d_InOutFlow)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
552 Advection_InOutFlowGeneric(ctx, Q, in, out, 2);
553 return 0;
554 }
555