1 // SPDX-FileCopyrightText: Copyright (c) 2017-2024, HONEE contributors. 2 // SPDX-License-Identifier: Apache-2.0 OR BSD-2-Clause 3 4 /// @file 5 /// Utility functions for setting up Freestream boundary condition 6 7 #include "../qfunctions/bc_freestream.h" 8 9 #include <ceed.h> 10 #include <petscdm.h> 11 12 #include <navierstokes.h> 13 #include "../qfunctions/newtonian_types.h" 14 15 static const char *const RiemannSolverTypes[] = {"HLL", "HLLC", "RiemannSolverTypes", "RIEMANN_", NULL}; 16 17 static PetscErrorCode RiemannSolverUnitTests(NewtonianIGProperties gas, CeedScalar rtol); 18 19 typedef struct { 20 RiemannFluxType flux_type; 21 } *FreestreamHoneeBCCtx; 22 23 static PetscErrorCode FreestreamBCSetup_CreateIFunctionQF(BCDefinition bc_def, CeedQFunction *qf) { 24 Honee honee; 25 HoneeBCStruct honee_bc; 26 27 PetscFunctionBeginUser; 28 PetscCall(BCDefinitionGetContext(bc_def, &honee_bc)); 29 honee = honee_bc->honee; 30 FreestreamHoneeBCCtx freestream_honee_bc = (FreestreamHoneeBCCtx)honee_bc->ctx; 31 32 switch (honee->phys->state_var) { 33 case STATEVAR_CONSERVATIVE: 34 switch (freestream_honee_bc->flux_type) { 35 case RIEMANN_HLL: 36 PetscCall(HoneeBCCreateIFunctionQF(bc_def, Freestream_Conserv_HLL, Freestream_Conserv_HLL_loc, honee_bc->qfctx, qf)); 37 break; 38 case RIEMANN_HLLC: 39 PetscCall(HoneeBCCreateIFunctionQF(bc_def, Freestream_Conserv_HLLC, Freestream_Conserv_HLLC_loc, honee_bc->qfctx, qf)); 40 break; 41 } 42 break; 43 case STATEVAR_PRIMITIVE: 44 switch (freestream_honee_bc->flux_type) { 45 case RIEMANN_HLL: 46 PetscCall(HoneeBCCreateIFunctionQF(bc_def, Freestream_Prim_HLL, Freestream_Prim_HLL_loc, honee_bc->qfctx, qf)); 47 break; 48 case RIEMANN_HLLC: 49 PetscCall(HoneeBCCreateIFunctionQF(bc_def, Freestream_Prim_HLLC, Freestream_Prim_HLLC_loc, honee_bc->qfctx, qf)); 50 break; 51 } 52 break; 53 case STATEVAR_ENTROPY: 54 switch (freestream_honee_bc->flux_type) { 55 case RIEMANN_HLL: 56 PetscCall(HoneeBCCreateIFunctionQF(bc_def, Freestream_Entropy_HLL, Freestream_Entropy_HLL_loc, honee_bc->qfctx, qf)); 57 break; 58 case RIEMANN_HLLC: 59 PetscCall(HoneeBCCreateIFunctionQF(bc_def, Freestream_Entropy_HLLC, Freestream_Entropy_HLLC_loc, honee_bc->qfctx, qf)); 60 break; 61 } 62 break; 63 } 64 PetscFunctionReturn(PETSC_SUCCESS); 65 } 66 67 static PetscErrorCode FreestreamBCSetup_CreateIJacobianQF(BCDefinition bc_def, CeedQFunction *qf) { 68 Honee honee; 69 HoneeBCStruct honee_bc; 70 71 PetscFunctionBeginUser; 72 PetscCall(BCDefinitionGetContext(bc_def, &honee_bc)); 73 honee = honee_bc->honee; 74 FreestreamHoneeBCCtx freestream_honee_bc = (FreestreamHoneeBCCtx)honee_bc->ctx; 75 76 switch (honee->phys->state_var) { 77 case STATEVAR_CONSERVATIVE: 78 switch (freestream_honee_bc->flux_type) { 79 case RIEMANN_HLL: 80 PetscCall(HoneeBCCreateIJacobianQF(bc_def, Freestream_Jacobian_Conserv_HLL, Freestream_Jacobian_Conserv_HLL_loc, honee_bc->qfctx, qf)); 81 break; 82 case RIEMANN_HLLC: 83 PetscCall(HoneeBCCreateIJacobianQF(bc_def, Freestream_Jacobian_Conserv_HLLC, Freestream_Jacobian_Conserv_HLLC_loc, honee_bc->qfctx, qf)); 84 break; 85 } 86 break; 87 case STATEVAR_PRIMITIVE: 88 switch (freestream_honee_bc->flux_type) { 89 case RIEMANN_HLL: 90 PetscCall(HoneeBCCreateIJacobianQF(bc_def, Freestream_Jacobian_Prim_HLL, Freestream_Jacobian_Prim_HLL_loc, honee_bc->qfctx, qf)); 91 break; 92 case RIEMANN_HLLC: 93 PetscCall(HoneeBCCreateIJacobianQF(bc_def, Freestream_Jacobian_Prim_HLLC, Freestream_Jacobian_Prim_HLLC_loc, honee_bc->qfctx, qf)); 94 break; 95 } 96 break; 97 case STATEVAR_ENTROPY: 98 switch (freestream_honee_bc->flux_type) { 99 case RIEMANN_HLL: 100 PetscCall(HoneeBCCreateIJacobianQF(bc_def, Freestream_Jacobian_Entropy_HLL, Freestream_Jacobian_Entropy_HLL_loc, honee_bc->qfctx, qf)); 101 break; 102 case RIEMANN_HLLC: 103 PetscCall(HoneeBCCreateIJacobianQF(bc_def, Freestream_Jacobian_Entropy_HLLC, Freestream_Jacobian_Entropy_HLLC_loc, honee_bc->qfctx, qf)); 104 break; 105 } 106 break; 107 } 108 PetscFunctionReturn(PETSC_SUCCESS); 109 } 110 111 PetscErrorCode FreestreamBCSetup(BCDefinition bc_def, ProblemData problem, DM dm, void *ctx, NewtonianIdealGasContext newtonian_ig_ctx, 112 const StatePrimitive *reference) { 113 Honee honee = *(Honee *)ctx; 114 MPI_Comm comm = honee->comm; 115 Ceed ceed = honee->ceed; 116 FreestreamContext freestream_ctx; 117 CeedQFunctionContext freestream_qfctx; 118 RiemannFluxType flux_type = RIEMANN_HLLC; 119 Units units = honee->units; 120 FreestreamHoneeBCCtx freestream_honee_bc; 121 HoneeBCStruct honee_bc; 122 123 PetscFunctionBeginUser; 124 // Freestream inherits reference state. We re-dimensionalize so the defaults in -help will be visible in SI units. 125 StatePrimitive Y_inf = {.pressure = reference->pressure / units->Pascal, .velocity = {0}, .temperature = reference->temperature / units->Kelvin}; 126 for (int i = 0; i < 3; i++) Y_inf.velocity[i] = reference->velocity[i] * units->second / units->meter; 127 128 PetscOptionsBegin(comm, NULL, "Options for Freestream boundary condition", NULL); 129 PetscCall(PetscOptionsEnum("-freestream_riemann", "Riemann solver to use in freestream boundary condition", NULL, RiemannSolverTypes, 130 (PetscEnum)flux_type, (PetscEnum *)&flux_type, NULL)); 131 PetscCall(PetscOptionsScalar("-freestream_pressure", "Pressure at freestream condition", NULL, Y_inf.pressure, &Y_inf.pressure, NULL)); 132 PetscInt narray = 3; 133 PetscCall(PetscOptionsScalarArray("-freestream_velocity", "Velocity at freestream condition", NULL, Y_inf.velocity, &narray, NULL)); 134 PetscCall(PetscOptionsScalar("-freestream_temperature", "Temperature at freestream condition", NULL, Y_inf.temperature, &Y_inf.temperature, NULL)); 135 PetscOptionsEnd(); 136 137 Y_inf.pressure *= units->Pascal; 138 for (int i = 0; i < 3; i++) Y_inf.velocity[i] *= units->meter / units->second; 139 Y_inf.temperature *= units->Kelvin; 140 141 State S_infty = StateFromPrimitive(newtonian_ig_ctx->gas, Y_inf); 142 143 // -- Set freestream_ctx struct values 144 PetscCall(PetscNew(&freestream_ctx)); 145 *freestream_ctx = (struct FreestreamContext_){ 146 .newt_ctx = *newtonian_ig_ctx, 147 .S_infty = S_infty, 148 }; 149 PetscCallCeed(ceed, CeedQFunctionContextCreate(honee->ceed, &freestream_qfctx)); 150 PetscCallCeed(ceed, CeedQFunctionContextSetData(freestream_qfctx, CEED_MEM_HOST, CEED_USE_POINTER, sizeof(*freestream_ctx), freestream_ctx)); 151 PetscCallCeed(ceed, CeedQFunctionContextSetDataDestroy(freestream_qfctx, CEED_MEM_HOST, FreeContextPetsc)); 152 153 PetscCall(PetscNew(&freestream_honee_bc)); 154 freestream_honee_bc->flux_type = flux_type; 155 PetscCall(PetscNew(&honee_bc)); 156 *honee_bc = (struct HoneeBCStruct_){ 157 .ctx = freestream_honee_bc, 158 .DestroyCtx = PetscCtxDestroyDefault, 159 .honee = honee, 160 .num_comps_jac_data = honee->phys->implicit ? 5 : 0, 161 .qfctx = freestream_qfctx, 162 }; 163 PetscCall(BCDefinitionSetContext(bc_def, (PetscCtxDestroyFn *)HoneeBCDestroy, honee_bc)); 164 165 PetscCall(BCDefinitionSetIFunction(bc_def, FreestreamBCSetup_CreateIFunctionQF, HoneeBCAddIFunctionOp)); 166 PetscCall(BCDefinitionSetIJacobian(bc_def, FreestreamBCSetup_CreateIJacobianQF, HoneeBCAddIJacobianOp)); 167 168 { 169 PetscBool run_unit_tests = PETSC_FALSE; 170 171 PetscCall(PetscOptionsGetBool(NULL, NULL, "-riemann_solver_unit_tests", &run_unit_tests, NULL)); 172 if (run_unit_tests) PetscCall(RiemannSolverUnitTests(newtonian_ig_ctx->gas, 5e-7)); 173 } 174 PetscFunctionReturn(PETSC_SUCCESS); 175 } 176 177 // ***************************************************************************** 178 // Code for verifying the Riemann solver and Riemann Jacobian functions 179 // ***************************************************************************** 180 181 // @brief Calculate relative error, (A - B) / S 182 // If S < threshold, then set S=1 183 static inline CeedScalar RelativeError(CeedScalar S, CeedScalar A, CeedScalar B, CeedScalar threshold) { 184 return (A - B) / (fabs(S) > threshold ? S : 1); 185 } 186 187 // @brief Check errors of a State vector and print if above tolerance 188 static PetscErrorCode CheckQWithTolerance(const CeedScalar Q_s[5], const CeedScalar Q_a[5], const CeedScalar Q_b[5], const char *name, 189 PetscReal rtol_0, PetscReal rtol_u, PetscReal rtol_4) { 190 CeedScalar relative_error[5]; // relative error 191 CeedScalar divisor_threshold = 10 * CEED_EPSILON; 192 193 PetscFunctionBeginUser; 194 relative_error[0] = RelativeError(Q_s[0], Q_a[0], Q_b[0], divisor_threshold); 195 relative_error[4] = RelativeError(Q_s[4], Q_a[4], Q_b[4], divisor_threshold); 196 197 CeedScalar u_magnitude = sqrt(Square(Q_s[1]) + Square(Q_s[2]) + Square(Q_s[3])); 198 for (int i = 1; i < 4; i++) { 199 relative_error[i] = RelativeError(u_magnitude, Q_a[i], Q_b[i], divisor_threshold); 200 } 201 202 if (fabs(relative_error[0]) >= rtol_0) { 203 printf("%s[0] error %g (expected %.10e, got %.10e)\n", name, relative_error[0], Q_s[0], Q_a[0]); 204 } 205 for (int i = 1; i < 4; i++) { 206 if (fabs(relative_error[i]) >= rtol_u) { 207 printf("%s[%d] error %g (expected %.10e, got %.10e)\n", name, i, relative_error[i], Q_s[i], Q_a[i]); 208 } 209 } 210 if (fabs(relative_error[4]) >= rtol_4) { 211 printf("%s[4] error %g (expected %.10e, got %.10e)\n", name, relative_error[4], Q_s[4], Q_a[4]); 212 } 213 PetscFunctionReturn(PETSC_SUCCESS); 214 } 215 216 // @brief Verify RiemannFlux_HLL_fwd function against finite-difference approximation 217 static PetscErrorCode TestRiemannHLL_fwd(NewtonianIGProperties gas, CeedScalar rtol_0, CeedScalar rtol_u, CeedScalar rtol_4) { 218 CeedScalar eps = 4e-7; // Finite difference step 219 char buf[128]; 220 const CeedScalar T = 200; 221 const CeedScalar rho = 1.2; 222 const CeedScalar p = (HeatCapacityRatio(gas) - 1) * rho * gas.cv * T; 223 const CeedScalar u_base = 40; 224 const CeedScalar u[3] = {u_base, u_base * 1.1, u_base * 1.2}; 225 const CeedScalar Y0_left[5] = {p, u[0], u[1], u[2], T}; 226 const CeedScalar Y0_right[5] = {1.2 * p, 1.2 * u[0], 1.2 * u[1], 1.2 * u[2], 1.2 * T}; 227 CeedScalar normal[3] = {1, 2, 3}; 228 229 PetscFunctionBeginUser; 230 State left0 = StateFromY(gas, Y0_left); 231 State right0 = StateFromY(gas, Y0_right); 232 ScaleN(normal, 1 / Norm3(normal), 3); 233 234 for (int i = 0; i < 10; i++) { 235 CeedScalar dFlux[5] = {0.}, dFlux_fd[5] = {0.}; 236 { // Calculate dFlux using *_fwd function 237 CeedScalar dY_right[5] = {0}; 238 CeedScalar dY_left[5] = {0}; 239 240 if (i < 5) { 241 dY_left[i] = Y0_left[i]; 242 } else { 243 dY_right[i % 5] = Y0_right[i % 5]; 244 } 245 State dleft0 = StateFromY_fwd(gas, left0, dY_left); 246 State dright0 = StateFromY_fwd(gas, right0, dY_right); 247 248 StateConservative dFlux_state = RiemannFlux_HLL_fwd(gas, left0, dleft0, right0, dright0, normal); 249 UnpackState_U(dFlux_state, dFlux); 250 } 251 252 { // Calculate dFlux_fd via finite difference approximation 253 CeedScalar Y1_left[5] = {Y0_left[0], Y0_left[1], Y0_left[2], Y0_left[3], Y0_left[4]}; 254 CeedScalar Y1_right[5] = {Y0_right[0], Y0_right[1], Y0_right[2], Y0_right[3], Y0_right[4]}; 255 CeedScalar Flux0[5], Flux1[5]; 256 257 if (i < 5) { 258 Y1_left[i] *= 1 + eps; 259 } else { 260 Y1_right[i % 5] *= 1 + eps; 261 } 262 State left1 = StateFromY(gas, Y1_left); 263 State right1 = StateFromY(gas, Y1_right); 264 265 StateConservative Flux0_state = RiemannFlux_HLL(gas, left0, right0, normal); 266 StateConservative Flux1_state = RiemannFlux_HLL(gas, left1, right1, normal); 267 UnpackState_U(Flux0_state, Flux0); 268 UnpackState_U(Flux1_state, Flux1); 269 for (int j = 0; j < 5; j++) dFlux_fd[j] = (Flux1[j] - Flux0[j]) / eps; 270 } 271 272 snprintf(buf, sizeof buf, "RiemannFlux_HLL i=%d: Flux", i); 273 PetscCall(CheckQWithTolerance(dFlux_fd, dFlux, dFlux_fd, buf, rtol_0, rtol_u, rtol_4)); 274 } 275 PetscFunctionReturn(PETSC_SUCCESS); 276 } 277 278 // @brief Verify RiemannFlux_HLLC_fwd function against finite-difference approximation 279 static PetscErrorCode TestRiemannHLLC_fwd(NewtonianIGProperties gas, CeedScalar rtol_0, CeedScalar rtol_u, CeedScalar rtol_4) { 280 CeedScalar eps = 4e-7; // Finite difference step 281 char buf[128]; 282 const CeedScalar T = 200; 283 const CeedScalar rho = 1.2; 284 const CeedScalar p = (HeatCapacityRatio(gas) - 1) * rho * gas.cv * T; 285 const CeedScalar u_base = 40; 286 const CeedScalar u[3] = {u_base, u_base * 1.1, u_base * 1.2}; 287 const CeedScalar Y0_left[5] = {p, u[0], u[1], u[2], T}; 288 const CeedScalar Y0_right[5] = {1.2 * p, 1.2 * u[0], 1.2 * u[1], 1.2 * u[2], 1.2 * T}; 289 CeedScalar normal[3] = {1, 2, 3}; 290 291 PetscFunctionBeginUser; 292 State left0 = StateFromY(gas, Y0_left); 293 State right0 = StateFromY(gas, Y0_right); 294 ScaleN(normal, 1 / Norm3(normal), 3); 295 296 for (int i = 0; i < 10; i++) { 297 CeedScalar dFlux[5] = {0.}, dFlux_fd[5] = {0.}; 298 { // Calculate dFlux using *_fwd function 299 CeedScalar dY_right[5] = {0}; 300 CeedScalar dY_left[5] = {0}; 301 302 if (i < 5) { 303 dY_left[i] = Y0_left[i]; 304 } else { 305 dY_right[i % 5] = Y0_right[i % 5]; 306 } 307 State dleft0 = StateFromY_fwd(gas, left0, dY_left); 308 State dright0 = StateFromY_fwd(gas, right0, dY_right); 309 310 StateConservative dFlux_state = RiemannFlux_HLLC_fwd(gas, left0, dleft0, right0, dright0, normal); 311 UnpackState_U(dFlux_state, dFlux); 312 } 313 314 { // Calculate dFlux_fd via finite difference approximation 315 CeedScalar Y1_left[5] = {Y0_left[0], Y0_left[1], Y0_left[2], Y0_left[3], Y0_left[4]}; 316 CeedScalar Y1_right[5] = {Y0_right[0], Y0_right[1], Y0_right[2], Y0_right[3], Y0_right[4]}; 317 CeedScalar Flux0[5], Flux1[5]; 318 319 if (i < 5) { 320 Y1_left[i] *= 1 + eps; 321 } else { 322 Y1_right[i % 5] *= 1 + eps; 323 } 324 State left1 = StateFromY(gas, Y1_left); 325 State right1 = StateFromY(gas, Y1_right); 326 327 StateConservative Flux0_state = RiemannFlux_HLLC(gas, left0, right0, normal); 328 StateConservative Flux1_state = RiemannFlux_HLLC(gas, left1, right1, normal); 329 UnpackState_U(Flux0_state, Flux0); 330 UnpackState_U(Flux1_state, Flux1); 331 for (int j = 0; j < 5; j++) dFlux_fd[j] = (Flux1[j] - Flux0[j]) / eps; 332 } 333 334 snprintf(buf, sizeof buf, "RiemannFlux_HLLC i=%d: Flux", i); 335 PetscCall(CheckQWithTolerance(dFlux_fd, dFlux, dFlux_fd, buf, rtol_0, rtol_u, rtol_4)); 336 } 337 PetscFunctionReturn(PETSC_SUCCESS); 338 } 339 340 // @brief Verify ComputeHLLSpeeds_Roe_fwd function against finite-difference approximation 341 static PetscErrorCode TestComputeHLLSpeeds_Roe_fwd(NewtonianIGProperties gas, CeedScalar rtol) { 342 CeedScalar eps = 4e-7; // Finite difference step 343 char buf[128]; 344 const CeedScalar T = 200; 345 const CeedScalar rho = 1.2; 346 const CeedScalar p = (HeatCapacityRatio(gas) - 1) * rho * gas.cv * T; 347 const CeedScalar u_base = 40; 348 const CeedScalar u[3] = {u_base, u_base * 1.1, u_base * 1.2}; 349 const CeedScalar Y0_left[5] = {p, u[0], u[1], u[2], T}; 350 const CeedScalar Y0_right[5] = {1.2 * p, 1.2 * u[0], 1.2 * u[1], 1.2 * u[2], 1.2 * T}; 351 CeedScalar normal[3] = {1, 2, 3}; 352 353 PetscFunctionBeginUser; 354 State left0 = StateFromY(gas, Y0_left); 355 State right0 = StateFromY(gas, Y0_right); 356 ScaleN(normal, 1 / Norm3(normal), 3); 357 CeedScalar u_left0 = Dot3(left0.Y.velocity, normal); 358 CeedScalar u_right0 = Dot3(right0.Y.velocity, normal); 359 360 for (int i = 0; i < 10; i++) { 361 CeedScalar ds_left, ds_right, ds_left_fd, ds_right_fd; 362 { // Calculate ds_{left,right} using *_fwd function 363 CeedScalar dY_right[5] = {0}; 364 CeedScalar dY_left[5] = {0}; 365 366 if (i < 5) { 367 dY_left[i] = Y0_left[i]; 368 } else { 369 dY_right[i % 5] = Y0_right[i % 5]; 370 } 371 State dleft0 = StateFromY_fwd(gas, left0, dY_left); 372 State dright0 = StateFromY_fwd(gas, right0, dY_right); 373 CeedScalar du_left = Dot3(dleft0.Y.velocity, normal); 374 CeedScalar du_right = Dot3(dright0.Y.velocity, normal); 375 376 CeedScalar s_left, s_right; // Throw away 377 ComputeHLLSpeeds_Roe_fwd(gas, left0, dleft0, u_left0, du_left, right0, dright0, u_right0, du_right, &s_left, &ds_left, &s_right, &ds_right); 378 } 379 380 { // Calculate ds_{left,right}_fd via finite difference approximation 381 CeedScalar Y1_left[5] = {Y0_left[0], Y0_left[1], Y0_left[2], Y0_left[3], Y0_left[4]}; 382 CeedScalar Y1_right[5] = {Y0_right[0], Y0_right[1], Y0_right[2], Y0_right[3], Y0_right[4]}; 383 384 if (i < 5) { 385 Y1_left[i] *= 1 + eps; 386 } else { 387 Y1_right[i % 5] *= 1 + eps; 388 } 389 State left1 = StateFromY(gas, Y1_left); 390 State right1 = StateFromY(gas, Y1_right); 391 CeedScalar u_left1 = Dot3(left1.Y.velocity, normal); 392 CeedScalar u_right1 = Dot3(right1.Y.velocity, normal); 393 394 CeedScalar s_left0, s_right0, s_left1, s_right1; 395 ComputeHLLSpeeds_Roe(gas, left0, u_left0, right0, u_right0, &s_left0, &s_right0); 396 ComputeHLLSpeeds_Roe(gas, left1, u_left1, right1, u_right1, &s_left1, &s_right1); 397 ds_left_fd = (s_left1 - s_left0) / eps; 398 ds_right_fd = (s_right1 - s_right0) / eps; 399 } 400 401 snprintf(buf, sizeof buf, "ComputeHLLSpeeds_Roe i=%d:", i); 402 { 403 CeedScalar divisor_threshold = 10 * CEED_EPSILON; 404 CeedScalar ds_left_err, ds_right_err; 405 406 ds_left_err = RelativeError(ds_left_fd, ds_left, ds_left_fd, divisor_threshold); 407 ds_right_err = RelativeError(ds_right_fd, ds_right, ds_right_fd, divisor_threshold); 408 if (fabs(ds_left_err) >= rtol) printf("%s ds_left error %g (expected %.10e, got %.10e)\n", buf, ds_left_err, ds_left_fd, ds_left); 409 if (fabs(ds_right_err) >= rtol) printf("%s ds_right error %g (expected %.10e, got %.10e)\n", buf, ds_right_err, ds_right_fd, ds_right); 410 } 411 } 412 PetscFunctionReturn(PETSC_SUCCESS); 413 } 414 415 // @brief Verify TotalSpecificEnthalpy_fwd function against finite-difference approximation 416 static PetscErrorCode TestTotalSpecificEnthalpy_fwd(NewtonianIGProperties gas, CeedScalar rtol) { 417 CeedScalar eps = 4e-7; // Finite difference step 418 char buf[128]; 419 const CeedScalar T = 200; 420 const CeedScalar rho = 1.2; 421 const CeedScalar p = (HeatCapacityRatio(gas) - 1) * rho * gas.cv * T; 422 const CeedScalar u_base = 40; 423 const CeedScalar u[3] = {u_base, u_base * 1.1, u_base * 1.2}; 424 const CeedScalar Y0[5] = {p, u[0], u[1], u[2], T}; 425 426 PetscFunctionBeginUser; 427 State state0 = StateFromY(gas, Y0); 428 429 for (int i = 0; i < 5; i++) { 430 CeedScalar dH, dH_fd; 431 { // Calculate dH using *_fwd function 432 CeedScalar dY[5] = {0}; 433 434 dY[i] = Y0[i]; 435 State dstate0 = StateFromY_fwd(gas, state0, dY); 436 dH = TotalSpecificEnthalpy_fwd(gas, state0, dstate0); 437 } 438 439 { // Calculate dH_fd via finite difference approximation 440 CeedScalar H0, H1; 441 CeedScalar Y1[5] = {Y0[0], Y0[1], Y0[2], Y0[3], Y0[4]}; 442 Y1[i] *= 1 + eps; 443 State state1 = StateFromY(gas, Y1); 444 445 H0 = TotalSpecificEnthalpy(gas, state0); 446 H1 = TotalSpecificEnthalpy(gas, state1); 447 dH_fd = (H1 - H0) / eps; 448 } 449 450 snprintf(buf, sizeof buf, "TotalSpecificEnthalpy i=%d:", i); 451 { 452 CeedScalar divisor_threshold = 10 * CEED_EPSILON; 453 CeedScalar dH_err; 454 455 dH_err = RelativeError(dH_fd, dH, dH_fd, divisor_threshold); 456 if (fabs(dH_err) >= rtol) printf("%s dH error %g (expected %.10e, got %.10e)\n", buf, dH_err, dH_fd, dH); 457 } 458 } 459 PetscFunctionReturn(PETSC_SUCCESS); 460 } 461 462 // @brief Verify RoeSetup_fwd function against finite-difference approximation 463 static PetscErrorCode TestRowSetup_fwd(NewtonianIGProperties gas, CeedScalar rtol) { 464 CeedScalar eps = 4e-7; // Finite difference step 465 char buf[128]; 466 const CeedScalar rho0[2] = {1.2, 1.4}; 467 468 PetscFunctionBeginUser; 469 for (int i = 0; i < 2; i++) { 470 RoeWeights dR, dR_fd; 471 { // Calculate using *_fwd function 472 CeedScalar drho[5] = {0}; 473 474 drho[i] = rho0[i]; 475 dR = RoeSetup_fwd(rho0[0], rho0[1], drho[0], drho[1]); 476 } 477 478 { // Calculate via finite difference approximation 479 RoeWeights R0, R1; 480 CeedScalar rho1[5] = {rho0[0], rho0[1]}; 481 rho1[i] *= 1 + eps; 482 483 R0 = RoeSetup(rho0[0], rho0[1]); 484 R1 = RoeSetup(rho1[0], rho1[1]); 485 dR_fd.left = (R1.left - R0.left) / eps; 486 dR_fd.right = (R1.right - R0.right) / eps; 487 } 488 489 snprintf(buf, sizeof buf, "RoeSetup i=%d:", i); 490 { 491 CeedScalar divisor_threshold = 10 * CEED_EPSILON; 492 RoeWeights dR_err; 493 494 dR_err.left = RelativeError(dR_fd.left, dR.left, dR_fd.left, divisor_threshold); 495 dR_err.right = RelativeError(dR_fd.right, dR.right, dR_fd.right, divisor_threshold); 496 if (fabs(dR_err.left) >= rtol) printf("%s dR.left error %g (expected %.10e, got %.10e)\n", buf, dR_err.left, dR_fd.left, dR.left); 497 if (fabs(dR_err.right) >= rtol) printf("%s dR.right error %g (expected %.10e, got %.10e)\n", buf, dR_err.right, dR_fd.right, dR.right); 498 } 499 } 500 PetscFunctionReturn(PETSC_SUCCESS); 501 } 502 503 // @brief Test Riemann solver related `*_fwd` functions via finite-difference approximation 504 static PetscErrorCode RiemannSolverUnitTests(NewtonianIGProperties gas, CeedScalar rtol) { 505 PetscFunctionBeginUser; 506 PetscCall(TestRiemannHLL_fwd(gas, rtol, rtol, rtol)); 507 PetscCall(TestRiemannHLLC_fwd(gas, rtol, rtol, rtol)); 508 PetscCall(TestComputeHLLSpeeds_Roe_fwd(gas, rtol)); 509 PetscCall(TestTotalSpecificEnthalpy_fwd(gas, rtol)); 510 PetscCall(TestRowSetup_fwd(gas, rtol)); 511 PetscFunctionReturn(PETSC_SUCCESS); 512 } 513