1 // SPDX-FileCopyrightText: Copyright (c) 2017-2024, HONEE contributors. 2 // SPDX-License-Identifier: Apache-2.0 OR BSD-2-Clause 3 4 /// @file 5 /// Operator for Navier-Stokes example using PETSc 6 #include <ceed/types.h> 7 8 #include "newtonian_state.h" 9 #include "newtonian_types.h" 10 #include "stabilization.h" 11 #include "utils.h" 12 13 CEED_QFUNCTION_HELPER void InternalDampingLayer(const NewtonianIdealGasContext context, const State s, const CeedScalar sigma, CeedScalar damp_Y[5], 14 CeedScalar damp_residual[5]) { 15 ScaleN(damp_Y, sigma, 5); 16 State damp_s = StateFromY_fwd(context, s, damp_Y); 17 18 CeedScalar U[5]; 19 UnpackState_U(damp_s.U, U); 20 for (int i = 0; i < 5; i++) damp_residual[i] += U[i]; 21 } 22 23 // ***************************************************************************** 24 // This QFunction sets a "still" initial condition for generic Newtonian IG problems 25 // ***************************************************************************** 26 CEED_QFUNCTION_HELPER int ICsNewtonianIG(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out, StateVariable state_var) { 27 CeedScalar(*q0)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0]; 28 29 const SetupContext context = (SetupContext)ctx; 30 31 CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { 32 CeedScalar q[5]; 33 State s = StateFromPrimitive(&context->gas, context->reference); 34 StateToQ(&context->gas, s, q, state_var); 35 for (CeedInt j = 0; j < 5; j++) q0[j][i] = q[j]; 36 } 37 return 0; 38 } 39 40 CEED_QFUNCTION(ICsNewtonianIG_Conserv)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 41 return ICsNewtonianIG(ctx, Q, in, out, STATEVAR_CONSERVATIVE); 42 } 43 44 CEED_QFUNCTION(ICsNewtonianIG_Prim)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 45 return ICsNewtonianIG(ctx, Q, in, out, STATEVAR_PRIMITIVE); 46 } 47 48 CEED_QFUNCTION(ICsNewtonianIG_Entropy)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 49 return ICsNewtonianIG(ctx, Q, in, out, STATEVAR_ENTROPY); 50 } 51 52 CEED_QFUNCTION_HELPER int MassFunction_Newtonian(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out, StateVariable state_var) { 53 const CeedScalar(*q_dot)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0]; 54 const CeedScalar(*q)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[1]; 55 const CeedScalar(*q_data) = in[2]; 56 CeedScalar(*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0]; 57 CeedScalar(*Grad_v)[5][CEED_Q_VLA] = (CeedScalar(*)[5][CEED_Q_VLA])out[1]; 58 59 NewtonianIdealGasContext context = (NewtonianIdealGasContext)ctx; 60 61 CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { 62 const CeedScalar qi[5] = {q[0][i], q[1][i], q[2][i], q[3][i], q[4][i]}; 63 const CeedScalar qi_dot[5] = {q_dot[0][i], q_dot[1][i], q_dot[2][i], q_dot[3][i], q_dot[4][i]}; 64 const State s = StateFromQ(context, qi, state_var); 65 const State s_dot = StateFromQ(context, qi_dot, state_var); 66 CeedScalar wdetJ, dXdx[3][3]; 67 QdataUnpack_3D(Q, i, q_data, &wdetJ, dXdx); 68 69 // Standard mass matrix term 70 for (CeedInt f = 0; f < 5; f++) { 71 v[f][i] = wdetJ * qi_dot[f]; 72 } 73 74 // Stabilization method: none (Galerkin), SU, or SUPG 75 State grad_s[3] = {{{0.}}}; 76 CeedScalar Tau_d[3], stab[5][3], body_force[5] = {0.}, divFdiff[5] = {0.}, U_dot[5]; 77 UnpackState_U(s_dot.U, U_dot); 78 Tau_diagPrim(context, s, dXdx, context->dt, Tau_d); 79 Stabilization(context, s, Tau_d, grad_s, U_dot, body_force, divFdiff, stab); 80 81 // Stabilized mass term 82 for (CeedInt j = 0; j < 5; j++) { 83 for (CeedInt k = 0; k < 3; k++) { 84 Grad_v[k][j][i] = wdetJ * (stab[j][0] * dXdx[k][0] + stab[j][1] * dXdx[k][1] + stab[j][2] * dXdx[k][2]); 85 } 86 } 87 } 88 return 0; 89 } 90 91 CEED_QFUNCTION(MassFunction_Newtonian_Conserv)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 92 return MassFunction_Newtonian(ctx, Q, in, out, STATEVAR_CONSERVATIVE); 93 } 94 95 // ***************************************************************************** 96 // This QFunction implements the following formulation of Navier-Stokes with explicit time stepping method 97 // 98 // This is 3D compressible Navier-Stokes in conservation form with state variables of density, momentum density, and total energy density. 99 // 100 // State Variables: q = ( rho, U1, U2, U3, E ) 101 // rho - Mass Density 102 // Ui - Momentum Density, Ui = rho ui 103 // E - Total Energy Density, E = rho (cv T + (u u)/2 + g z) 104 // 105 // Navier-Stokes Equations: 106 // drho/dt + div( U ) = 0 107 // dU/dt + div( rho (u x u) + P I3 ) + rho g khat = div( Fu ) 108 // dE/dt + div( (E + P) u ) = div( Fe ) 109 // 110 // Viscous Stress: 111 // Fu = mu (grad( u ) + grad( u )^T + lambda div ( u ) I3) 112 // 113 // Thermal Stress: 114 // Fe = u Fu + k grad( T ) 115 // Equation of State 116 // P = (gamma - 1) (E - rho (u u) / 2 - rho g z) 117 // 118 // Stabilization: 119 // Tau = diag(TauC, TauM, TauM, TauM, TauE) 120 // f1 = rho sqrt(ui uj gij) 121 // gij = dXi/dX * dXi/dX 122 // TauC = Cc f1 / (8 gii) 123 // TauM = min( 1 , 1 / f1 ) 124 // TauE = TauM / (Ce cv) 125 // 126 // SU = Galerkin + grad(v) . ( Ai^T * Tau * (Aj q,j) ) 127 // 128 // Constants: 129 // lambda = - 2 / 3, From Stokes hypothesis 130 // mu , Dynamic viscosity 131 // k , Thermal conductivity 132 // cv , Specific heat, constant volume 133 // cp , Specific heat, constant pressure 134 // g , Gravity 135 // gamma = cp / cv, Specific heat ratio 136 // 137 // We require the product of the inverse of the Jacobian (dXdx_j,k) and its transpose (dXdx_k,j) to properly compute integrals of the form: int( gradv 138 // gradu ) 139 // ***************************************************************************** 140 CEED_QFUNCTION(RHSFunction_Newtonian)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 141 const CeedScalar(*q)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0]; 142 const CeedScalar(*Grad_q) = in[1]; 143 const CeedScalar(*q_data) = in[2]; 144 const CeedScalar(*x)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[3]; 145 CeedScalar(*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0]; 146 CeedScalar(*Grad_v)[5][CEED_Q_VLA] = (CeedScalar(*)[5][CEED_Q_VLA])out[1]; 147 148 NewtonianIdealGasContext context = (NewtonianIdealGasContext)ctx; 149 const CeedScalar *g = context->g; 150 const CeedScalar dt = context->dt; 151 const CeedScalar P0 = context->idl_pressure; 152 153 CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { 154 CeedScalar U[5], wdetJ, dXdx[3][3]; 155 const CeedScalar x_i[3] = {x[0][i], x[1][i], x[2][i]}; 156 for (int j = 0; j < 5; j++) U[j] = q[j][i]; 157 QdataUnpack_3D(Q, i, q_data, &wdetJ, dXdx); 158 State s = StateFromU(context, U); 159 160 State grad_s[3]; 161 StatePhysicalGradientFromReference(Q, i, context, s, STATEVAR_CONSERVATIVE, Grad_q, dXdx, grad_s); 162 163 CeedScalar strain_rate[6], kmstress[6], stress[3][3], Fe[3]; 164 KMStrainRate_State(grad_s, strain_rate); 165 NewtonianStress(context, strain_rate, kmstress); 166 KMUnpack(kmstress, stress); 167 ViscousEnergyFlux(context, s.Y, grad_s, stress, Fe); 168 169 StateConservative F_inviscid[3]; 170 FluxInviscid(context, s, F_inviscid); 171 172 // Total flux 173 CeedScalar Flux[5][3]; 174 FluxTotal(F_inviscid, stress, Fe, Flux); 175 176 for (CeedInt j = 0; j < 5; j++) { 177 for (CeedInt k = 0; k < 3; k++) Grad_v[k][j][i] = wdetJ * (dXdx[k][0] * Flux[j][0] + dXdx[k][1] * Flux[j][1] + dXdx[k][2] * Flux[j][2]); 178 } 179 180 const CeedScalar body_force[5] = {0, s.U.density * g[0], s.U.density * g[1], s.U.density * g[2], Dot3(s.U.momentum, g)}; 181 for (int j = 0; j < 5; j++) v[j][i] = wdetJ * body_force[j]; 182 183 if (context->idl_enable) { 184 const CeedScalar sigma = LinearRampCoefficient(context->idl_amplitude, context->idl_length, context->idl_start, x_i[0]); 185 CeedScalar damp_state[5] = {s.Y.pressure - P0, 0, 0, 0, 0}, idl_residual[5] = {0.}; 186 InternalDampingLayer(context, s, sigma, damp_state, idl_residual); 187 for (int j = 0; j < 5; j++) v[j][i] -= wdetJ * idl_residual[j]; 188 } 189 190 // -- Stabilization method: none (Galerkin), SU, or SUPG 191 CeedScalar Tau_d[3], stab[5][3], U_dot[5] = {0}, zeroFlux[5] = {0.}; 192 Tau_diagPrim(context, s, dXdx, dt, Tau_d); 193 Stabilization(context, s, Tau_d, grad_s, U_dot, body_force, zeroFlux, stab); 194 195 for (CeedInt j = 0; j < 5; j++) { 196 for (CeedInt k = 0; k < 3; k++) Grad_v[k][j][i] -= wdetJ * (stab[j][0] * dXdx[k][0] + stab[j][1] * dXdx[k][1] + stab[j][2] * dXdx[k][2]); 197 } 198 } 199 return 0; 200 } 201 202 // ***************************************************************************** 203 // This QFunction implements the Navier-Stokes equations (mentioned above) with implicit time stepping method 204 // 205 // SU = Galerkin + grad(v) . ( Ai^T * Tau * (Aj q,j) ) 206 // SUPG = Galerkin + grad(v) . ( Ai^T * Tau * (q_dot + Aj q,j - body force) ) 207 // (diffusive terms will be added later) 208 // ***************************************************************************** 209 CEED_QFUNCTION_HELPER int IFunction_Newtonian(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out, StateVariable state_var) { 210 NewtonianIdealGasContext context = (NewtonianIdealGasContext)ctx; 211 212 const CeedScalar(*q)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0]; 213 const CeedScalar(*Grad_q) = in[1]; 214 const CeedScalar(*q_dot)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[2]; 215 const CeedScalar(*q_data) = in[3]; 216 const CeedScalar(*x)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[4]; 217 const CeedScalar(*divFdiff)[CEED_Q_VLA] = context->divFdiff_method != DIV_DIFF_FLUX_PROJ_NONE ? (const CeedScalar(*)[CEED_Q_VLA])in[5] : NULL; 218 CeedScalar(*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0]; 219 CeedScalar(*Grad_v)[5][CEED_Q_VLA] = (CeedScalar(*)[5][CEED_Q_VLA])out[1]; 220 CeedScalar(*jac_data) = out[2]; 221 222 const CeedScalar *g = context->g; 223 const CeedScalar dt = context->dt; 224 const CeedScalar P0 = context->idl_pressure; 225 226 CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { 227 const CeedScalar qi[5] = {q[0][i], q[1][i], q[2][i], q[3][i], q[4][i]}; 228 const CeedScalar x_i[3] = {x[0][i], x[1][i], x[2][i]}; 229 const State s = StateFromQ(context, qi, state_var); 230 231 CeedScalar wdetJ, dXdx[3][3]; 232 QdataUnpack_3D(Q, i, q_data, &wdetJ, dXdx); 233 State grad_s[3]; 234 StatePhysicalGradientFromReference(Q, i, context, s, state_var, Grad_q, dXdx, grad_s); 235 236 CeedScalar strain_rate[6], kmstress[6], stress[3][3], Fe[3]; 237 KMStrainRate_State(grad_s, strain_rate); 238 NewtonianStress(context, strain_rate, kmstress); 239 KMUnpack(kmstress, stress); 240 ViscousEnergyFlux(context, s.Y, grad_s, stress, Fe); 241 242 StateConservative F_inviscid[3]; 243 FluxInviscid(context, s, F_inviscid); 244 245 // Total flux 246 CeedScalar Flux[5][3]; 247 FluxTotal(F_inviscid, stress, Fe, Flux); 248 249 for (CeedInt j = 0; j < 5; j++) { 250 for (CeedInt k = 0; k < 3; k++) { 251 Grad_v[k][j][i] = -wdetJ * (dXdx[k][0] * Flux[j][0] + dXdx[k][1] * Flux[j][1] + dXdx[k][2] * Flux[j][2]); 252 } 253 } 254 255 const CeedScalar body_force[5] = {0, s.U.density * g[0], s.U.density * g[1], s.U.density * g[2], Dot3(s.U.momentum, g)}; 256 257 // -- Stabilization method: none (Galerkin), SU, or SUPG 258 CeedScalar Tau_d[3], stab[5][3], U_dot[5] = {0}, qi_dot[5]; 259 for (int j = 0; j < 5; j++) qi_dot[j] = q_dot[j][i]; 260 State s_dot = StateFromQ_fwd(context, s, qi_dot, state_var); 261 UnpackState_U(s_dot.U, U_dot); 262 263 for (CeedInt j = 0; j < 5; j++) v[j][i] = wdetJ * (U_dot[j] - body_force[j]); 264 if (context->idl_enable) { 265 const CeedScalar sigma = LinearRampCoefficient(context->idl_amplitude, context->idl_length, context->idl_start, x_i[0]); 266 StoredValuesPack(Q, i, 14, 1, &sigma, jac_data); 267 CeedScalar damp_state[5] = {s.Y.pressure - P0, 0, 0, 0, 0}, idl_residual[5] = {0.}; 268 InternalDampingLayer(context, s, sigma, damp_state, idl_residual); 269 for (int j = 0; j < 5; j++) v[j][i] += wdetJ * idl_residual[j]; 270 } 271 272 CeedScalar divFdiff_i[5] = {0.}; 273 if (context->divFdiff_method != DIV_DIFF_FLUX_PROJ_NONE) { 274 for (int j = 1; j < 5; j++) divFdiff_i[j] = divFdiff[j - 1][i]; 275 } 276 Tau_diagPrim(context, s, dXdx, dt, Tau_d); 277 Stabilization(context, s, Tau_d, grad_s, U_dot, body_force, divFdiff_i, stab); 278 279 for (CeedInt j = 0; j < 5; j++) { 280 for (CeedInt k = 0; k < 3; k++) { 281 Grad_v[k][j][i] += wdetJ * (stab[j][0] * dXdx[k][0] + stab[j][1] * dXdx[k][1] + stab[j][2] * dXdx[k][2]); 282 } 283 } 284 StoredValuesPack(Q, i, 0, 5, qi, jac_data); 285 StoredValuesPack(Q, i, 5, 6, kmstress, jac_data); 286 StoredValuesPack(Q, i, 11, 3, Tau_d, jac_data); 287 } 288 return 0; 289 } 290 291 CEED_QFUNCTION(IFunction_Newtonian_Conserv)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 292 return IFunction_Newtonian(ctx, Q, in, out, STATEVAR_CONSERVATIVE); 293 } 294 295 CEED_QFUNCTION(IFunction_Newtonian_Prim)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 296 return IFunction_Newtonian(ctx, Q, in, out, STATEVAR_PRIMITIVE); 297 } 298 299 CEED_QFUNCTION(IFunction_Newtonian_Entropy)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 300 return IFunction_Newtonian(ctx, Q, in, out, STATEVAR_ENTROPY); 301 } 302 303 // ***************************************************************************** 304 // This QFunction implements the jacobian of the Navier-Stokes equations for implicit time stepping method. 305 // ***************************************************************************** 306 CEED_QFUNCTION_HELPER int IJacobian_Newtonian(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out, StateVariable state_var) { 307 const CeedScalar(*dq)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0]; 308 const CeedScalar(*Grad_dq) = in[1]; 309 const CeedScalar(*q_data) = in[2]; 310 const CeedScalar(*jac_data) = in[3]; 311 CeedScalar(*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0]; 312 CeedScalar(*Grad_v)[5][CEED_Q_VLA] = (CeedScalar(*)[5][CEED_Q_VLA])out[1]; 313 314 NewtonianIdealGasContext context = (NewtonianIdealGasContext)ctx; 315 const CeedScalar *g = context->g; 316 317 CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { 318 CeedScalar wdetJ, dXdx[3][3]; 319 QdataUnpack_3D(Q, i, q_data, &wdetJ, dXdx); 320 321 CeedScalar qi[5], kmstress[6], Tau_d[3]; 322 StoredValuesUnpack(Q, i, 0, 5, jac_data, qi); 323 StoredValuesUnpack(Q, i, 5, 6, jac_data, kmstress); 324 StoredValuesUnpack(Q, i, 11, 3, jac_data, Tau_d); 325 State s = StateFromQ(context, qi, state_var); 326 327 CeedScalar dqi[5]; 328 for (int j = 0; j < 5; j++) dqi[j] = dq[j][i]; 329 State ds = StateFromQ_fwd(context, s, dqi, state_var); 330 331 State grad_ds[3]; 332 StatePhysicalGradientFromReference(Q, i, context, s, state_var, Grad_dq, dXdx, grad_ds); 333 334 CeedScalar dstrain_rate[6], dkmstress[6], stress[3][3], dstress[3][3], dFe[3]; 335 KMStrainRate_State(grad_ds, dstrain_rate); 336 NewtonianStress(context, dstrain_rate, dkmstress); 337 KMUnpack(dkmstress, dstress); 338 KMUnpack(kmstress, stress); 339 ViscousEnergyFlux_fwd(context, s.Y, ds.Y, grad_ds, stress, dstress, dFe); 340 341 StateConservative dF_inviscid[3]; 342 FluxInviscid_fwd(context, s, ds, dF_inviscid); 343 344 // Total flux 345 CeedScalar dFlux[5][3]; 346 FluxTotal(dF_inviscid, dstress, dFe, dFlux); 347 348 for (int j = 0; j < 5; j++) { 349 for (int k = 0; k < 3; k++) Grad_v[k][j][i] = -wdetJ * (dXdx[k][0] * dFlux[j][0] + dXdx[k][1] * dFlux[j][1] + dXdx[k][2] * dFlux[j][2]); 350 } 351 352 const CeedScalar dbody_force[5] = {0, ds.U.density * g[0], ds.U.density * g[1], ds.U.density * g[2], Dot3(ds.U.momentum, g)}; 353 CeedScalar dU[5] = {0.}; 354 UnpackState_U(ds.U, dU); 355 for (int j = 0; j < 5; j++) v[j][i] = wdetJ * (context->ijacobian_time_shift * dU[j] - dbody_force[j]); 356 357 if (context->idl_enable) { 358 const CeedScalar sigma = jac_data[14 * Q + i]; 359 CeedScalar damp_state[5] = {ds.Y.pressure, 0, 0, 0, 0}, idl_residual[5] = {0.}; 360 // This is a Picard-type linearization of the damping and could be replaced by an InternalDampingLayer_fwd that uses s and ds. 361 InternalDampingLayer(context, s, sigma, damp_state, idl_residual); 362 for (int j = 0; j < 5; j++) v[j][i] += wdetJ * idl_residual[j]; 363 } 364 365 // -- Stabilization method: none (Galerkin), SU, or SUPG 366 CeedScalar dstab[5][3], U_dot[5] = {0}; 367 for (CeedInt j = 0; j < 5; j++) U_dot[j] = context->ijacobian_time_shift * dU[j]; 368 const CeedScalar zeroFlux[5] = {0.}; 369 Stabilization(context, s, Tau_d, grad_ds, U_dot, dbody_force, zeroFlux, dstab); 370 371 for (int j = 0; j < 5; j++) { 372 for (int k = 0; k < 3; k++) Grad_v[k][j][i] += wdetJ * (dstab[j][0] * dXdx[k][0] + dstab[j][1] * dXdx[k][1] + dstab[j][2] * dXdx[k][2]); 373 } 374 } 375 return 0; 376 } 377 378 CEED_QFUNCTION(IJacobian_Newtonian_Conserv)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 379 return IJacobian_Newtonian(ctx, Q, in, out, STATEVAR_CONSERVATIVE); 380 } 381 382 CEED_QFUNCTION(IJacobian_Newtonian_Prim)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 383 return IJacobian_Newtonian(ctx, Q, in, out, STATEVAR_PRIMITIVE); 384 } 385 386 CEED_QFUNCTION(IJacobian_Newtonian_Entropy)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 387 return IJacobian_Newtonian(ctx, Q, in, out, STATEVAR_ENTROPY); 388 } 389 390 // ***************************************************************************** 391 // Compute boundary integral (ie. for strongly set inflows) 392 // ***************************************************************************** 393 CEED_QFUNCTION_HELPER int BoundaryIntegral(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out, StateVariable state_var) { 394 const NewtonianIdealGasContext context = (NewtonianIdealGasContext)ctx; 395 const CeedScalar(*q)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0]; 396 const CeedScalar(*Grad_q) = in[1]; 397 const CeedScalar(*q_data_sur) = in[2]; 398 CeedScalar(*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0]; 399 CeedScalar(*jac_data_sur) = context->is_implicit ? out[1] : NULL; 400 401 const bool is_implicit = context->is_implicit; 402 403 CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { 404 const CeedScalar qi[5] = {q[0][i], q[1][i], q[2][i], q[3][i], q[4][i]}; 405 State s = StateFromQ(context, qi, state_var); 406 407 CeedScalar wdetJb, dXdx[2][3], normal[3]; 408 QdataBoundaryUnpack_3D(Q, i, q_data_sur, &wdetJb, dXdx, normal); 409 wdetJb *= is_implicit ? -1. : 1.; 410 411 State grad_s[3]; 412 StatePhysicalGradientFromReference_Boundary(Q, i, context, s, state_var, Grad_q, dXdx, grad_s); 413 414 CeedScalar strain_rate[6], kmstress[6], stress[3][3], Fe[3]; 415 KMStrainRate_State(grad_s, strain_rate); 416 NewtonianStress(context, strain_rate, kmstress); 417 KMUnpack(kmstress, stress); 418 ViscousEnergyFlux(context, s.Y, grad_s, stress, Fe); 419 420 StateConservative F_inviscid[3]; 421 FluxInviscid(context, s, F_inviscid); 422 423 CeedScalar Flux[5]; 424 FluxTotal_Boundary(F_inviscid, stress, Fe, normal, Flux); 425 426 for (CeedInt j = 0; j < 5; j++) v[j][i] = -wdetJb * Flux[j]; 427 428 if (is_implicit) { 429 StoredValuesPack(Q, i, 0, 5, qi, jac_data_sur); 430 StoredValuesPack(Q, i, 5, 6, kmstress, jac_data_sur); 431 } 432 } 433 return 0; 434 } 435 436 CEED_QFUNCTION(BoundaryIntegral_Conserv)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 437 return BoundaryIntegral(ctx, Q, in, out, STATEVAR_CONSERVATIVE); 438 } 439 440 CEED_QFUNCTION(BoundaryIntegral_Prim)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 441 return BoundaryIntegral(ctx, Q, in, out, STATEVAR_PRIMITIVE); 442 } 443 444 CEED_QFUNCTION(BoundaryIntegral_Entropy)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 445 return BoundaryIntegral(ctx, Q, in, out, STATEVAR_ENTROPY); 446 } 447 448 // ***************************************************************************** 449 // Jacobian for "set nothing" boundary integral 450 // ***************************************************************************** 451 CEED_QFUNCTION_HELPER int BoundaryIntegral_Jacobian(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out, 452 StateVariable state_var) { 453 const CeedScalar(*dq)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0]; 454 const CeedScalar(*Grad_dq) = in[1]; 455 const CeedScalar(*q_data_sur) = in[2]; 456 const CeedScalar(*jac_data_sur) = in[4]; 457 CeedScalar(*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0]; 458 459 const NewtonianIdealGasContext context = (NewtonianIdealGasContext)ctx; 460 const bool is_implicit = context->is_implicit; 461 462 CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { 463 CeedScalar wdetJb, dXdx[2][3], normal[3]; 464 QdataBoundaryUnpack_3D(Q, i, q_data_sur, &wdetJb, dXdx, normal); 465 wdetJb *= is_implicit ? -1. : 1.; 466 467 CeedScalar qi[5], kmstress[6], dqi[5]; 468 StoredValuesUnpack(Q, i, 0, 5, jac_data_sur, qi); 469 StoredValuesUnpack(Q, i, 5, 6, jac_data_sur, kmstress); 470 for (int j = 0; j < 5; j++) dqi[j] = dq[j][i]; 471 472 State s = StateFromQ(context, qi, state_var); 473 State ds = StateFromQ_fwd(context, s, dqi, state_var); 474 475 State grad_ds[3]; 476 StatePhysicalGradientFromReference_Boundary(Q, i, context, s, state_var, Grad_dq, dXdx, grad_ds); 477 478 CeedScalar dstrain_rate[6], dkmstress[6], stress[3][3], dstress[3][3], dFe[3]; 479 KMStrainRate_State(grad_ds, dstrain_rate); 480 NewtonianStress(context, dstrain_rate, dkmstress); 481 KMUnpack(dkmstress, dstress); 482 KMUnpack(kmstress, stress); 483 ViscousEnergyFlux_fwd(context, s.Y, ds.Y, grad_ds, stress, dstress, dFe); 484 485 StateConservative dF_inviscid[3]; 486 FluxInviscid_fwd(context, s, ds, dF_inviscid); 487 488 CeedScalar dFlux[5]; 489 FluxTotal_Boundary(dF_inviscid, dstress, dFe, normal, dFlux); 490 491 for (int j = 0; j < 5; j++) v[j][i] = -wdetJb * dFlux[j]; 492 } 493 return 0; 494 } 495 496 CEED_QFUNCTION(BoundaryIntegral_Jacobian_Conserv)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 497 return BoundaryIntegral_Jacobian(ctx, Q, in, out, STATEVAR_CONSERVATIVE); 498 } 499 500 CEED_QFUNCTION(BoundaryIntegral_Jacobian_Prim)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 501 return BoundaryIntegral_Jacobian(ctx, Q, in, out, STATEVAR_PRIMITIVE); 502 } 503 504 CEED_QFUNCTION(BoundaryIntegral_Jacobian_Entropy)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 505 return BoundaryIntegral_Jacobian(ctx, Q, in, out, STATEVAR_ENTROPY); 506 } 507 508 // @brief Volume integral for RHS of divergence of diffusive flux direct projection 509 CEED_QFUNCTION_HELPER int DivDiffusiveFluxVolumeRHS_NS(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out, 510 StateVariable state_var) { 511 const CeedScalar(*q)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0]; 512 const CeedScalar(*Grad_q) = in[1]; 513 const CeedScalar(*q_data) = in[2]; 514 CeedScalar(*Grad_v)[4][CEED_Q_VLA] = (CeedScalar(*)[4][CEED_Q_VLA])out[0]; 515 516 const NewtonianIdealGasContext context = (NewtonianIdealGasContext)ctx; 517 const StateConservative ZeroInviscidFluxes[3] = {{0}}; 518 519 CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { 520 const CeedScalar qi[5] = {q[0][i], q[1][i], q[2][i], q[3][i], q[4][i]}; 521 const State s = StateFromQ(context, qi, state_var); 522 CeedScalar wdetJ, dXdx[3][3]; 523 CeedScalar stress[3][3], Fe[3], Fdiff[5][3]; 524 525 QdataUnpack_3D(Q, i, q_data, &wdetJ, dXdx); 526 { // Get stress and Fe 527 State grad_s[3]; 528 CeedScalar strain_rate[6], kmstress[6]; 529 530 StatePhysicalGradientFromReference(Q, i, context, s, state_var, Grad_q, dXdx, grad_s); 531 KMStrainRate_State(grad_s, strain_rate); 532 NewtonianStress(context, strain_rate, kmstress); 533 KMUnpack(kmstress, stress); 534 ViscousEnergyFlux(context, s.Y, grad_s, stress, Fe); 535 } 536 537 FluxTotal(ZeroInviscidFluxes, stress, Fe, Fdiff); 538 539 for (CeedInt j = 1; j < 5; j++) { // Continuity has no diffusive flux, therefore skip 540 for (CeedInt k = 0; k < 3; k++) { 541 Grad_v[k][j - 1][i] = -wdetJ * Dot3(dXdx[k], Fdiff[j]); 542 } 543 } 544 } 545 return 0; 546 } 547 548 CEED_QFUNCTION(DivDiffusiveFluxVolumeRHS_NS_Conserv)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 549 return DivDiffusiveFluxVolumeRHS_NS(ctx, Q, in, out, STATEVAR_CONSERVATIVE); 550 } 551 552 CEED_QFUNCTION(DivDiffusiveFluxVolumeRHS_NS_Prim)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 553 return DivDiffusiveFluxVolumeRHS_NS(ctx, Q, in, out, STATEVAR_PRIMITIVE); 554 } 555 556 CEED_QFUNCTION(DivDiffusiveFluxVolumeRHS_NS_Entropy)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 557 return DivDiffusiveFluxVolumeRHS_NS(ctx, Q, in, out, STATEVAR_ENTROPY); 558 } 559 560 // @brief Boundary integral for RHS of divergence of diffusive flux direct projection 561 CEED_QFUNCTION_HELPER int DivDiffusiveFluxBoundaryRHS_NS(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out, 562 StateVariable state_var) { 563 const CeedScalar(*q)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0]; 564 const CeedScalar(*Grad_q) = in[1]; 565 const CeedScalar(*q_data) = in[2]; 566 CeedScalar(*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0]; 567 568 const NewtonianIdealGasContext context = (NewtonianIdealGasContext)ctx; 569 const StateConservative ZeroInviscidFluxes[3] = {{0}}; 570 571 CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { 572 const CeedScalar qi[5] = {q[0][i], q[1][i], q[2][i], q[3][i], q[4][i]}; 573 const State s = StateFromQ(context, qi, state_var); 574 CeedScalar wdetJ, dXdx[3][3], normal[3]; 575 CeedScalar stress[3][3], Fe[3], Fdiff[5]; 576 577 QdataBoundaryGradientUnpack_3D(Q, i, q_data, &wdetJ, dXdx, normal); 578 { // Get stress and Fe 579 State grad_s[3]; 580 CeedScalar strain_rate[6], kmstress[6]; 581 582 StatePhysicalGradientFromReference(Q, i, context, s, state_var, Grad_q, dXdx, grad_s); 583 KMStrainRate_State(grad_s, strain_rate); 584 NewtonianStress(context, strain_rate, kmstress); 585 KMUnpack(kmstress, stress); 586 ViscousEnergyFlux(context, s.Y, grad_s, stress, Fe); 587 } 588 589 FluxTotal_Boundary(ZeroInviscidFluxes, stress, Fe, normal, Fdiff); 590 591 // Continuity has no diffusive flux, therefore skip 592 for (CeedInt j = 1; j < 5; j++) v[j - 1][i] = wdetJ * Fdiff[j]; 593 } 594 return 0; 595 } 596 597 CEED_QFUNCTION(DivDiffusiveFluxBoundaryRHS_NS_Conserv)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 598 return DivDiffusiveFluxBoundaryRHS_NS(ctx, Q, in, out, STATEVAR_CONSERVATIVE); 599 } 600 601 CEED_QFUNCTION(DivDiffusiveFluxBoundaryRHS_NS_Prim)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 602 return DivDiffusiveFluxBoundaryRHS_NS(ctx, Q, in, out, STATEVAR_PRIMITIVE); 603 } 604 605 CEED_QFUNCTION(DivDiffusiveFluxBoundaryRHS_NS_Entropy)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 606 return DivDiffusiveFluxBoundaryRHS_NS(ctx, Q, in, out, STATEVAR_ENTROPY); 607 } 608 609 // @brief Integral for RHS of diffusive flux indirect projection 610 CEED_QFUNCTION_HELPER int DiffusiveFluxRHS_NS(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out, StateVariable state_var) { 611 const CeedScalar(*q)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0]; 612 const CeedScalar(*Grad_q) = in[1]; 613 const CeedScalar(*q_data) = in[2]; 614 CeedScalar(*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0]; 615 616 const NewtonianIdealGasContext context = (NewtonianIdealGasContext)ctx; 617 const StateConservative ZeroInviscidFluxes[3] = {{0}}; 618 619 CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { 620 const CeedScalar qi[5] = {q[0][i], q[1][i], q[2][i], q[3][i], q[4][i]}; 621 const State s = StateFromQ(context, qi, state_var); 622 CeedScalar wdetJ, dXdx[3][3]; 623 CeedScalar stress[3][3], Fe[3], Fdiff[5][3]; 624 625 QdataUnpack_3D(Q, i, q_data, &wdetJ, dXdx); 626 { // Get stress and Fe 627 State grad_s[3]; 628 CeedScalar strain_rate[6], kmstress[6]; 629 630 StatePhysicalGradientFromReference(Q, i, context, s, state_var, Grad_q, dXdx, grad_s); 631 KMStrainRate_State(grad_s, strain_rate); 632 NewtonianStress(context, strain_rate, kmstress); 633 KMUnpack(kmstress, stress); 634 ViscousEnergyFlux(context, s.Y, grad_s, stress, Fe); 635 } 636 637 FluxTotal(ZeroInviscidFluxes, stress, Fe, Fdiff); 638 639 for (CeedInt j = 1; j < 5; j++) { // Continuity has no diffusive flux, therefore skip 640 for (CeedInt k = 0; k < 3; k++) { 641 v[(j - 1) * 3 + k][i] = wdetJ * Fdiff[j][k]; 642 } 643 } 644 } 645 return 0; 646 } 647 648 CEED_QFUNCTION(DiffusiveFluxRHS_NS_Conserv)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 649 return DiffusiveFluxRHS_NS(ctx, Q, in, out, STATEVAR_CONSERVATIVE); 650 } 651 652 CEED_QFUNCTION(DiffusiveFluxRHS_NS_Prim)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 653 return DiffusiveFluxRHS_NS(ctx, Q, in, out, STATEVAR_PRIMITIVE); 654 } 655 656 CEED_QFUNCTION(DiffusiveFluxRHS_NS_Entropy)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 657 return DiffusiveFluxRHS_NS(ctx, Q, in, out, STATEVAR_ENTROPY); 658 } 659