1 // Copyright (c) 2017-2024, 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 #include <ceed.h> 11 #include <math.h> 12 13 #include "newtonian_state.h" 14 #include "newtonian_types.h" 15 #include "utils.h" 16 17 typedef struct ChannelContext_ *ChannelContext; 18 struct ChannelContext_ { 19 bool implicit; // !< Using implicit timesteping or not 20 CeedScalar theta0; // !< Reference temperature 21 CeedScalar P0; // !< Reference Pressure 22 CeedScalar umax; // !< Centerline velocity 23 CeedScalar center; // !< Y Coordinate for center of channel 24 CeedScalar H; // !< Channel half-height 25 CeedScalar B; // !< Body-force driving the flow 26 struct NewtonianIdealGasContext_ newtonian_ctx; 27 }; 28 29 CEED_QFUNCTION_HELPER State Exact_Channel(CeedInt dim, CeedScalar time, const CeedScalar X[], CeedInt Nf, void *ctx) { 30 const ChannelContext context = (ChannelContext)ctx; 31 const CeedScalar theta0 = context->theta0; 32 const CeedScalar P0 = context->P0; 33 const CeedScalar umax = context->umax; 34 const CeedScalar center = context->center; 35 const CeedScalar H = context->H; 36 NewtonianIdealGasContext gas = &context->newtonian_ctx; 37 const CeedScalar cp = gas->cp; 38 const CeedScalar mu = gas->mu; 39 const CeedScalar k = gas->k; 40 // There is a gravity body force but it is excluded from 41 // the potential energy due to periodicity. 42 // g = (g, 0, 0) 43 // x = (0, x_2, x_3) 44 // e_potential = dot(g, x) = 0 45 const CeedScalar x[3] = {0, X[1], X[2]}; 46 47 const CeedScalar Pr = mu / (cp * k); 48 const CeedScalar Ec = (umax * umax) / (cp * theta0); 49 const CeedScalar theta = theta0 * (1 + (Pr * Ec / 3) * (1 - Square(Square((x[1] - center) / H)))); 50 CeedScalar Y[5] = {0.}; 51 Y[0] = P0; 52 Y[1] = umax * (1 - Square((x[1] - center) / H)); 53 Y[2] = 0.; 54 Y[3] = 0.; 55 Y[4] = theta; 56 57 return StateFromY(gas, Y); 58 } 59 60 // ***************************************************************************** 61 // This QFunction set the initial condition 62 // ***************************************************************************** 63 CEED_QFUNCTION(ICsChannel)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 64 // Inputs 65 const CeedScalar(*X)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0]; 66 67 // Outputs 68 CeedScalar(*q0)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0]; 69 70 // Context 71 const ChannelContext context = (ChannelContext)ctx; 72 73 // Quadrature Point Loop 74 CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { 75 const CeedScalar x[] = {X[0][i], X[1][i], X[2][i]}; 76 State s = Exact_Channel(3, 0., x, 5, ctx); 77 CeedScalar q[5] = {0}; 78 switch (context->newtonian_ctx.state_var) { 79 case STATEVAR_CONSERVATIVE: 80 UnpackState_U(s.U, q); 81 break; 82 case STATEVAR_PRIMITIVE: 83 UnpackState_Y(s.Y, q); 84 break; 85 } 86 87 for (CeedInt j = 0; j < 5; j++) q0[j][i] = q[j]; 88 89 } // End of Quadrature Point Loop 90 return 0; 91 } 92 93 // ***************************************************************************** 94 // This QFunction set the inflow boundary condition for conservative variables 95 // ***************************************************************************** 96 CEED_QFUNCTION(Channel_Inflow)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 97 // Inputs 98 const CeedScalar(*q)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0]; 99 const CeedScalar(*q_data_sur) = in[2]; 100 const CeedScalar(*X)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[3]; 101 102 // Outputs 103 CeedScalar(*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0]; 104 105 const ChannelContext context = (ChannelContext)ctx; 106 const bool is_implicit = context->implicit; 107 NewtonianIdealGasContext gas = &context->newtonian_ctx; 108 const CeedScalar cv = gas->cv; 109 const CeedScalar gamma = HeatCapacityRatio(&context->newtonian_ctx); 110 111 // Quadrature Point Loop 112 CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { 113 CeedScalar wdetJb, norm[3]; 114 QdataBoundaryUnpack_3D(Q, i, q_data_sur, &wdetJb, NULL, norm); 115 wdetJb *= is_implicit ? -1. : 1.; 116 117 // There is a gravity body force but it is excluded from 118 // the potential energy due to periodicity. 119 // g = (g, 0, 0) 120 // x = (0, x_2, x_3) 121 // e_potential = dot(g, x) = 0 122 const CeedScalar x[3] = {0, X[1][i], X[2][i]}; 123 124 // Calculate prescribed inflow values 125 State s_exact = Exact_Channel(3, 0., x, 5, ctx); 126 CeedScalar q_exact[5] = {0.}; 127 UnpackState_U(s_exact.U, q_exact); 128 129 // Find pressure using state inside the domain 130 CeedScalar q_inside[5] = {0}; 131 for (CeedInt j = 0; j < 5; j++) q_inside[j] = q[j][i]; 132 State s_inside = StateFromU(gas, q_inside); 133 const CeedScalar P = s_inside.Y.pressure; 134 135 // Find inflow state using calculated P and prescribed velocity, theta0 136 const CeedScalar e_internal = cv * s_exact.Y.temperature; 137 const CeedScalar rho_in = P / ((gamma - 1) * e_internal); 138 const CeedScalar E_kinetic = .5 * rho_in * Dot3(s_exact.Y.velocity, s_exact.Y.velocity); 139 const CeedScalar E = rho_in * e_internal + E_kinetic; 140 141 // The Physics 142 // Zero v so all future terms can safely sum into it 143 for (CeedInt j = 0; j < 5; j++) v[j][i] = 0.; 144 145 const CeedScalar u_normal = Dot3(norm, s_exact.Y.velocity); 146 147 // The Physics 148 // -- Density 149 v[0][i] -= wdetJb * rho_in * u_normal; 150 151 // -- Momentum 152 for (CeedInt j = 0; j < 3; j++) v[j + 1][i] -= wdetJb * (rho_in * u_normal * s_exact.Y.velocity[j] + norm[j] * P); 153 154 // -- Total Energy Density 155 v[4][i] -= wdetJb * u_normal * (E + P); 156 157 } // End Quadrature Point Loop 158 return 0; 159 } 160 161 // ***************************************************************************** 162 // This QFunction set the outflow boundary condition for conservative variables 163 // ***************************************************************************** 164 CEED_QFUNCTION(Channel_Outflow)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 165 // Inputs 166 const CeedScalar(*q)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0]; 167 const CeedScalar(*q_data_sur) = in[2]; 168 169 // Outputs 170 CeedScalar(*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0]; 171 172 const ChannelContext context = (ChannelContext)ctx; 173 const bool is_implicit = context->implicit; 174 const CeedScalar P0 = context->P0; 175 176 // Quadrature Point Loop 177 CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { 178 CeedScalar wdetJb, norm[3]; 179 QdataBoundaryUnpack_3D(Q, i, q_data_sur, &wdetJb, NULL, norm); 180 wdetJb *= is_implicit ? -1. : 1.; 181 182 const CeedScalar rho = q[0][i]; 183 const CeedScalar u[3] = {q[1][i] / rho, q[2][i] / rho, q[3][i] / rho}; 184 const CeedScalar E = q[4][i]; 185 186 // The Physics 187 // Zero v so all future terms can safely sum into it 188 for (CeedInt j = 0; j < 5; j++) v[j][i] = 0.; 189 190 // Implementing outflow condition 191 const CeedScalar P = P0; // pressure 192 const CeedScalar u_normal = Dot3(norm, u); // Normal velocity 193 // The Physics 194 // -- Density 195 v[0][i] -= wdetJb * rho * u_normal; 196 197 // -- Momentum 198 for (CeedInt j = 0; j < 3; j++) v[j + 1][i] -= wdetJb * (rho * u_normal * u[j] + norm[j] * P); 199 200 // -- Total Energy Density 201 v[4][i] -= wdetJb * u_normal * (E + P); 202 203 } // End Quadrature Point Loop 204 return 0; 205 } 206