1ae2b091fSJames Wright // SPDX-FileCopyrightText: Copyright (c) 2017-2024, HONEE contributors. 2ae2b091fSJames Wright // SPDX-License-Identifier: Apache-2.0 OR BSD-2-Clause 39ed3d70dSJames Wright 49ed3d70dSJames Wright /// @file 59ed3d70dSJames Wright /// Utility functions for setting up Freestream boundary condition 69ed3d70dSJames Wright 79ed3d70dSJames Wright #include "../qfunctions/bc_freestream.h" 89ed3d70dSJames Wright 99ed3d70dSJames Wright #include <ceed.h> 109ed3d70dSJames Wright #include <petscdm.h> 119ed3d70dSJames Wright 12149fb536SJames Wright #include <navierstokes.h> 139ed3d70dSJames Wright #include "../qfunctions/newtonian_types.h" 149ed3d70dSJames Wright 156dfcbb05SJames Wright static const char *const RiemannSolverTypes[] = {"HLL", "HLLC", "RiemannSolverTypes", "RIEMANN_", NULL}; 169ed3d70dSJames Wright 173e3353edSJames Wright static PetscErrorCode RiemannSolverUnitTests(NewtonianIdealGasContext gas, CeedScalar rtol); 183e3353edSJames Wright 19991aef52SJames Wright PetscErrorCode FreestreamBCSetup(ProblemData problem, DM dm, void *ctx, NewtonianIdealGasContext newtonian_ig_ctx, const StatePrimitive *reference) { 209ed3d70dSJames Wright User user = *(User *)ctx; 219ed3d70dSJames Wright MPI_Comm comm = user->comm; 229ed3d70dSJames Wright Ceed ceed = user->ceed; 239ed3d70dSJames Wright FreestreamContext freestream_ctx; 24*e07531f7SJames Wright CeedQFunctionContext freestream_qfctx; 259ed3d70dSJames Wright RiemannFluxType riemann = RIEMANN_HLLC; 269ed3d70dSJames Wright PetscScalar meter = user->units->meter; 279ed3d70dSJames Wright PetscScalar second = user->units->second; 289ed3d70dSJames Wright PetscScalar Kelvin = user->units->Kelvin; 299ed3d70dSJames Wright PetscScalar Pascal = user->units->Pascal; 309ed3d70dSJames Wright 319ed3d70dSJames Wright PetscFunctionBeginUser; 329ed3d70dSJames Wright // Freestream inherits reference state. We re-dimensionalize so the defaults 339ed3d70dSJames Wright // in -help will be visible in SI units. 349ed3d70dSJames Wright StatePrimitive Y_inf = {.pressure = reference->pressure / Pascal, .velocity = {0}, .temperature = reference->temperature / Kelvin}; 359ed3d70dSJames Wright for (int i = 0; i < 3; i++) Y_inf.velocity[i] = reference->velocity[i] * second / meter; 369ed3d70dSJames Wright 379ed3d70dSJames Wright PetscOptionsBegin(comm, NULL, "Options for Freestream boundary condition", NULL); 389ed3d70dSJames Wright PetscCall(PetscOptionsEnum("-freestream_riemann", "Riemann solver to use in freestream boundary condition", NULL, RiemannSolverTypes, 399ed3d70dSJames Wright (PetscEnum)riemann, (PetscEnum *)&riemann, NULL)); 409ed3d70dSJames Wright PetscCall(PetscOptionsScalar("-freestream_pressure", "Pressure at freestream condition", NULL, Y_inf.pressure, &Y_inf.pressure, NULL)); 419ed3d70dSJames Wright PetscInt narray = 3; 429ed3d70dSJames Wright PetscCall(PetscOptionsScalarArray("-freestream_velocity", "Velocity at freestream condition", NULL, Y_inf.velocity, &narray, NULL)); 439ed3d70dSJames Wright PetscCall(PetscOptionsScalar("-freestream_temperature", "Temperature at freestream condition", NULL, Y_inf.temperature, &Y_inf.temperature, NULL)); 449ed3d70dSJames Wright PetscOptionsEnd(); 459ed3d70dSJames Wright 469ed3d70dSJames Wright switch (user->phys->state_var) { 479ed3d70dSJames Wright case STATEVAR_CONSERVATIVE: 489ed3d70dSJames Wright switch (riemann) { 499ed3d70dSJames Wright case RIEMANN_HLL: 50*e07531f7SJames Wright problem->apply_freestream.qf_func_ptr = Freestream_Conserv_HLL; 51*e07531f7SJames Wright problem->apply_freestream.qf_loc = Freestream_Conserv_HLL_loc; 52*e07531f7SJames Wright problem->apply_freestream_jacobian.qf_func_ptr = Freestream_Jacobian_Conserv_HLL; 53*e07531f7SJames Wright problem->apply_freestream_jacobian.qf_loc = Freestream_Jacobian_Conserv_HLL_loc; 549ed3d70dSJames Wright break; 559ed3d70dSJames Wright case RIEMANN_HLLC: 56*e07531f7SJames Wright problem->apply_freestream.qf_func_ptr = Freestream_Conserv_HLLC; 57*e07531f7SJames Wright problem->apply_freestream.qf_loc = Freestream_Conserv_HLLC_loc; 58*e07531f7SJames Wright problem->apply_freestream_jacobian.qf_func_ptr = Freestream_Jacobian_Conserv_HLLC; 59*e07531f7SJames Wright problem->apply_freestream_jacobian.qf_loc = Freestream_Jacobian_Conserv_HLLC_loc; 609ed3d70dSJames Wright break; 619ed3d70dSJames Wright } 629ed3d70dSJames Wright break; 639ed3d70dSJames Wright case STATEVAR_PRIMITIVE: 649ed3d70dSJames Wright switch (riemann) { 659ed3d70dSJames Wright case RIEMANN_HLL: 66*e07531f7SJames Wright problem->apply_freestream.qf_func_ptr = Freestream_Prim_HLL; 67*e07531f7SJames Wright problem->apply_freestream.qf_loc = Freestream_Prim_HLL_loc; 68*e07531f7SJames Wright problem->apply_freestream_jacobian.qf_func_ptr = Freestream_Jacobian_Prim_HLL; 69*e07531f7SJames Wright problem->apply_freestream_jacobian.qf_loc = Freestream_Jacobian_Prim_HLL_loc; 709ed3d70dSJames Wright break; 719ed3d70dSJames Wright case RIEMANN_HLLC: 72*e07531f7SJames Wright problem->apply_freestream.qf_func_ptr = Freestream_Prim_HLLC; 73*e07531f7SJames Wright problem->apply_freestream.qf_loc = Freestream_Prim_HLLC_loc; 74*e07531f7SJames Wright problem->apply_freestream_jacobian.qf_func_ptr = Freestream_Jacobian_Prim_HLLC; 75*e07531f7SJames Wright problem->apply_freestream_jacobian.qf_loc = Freestream_Jacobian_Prim_HLLC_loc; 769ed3d70dSJames Wright break; 779ed3d70dSJames Wright } 789ed3d70dSJames Wright break; 799b103f75SJames Wright case STATEVAR_ENTROPY: 809b103f75SJames Wright switch (riemann) { 819b103f75SJames Wright case RIEMANN_HLL: 82*e07531f7SJames Wright problem->apply_freestream.qf_func_ptr = Freestream_Entropy_HLL; 83*e07531f7SJames Wright problem->apply_freestream.qf_loc = Freestream_Entropy_HLL_loc; 84*e07531f7SJames Wright problem->apply_freestream_jacobian.qf_func_ptr = Freestream_Jacobian_Entropy_HLL; 85*e07531f7SJames Wright problem->apply_freestream_jacobian.qf_loc = Freestream_Jacobian_Entropy_HLL_loc; 869b103f75SJames Wright break; 879b103f75SJames Wright case RIEMANN_HLLC: 88*e07531f7SJames Wright problem->apply_freestream.qf_func_ptr = Freestream_Entropy_HLLC; 89*e07531f7SJames Wright problem->apply_freestream.qf_loc = Freestream_Entropy_HLLC_loc; 90*e07531f7SJames Wright problem->apply_freestream_jacobian.qf_func_ptr = Freestream_Jacobian_Entropy_HLLC; 91*e07531f7SJames Wright problem->apply_freestream_jacobian.qf_loc = Freestream_Jacobian_Entropy_HLLC_loc; 929b103f75SJames Wright break; 939b103f75SJames Wright } 949b103f75SJames Wright break; 959ed3d70dSJames Wright } 969ed3d70dSJames Wright 979ed3d70dSJames Wright Y_inf.pressure *= Pascal; 989ed3d70dSJames Wright for (int i = 0; i < 3; i++) Y_inf.velocity[i] *= meter / second; 999ed3d70dSJames Wright Y_inf.temperature *= Kelvin; 1009ed3d70dSJames Wright 1019ed3d70dSJames Wright State S_infty = StateFromPrimitive(newtonian_ig_ctx, Y_inf); 1029ed3d70dSJames Wright 1039ed3d70dSJames Wright // -- Set freestream_ctx struct values 1049ed3d70dSJames Wright PetscCall(PetscCalloc1(1, &freestream_ctx)); 1059ed3d70dSJames Wright freestream_ctx->newtonian_ctx = *newtonian_ig_ctx; 1069ed3d70dSJames Wright freestream_ctx->S_infty = S_infty; 1079ed3d70dSJames Wright 108*e07531f7SJames Wright PetscCallCeed(ceed, CeedQFunctionContextCreate(user->ceed, &freestream_qfctx)); 109*e07531f7SJames Wright PetscCallCeed(ceed, CeedQFunctionContextSetData(freestream_qfctx, CEED_MEM_HOST, CEED_USE_POINTER, sizeof(*freestream_ctx), freestream_ctx)); 110*e07531f7SJames Wright PetscCallCeed(ceed, CeedQFunctionContextSetDataDestroy(freestream_qfctx, CEED_MEM_HOST, FreeContextPetsc)); 111*e07531f7SJames Wright problem->apply_freestream.qfctx = freestream_qfctx; 112*e07531f7SJames Wright PetscCallCeed(ceed, CeedQFunctionContextReferenceCopy(freestream_qfctx, &problem->apply_freestream_jacobian.qfctx)); 1133e3353edSJames Wright 1143e3353edSJames Wright { 1153e3353edSJames Wright PetscBool run_unit_tests = PETSC_FALSE; 1163e3353edSJames Wright 1173e3353edSJames Wright PetscCall(PetscOptionsGetBool(NULL, NULL, "-riemann_solver_unit_tests", &run_unit_tests, NULL)); 1183e3353edSJames Wright if (run_unit_tests) PetscCall(RiemannSolverUnitTests(newtonian_ig_ctx, 5e-7)); 1193e3353edSJames Wright } 1209ed3d70dSJames Wright PetscFunctionReturn(PETSC_SUCCESS); 1219ed3d70dSJames Wright } 1229ed3d70dSJames Wright 1239ed3d70dSJames Wright static const char *const OutflowTypes[] = {"RIEMANN", "PRESSURE", "OutflowType", "OUTFLOW_", NULL}; 1249ed3d70dSJames Wright typedef enum { 1259ed3d70dSJames Wright OUTFLOW_RIEMANN, 1269ed3d70dSJames Wright OUTFLOW_PRESSURE, 1279ed3d70dSJames Wright } OutflowType; 1289ed3d70dSJames Wright 129991aef52SJames Wright PetscErrorCode OutflowBCSetup(ProblemData problem, DM dm, void *ctx, NewtonianIdealGasContext newtonian_ig_ctx, const StatePrimitive *reference) { 1309ed3d70dSJames Wright User user = *(User *)ctx; 1319ed3d70dSJames Wright Ceed ceed = user->ceed; 1329ed3d70dSJames Wright OutflowContext outflow_ctx; 1339ed3d70dSJames Wright OutflowType outflow_type = OUTFLOW_RIEMANN; 134*e07531f7SJames Wright CeedQFunctionContext outflow_qfctx; 1359ed3d70dSJames Wright const PetscScalar Kelvin = user->units->Kelvin; 1369ed3d70dSJames Wright const PetscScalar Pascal = user->units->Pascal; 1379ed3d70dSJames Wright 1389ed3d70dSJames Wright PetscFunctionBeginUser; 1399ed3d70dSJames Wright CeedScalar pressure = reference->pressure / Pascal; 1409ed3d70dSJames Wright CeedScalar temperature = reference->temperature / Kelvin; 1419ed3d70dSJames Wright CeedScalar recirc = 1, softplus_velocity = 1e-2; 1429ed3d70dSJames Wright PetscOptionsBegin(user->comm, NULL, "Options for Outflow boundary condition", NULL); 1439ed3d70dSJames Wright PetscCall( 1449ed3d70dSJames Wright PetscOptionsEnum("-outflow_type", "Type of outflow condition", NULL, OutflowTypes, (PetscEnum)outflow_type, (PetscEnum *)&outflow_type, NULL)); 1459ed3d70dSJames Wright PetscCall(PetscOptionsScalar("-outflow_pressure", "Pressure at outflow condition", NULL, pressure, &pressure, NULL)); 1469ed3d70dSJames Wright if (outflow_type == OUTFLOW_RIEMANN) { 1479ed3d70dSJames Wright PetscCall(PetscOptionsScalar("-outflow_temperature", "Temperature at outflow condition", NULL, temperature, &temperature, NULL)); 1489ed3d70dSJames Wright PetscCall( 1499ed3d70dSJames Wright PetscOptionsReal("-outflow_recirc", "Fraction of recirculation to allow in exterior velocity state [0,1]", NULL, recirc, &recirc, NULL)); 1509ed3d70dSJames Wright PetscCall(PetscOptionsReal("-outflow_softplus_velocity", "Characteristic velocity of softplus regularization", NULL, softplus_velocity, 1519ed3d70dSJames Wright &softplus_velocity, NULL)); 1529ed3d70dSJames Wright } 1539ed3d70dSJames Wright PetscOptionsEnd(); 1549ed3d70dSJames Wright pressure *= Pascal; 1559ed3d70dSJames Wright temperature *= Kelvin; 1569ed3d70dSJames Wright 1579ed3d70dSJames Wright switch (outflow_type) { 1589ed3d70dSJames Wright case OUTFLOW_RIEMANN: 1599ed3d70dSJames Wright switch (user->phys->state_var) { 1609ed3d70dSJames Wright case STATEVAR_CONSERVATIVE: 161*e07531f7SJames Wright problem->apply_outflow.qf_func_ptr = RiemannOutflow_Conserv; 162*e07531f7SJames Wright problem->apply_outflow.qf_loc = RiemannOutflow_Conserv_loc; 163*e07531f7SJames Wright problem->apply_outflow_jacobian.qf_func_ptr = RiemannOutflow_Jacobian_Conserv; 164*e07531f7SJames Wright problem->apply_outflow_jacobian.qf_loc = RiemannOutflow_Jacobian_Conserv_loc; 1659ed3d70dSJames Wright break; 1669ed3d70dSJames Wright case STATEVAR_PRIMITIVE: 167*e07531f7SJames Wright problem->apply_outflow.qf_func_ptr = RiemannOutflow_Prim; 168*e07531f7SJames Wright problem->apply_outflow.qf_loc = RiemannOutflow_Prim_loc; 169*e07531f7SJames Wright problem->apply_outflow_jacobian.qf_func_ptr = RiemannOutflow_Jacobian_Prim; 170*e07531f7SJames Wright problem->apply_outflow_jacobian.qf_loc = RiemannOutflow_Jacobian_Prim_loc; 1719ed3d70dSJames Wright break; 1729b103f75SJames Wright case STATEVAR_ENTROPY: 173*e07531f7SJames Wright problem->apply_outflow.qf_func_ptr = RiemannOutflow_Entropy; 174*e07531f7SJames Wright problem->apply_outflow.qf_loc = RiemannOutflow_Entropy_loc; 175*e07531f7SJames Wright problem->apply_outflow_jacobian.qf_func_ptr = RiemannOutflow_Jacobian_Entropy; 176*e07531f7SJames Wright problem->apply_outflow_jacobian.qf_loc = RiemannOutflow_Jacobian_Entropy_loc; 1779b103f75SJames Wright break; 1789ed3d70dSJames Wright } 1799ed3d70dSJames Wright break; 1809ed3d70dSJames Wright case OUTFLOW_PRESSURE: 1819ed3d70dSJames Wright switch (user->phys->state_var) { 1829ed3d70dSJames Wright case STATEVAR_CONSERVATIVE: 183*e07531f7SJames Wright problem->apply_outflow.qf_func_ptr = PressureOutflow_Conserv; 184*e07531f7SJames Wright problem->apply_outflow.qf_loc = PressureOutflow_Conserv_loc; 185*e07531f7SJames Wright problem->apply_outflow_jacobian.qf_func_ptr = PressureOutflow_Jacobian_Conserv; 186*e07531f7SJames Wright problem->apply_outflow_jacobian.qf_loc = PressureOutflow_Jacobian_Conserv_loc; 1879ed3d70dSJames Wright break; 1889ed3d70dSJames Wright case STATEVAR_PRIMITIVE: 189*e07531f7SJames Wright problem->apply_outflow.qf_func_ptr = PressureOutflow_Prim; 190*e07531f7SJames Wright problem->apply_outflow.qf_loc = PressureOutflow_Prim_loc; 191*e07531f7SJames Wright problem->apply_outflow_jacobian.qf_func_ptr = PressureOutflow_Jacobian_Prim; 192*e07531f7SJames Wright problem->apply_outflow_jacobian.qf_loc = PressureOutflow_Jacobian_Prim_loc; 1939ed3d70dSJames Wright break; 1949b103f75SJames Wright case STATEVAR_ENTROPY: 195*e07531f7SJames Wright problem->apply_outflow.qf_func_ptr = PressureOutflow_Entropy; 196*e07531f7SJames Wright problem->apply_outflow.qf_loc = PressureOutflow_Entropy_loc; 197*e07531f7SJames Wright problem->apply_outflow_jacobian.qf_func_ptr = PressureOutflow_Jacobian_Entropy; 198*e07531f7SJames Wright problem->apply_outflow_jacobian.qf_loc = PressureOutflow_Jacobian_Entropy_loc; 1999b103f75SJames Wright break; 2009ed3d70dSJames Wright } 2019ed3d70dSJames Wright break; 2029ed3d70dSJames Wright } 2039ed3d70dSJames Wright PetscCall(PetscCalloc1(1, &outflow_ctx)); 2049ed3d70dSJames Wright outflow_ctx->gas = *newtonian_ig_ctx; 2059ed3d70dSJames Wright outflow_ctx->recirc = recirc; 2069ed3d70dSJames Wright outflow_ctx->softplus_velocity = softplus_velocity; 2079ed3d70dSJames Wright outflow_ctx->pressure = pressure; 2089ed3d70dSJames Wright outflow_ctx->temperature = temperature; 2099ed3d70dSJames Wright 210*e07531f7SJames Wright PetscCallCeed(ceed, CeedQFunctionContextCreate(user->ceed, &outflow_qfctx)); 211*e07531f7SJames Wright PetscCallCeed(ceed, CeedQFunctionContextSetData(outflow_qfctx, CEED_MEM_HOST, CEED_USE_POINTER, sizeof(*outflow_ctx), outflow_ctx)); 212*e07531f7SJames Wright PetscCallCeed(ceed, CeedQFunctionContextSetDataDestroy(outflow_qfctx, CEED_MEM_HOST, FreeContextPetsc)); 213*e07531f7SJames Wright problem->apply_outflow.qfctx = outflow_qfctx; 214*e07531f7SJames Wright PetscCallCeed(ceed, CeedQFunctionContextReferenceCopy(outflow_qfctx, &problem->apply_outflow_jacobian.qfctx)); 2159ed3d70dSJames Wright PetscFunctionReturn(PETSC_SUCCESS); 2169ed3d70dSJames Wright } 2173e3353edSJames Wright 2183e3353edSJames Wright // @brief Calculate relative error, (A - B) / S 2193e3353edSJames Wright // If S < threshold, then set S=1 2203e3353edSJames Wright static inline CeedScalar RelativeError(CeedScalar S, CeedScalar A, CeedScalar B, CeedScalar threshold) { 2213e3353edSJames Wright return (A - B) / (fabs(S) > threshold ? S : 1); 2223e3353edSJames Wright } 2233e3353edSJames Wright 2243e3353edSJames Wright // @brief Check errors of a State vector and print if above tolerance 2253e3353edSJames Wright static PetscErrorCode CheckQWithTolerance(const CeedScalar Q_s[5], const CeedScalar Q_a[5], const CeedScalar Q_b[5], const char *name, 2263e3353edSJames Wright PetscReal rtol_0, PetscReal rtol_u, PetscReal rtol_4) { 2273e3353edSJames Wright CeedScalar relative_error[5]; // relative error 2283e3353edSJames Wright CeedScalar divisor_threshold = 10 * CEED_EPSILON; 2293e3353edSJames Wright 2303e3353edSJames Wright PetscFunctionBeginUser; 2313e3353edSJames Wright relative_error[0] = RelativeError(Q_s[0], Q_a[0], Q_b[0], divisor_threshold); 2323e3353edSJames Wright relative_error[4] = RelativeError(Q_s[4], Q_a[4], Q_b[4], divisor_threshold); 2333e3353edSJames Wright 2343e3353edSJames Wright CeedScalar u_magnitude = sqrt(Square(Q_s[1]) + Square(Q_s[2]) + Square(Q_s[3])); 2353e3353edSJames Wright for (int i = 1; i < 4; i++) { 2363e3353edSJames Wright relative_error[i] = RelativeError(u_magnitude, Q_a[i], Q_b[i], divisor_threshold); 2373e3353edSJames Wright } 2383e3353edSJames Wright 2393e3353edSJames Wright if (fabs(relative_error[0]) >= rtol_0) { 2403e3353edSJames Wright printf("%s[0] error %g (expected %.10e, got %.10e)\n", name, relative_error[0], Q_s[0], Q_a[0]); 2413e3353edSJames Wright } 2423e3353edSJames Wright for (int i = 1; i < 4; i++) { 2433e3353edSJames Wright if (fabs(relative_error[i]) >= rtol_u) { 2443e3353edSJames Wright printf("%s[%d] error %g (expected %.10e, got %.10e)\n", name, i, relative_error[i], Q_s[i], Q_a[i]); 2453e3353edSJames Wright } 2463e3353edSJames Wright } 2473e3353edSJames Wright if (fabs(relative_error[4]) >= rtol_4) { 2483e3353edSJames Wright printf("%s[4] error %g (expected %.10e, got %.10e)\n", name, relative_error[4], Q_s[4], Q_a[4]); 2493e3353edSJames Wright } 2503e3353edSJames Wright PetscFunctionReturn(PETSC_SUCCESS); 2513e3353edSJames Wright } 2523e3353edSJames Wright 2533e3353edSJames Wright // @brief Verify RiemannFlux_HLL_fwd function against finite-difference approximation 2543e3353edSJames Wright static PetscErrorCode TestRiemannHLL_fwd(NewtonianIdealGasContext gas, CeedScalar rtol_0, CeedScalar rtol_u, CeedScalar rtol_4) { 2553e3353edSJames Wright CeedScalar eps = 4e-7; // Finite difference step 2563e3353edSJames Wright char buf[128]; 2573e3353edSJames Wright const CeedScalar T = 200; 2583e3353edSJames Wright const CeedScalar rho = 1.2; 2593e3353edSJames Wright const CeedScalar p = (HeatCapacityRatio(gas) - 1) * rho * gas->cv * T; 2603e3353edSJames Wright const CeedScalar u_base = 40; 2613e3353edSJames Wright const CeedScalar u[3] = {u_base, u_base * 1.1, u_base * 1.2}; 2623e3353edSJames Wright const CeedScalar Y0_left[5] = {p, u[0], u[1], u[2], T}; 2633e3353edSJames Wright const CeedScalar Y0_right[5] = {1.2 * p, 1.2 * u[0], 1.2 * u[1], 1.2 * u[2], 1.2 * T}; 2643e3353edSJames Wright CeedScalar normal[3] = {1, 2, 3}; 2653e3353edSJames Wright 2663e3353edSJames Wright PetscFunctionBeginUser; 2673e3353edSJames Wright State left0 = StateFromY(gas, Y0_left); 2683e3353edSJames Wright State right0 = StateFromY(gas, Y0_right); 26964667825SJames Wright ScaleN(normal, 1 / Norm3(normal), 3); 2703e3353edSJames Wright 2713e3353edSJames Wright for (int i = 0; i < 10; i++) { 2723e3353edSJames Wright CeedScalar dFlux[5] = {0.}, dFlux_fd[5] = {0.}; 2733e3353edSJames Wright { // Calculate dFlux using *_fwd function 2743e3353edSJames Wright CeedScalar dY_right[5] = {0}; 2753e3353edSJames Wright CeedScalar dY_left[5] = {0}; 2763e3353edSJames Wright 2773e3353edSJames Wright if (i < 5) { 2783e3353edSJames Wright dY_left[i] = Y0_left[i]; 2793e3353edSJames Wright } else { 2803e3353edSJames Wright dY_right[i % 5] = Y0_right[i % 5]; 2813e3353edSJames Wright } 2823e3353edSJames Wright State dleft0 = StateFromY_fwd(gas, left0, dY_left); 2833e3353edSJames Wright State dright0 = StateFromY_fwd(gas, right0, dY_right); 2843e3353edSJames Wright 2853e3353edSJames Wright StateConservative dFlux_state = RiemannFlux_HLL_fwd(gas, left0, dleft0, right0, dright0, normal); 2863e3353edSJames Wright UnpackState_U(dFlux_state, dFlux); 2873e3353edSJames Wright } 2883e3353edSJames Wright 2893e3353edSJames Wright { // Calculate dFlux_fd via finite difference approximation 2903e3353edSJames Wright CeedScalar Y1_left[5] = {Y0_left[0], Y0_left[1], Y0_left[2], Y0_left[3], Y0_left[4]}; 2913e3353edSJames Wright CeedScalar Y1_right[5] = {Y0_right[0], Y0_right[1], Y0_right[2], Y0_right[3], Y0_right[4]}; 2923e3353edSJames Wright CeedScalar Flux0[5], Flux1[5]; 2933e3353edSJames Wright 2943e3353edSJames Wright if (i < 5) { 2953e3353edSJames Wright Y1_left[i] *= 1 + eps; 2963e3353edSJames Wright } else { 2973e3353edSJames Wright Y1_right[i % 5] *= 1 + eps; 2983e3353edSJames Wright } 2993e3353edSJames Wright State left1 = StateFromY(gas, Y1_left); 3003e3353edSJames Wright State right1 = StateFromY(gas, Y1_right); 3013e3353edSJames Wright 3023e3353edSJames Wright StateConservative Flux0_state = RiemannFlux_HLL(gas, left0, right0, normal); 3033e3353edSJames Wright StateConservative Flux1_state = RiemannFlux_HLL(gas, left1, right1, normal); 3043e3353edSJames Wright UnpackState_U(Flux0_state, Flux0); 3053e3353edSJames Wright UnpackState_U(Flux1_state, Flux1); 3063e3353edSJames Wright for (int j = 0; j < 5; j++) dFlux_fd[j] = (Flux1[j] - Flux0[j]) / eps; 3073e3353edSJames Wright } 3083e3353edSJames Wright 3093e3353edSJames Wright snprintf(buf, sizeof buf, "RiemannFlux_HLL i=%d: Flux", i); 3103e3353edSJames Wright PetscCall(CheckQWithTolerance(dFlux_fd, dFlux, dFlux_fd, buf, rtol_0, rtol_u, rtol_4)); 3113e3353edSJames Wright } 3123e3353edSJames Wright PetscFunctionReturn(PETSC_SUCCESS); 3133e3353edSJames Wright } 3143e3353edSJames Wright 3153e3353edSJames Wright // @brief Verify RiemannFlux_HLLC_fwd function against finite-difference approximation 3163e3353edSJames Wright static PetscErrorCode TestRiemannHLLC_fwd(NewtonianIdealGasContext gas, CeedScalar rtol_0, CeedScalar rtol_u, CeedScalar rtol_4) { 3173e3353edSJames Wright CeedScalar eps = 4e-7; // Finite difference step 3183e3353edSJames Wright char buf[128]; 3193e3353edSJames Wright const CeedScalar T = 200; 3203e3353edSJames Wright const CeedScalar rho = 1.2; 3213e3353edSJames Wright const CeedScalar p = (HeatCapacityRatio(gas) - 1) * rho * gas->cv * T; 3223e3353edSJames Wright const CeedScalar u_base = 40; 3233e3353edSJames Wright const CeedScalar u[3] = {u_base, u_base * 1.1, u_base * 1.2}; 3243e3353edSJames Wright const CeedScalar Y0_left[5] = {p, u[0], u[1], u[2], T}; 3253e3353edSJames Wright const CeedScalar Y0_right[5] = {1.2 * p, 1.2 * u[0], 1.2 * u[1], 1.2 * u[2], 1.2 * T}; 3263e3353edSJames Wright CeedScalar normal[3] = {1, 2, 3}; 3273e3353edSJames Wright 3283e3353edSJames Wright PetscFunctionBeginUser; 3293e3353edSJames Wright State left0 = StateFromY(gas, Y0_left); 3303e3353edSJames Wright State right0 = StateFromY(gas, Y0_right); 33164667825SJames Wright ScaleN(normal, 1 / Norm3(normal), 3); 3323e3353edSJames Wright 3333e3353edSJames Wright for (int i = 0; i < 10; i++) { 3343e3353edSJames Wright CeedScalar dFlux[5] = {0.}, dFlux_fd[5] = {0.}; 3353e3353edSJames Wright { // Calculate dFlux using *_fwd function 3363e3353edSJames Wright CeedScalar dY_right[5] = {0}; 3373e3353edSJames Wright CeedScalar dY_left[5] = {0}; 3383e3353edSJames Wright 3393e3353edSJames Wright if (i < 5) { 3403e3353edSJames Wright dY_left[i] = Y0_left[i]; 3413e3353edSJames Wright } else { 3423e3353edSJames Wright dY_right[i % 5] = Y0_right[i % 5]; 3433e3353edSJames Wright } 3443e3353edSJames Wright State dleft0 = StateFromY_fwd(gas, left0, dY_left); 3453e3353edSJames Wright State dright0 = StateFromY_fwd(gas, right0, dY_right); 3463e3353edSJames Wright 3473e3353edSJames Wright StateConservative dFlux_state = RiemannFlux_HLLC_fwd(gas, left0, dleft0, right0, dright0, normal); 3483e3353edSJames Wright UnpackState_U(dFlux_state, dFlux); 3493e3353edSJames Wright } 3503e3353edSJames Wright 3513e3353edSJames Wright { // Calculate dFlux_fd via finite difference approximation 3523e3353edSJames Wright CeedScalar Y1_left[5] = {Y0_left[0], Y0_left[1], Y0_left[2], Y0_left[3], Y0_left[4]}; 3533e3353edSJames Wright CeedScalar Y1_right[5] = {Y0_right[0], Y0_right[1], Y0_right[2], Y0_right[3], Y0_right[4]}; 3543e3353edSJames Wright CeedScalar Flux0[5], Flux1[5]; 3553e3353edSJames Wright 3563e3353edSJames Wright if (i < 5) { 3573e3353edSJames Wright Y1_left[i] *= 1 + eps; 3583e3353edSJames Wright } else { 3593e3353edSJames Wright Y1_right[i % 5] *= 1 + eps; 3603e3353edSJames Wright } 3613e3353edSJames Wright State left1 = StateFromY(gas, Y1_left); 3623e3353edSJames Wright State right1 = StateFromY(gas, Y1_right); 3633e3353edSJames Wright 3643e3353edSJames Wright StateConservative Flux0_state = RiemannFlux_HLLC(gas, left0, right0, normal); 3653e3353edSJames Wright StateConservative Flux1_state = RiemannFlux_HLLC(gas, left1, right1, normal); 3663e3353edSJames Wright UnpackState_U(Flux0_state, Flux0); 3673e3353edSJames Wright UnpackState_U(Flux1_state, Flux1); 3683e3353edSJames Wright for (int j = 0; j < 5; j++) dFlux_fd[j] = (Flux1[j] - Flux0[j]) / eps; 3693e3353edSJames Wright } 3703e3353edSJames Wright 3713e3353edSJames Wright snprintf(buf, sizeof buf, "RiemannFlux_HLLC i=%d: Flux", i); 3723e3353edSJames Wright PetscCall(CheckQWithTolerance(dFlux_fd, dFlux, dFlux_fd, buf, rtol_0, rtol_u, rtol_4)); 3733e3353edSJames Wright } 3743e3353edSJames Wright PetscFunctionReturn(PETSC_SUCCESS); 3753e3353edSJames Wright } 3763e3353edSJames Wright 3773e3353edSJames Wright // @brief Verify ComputeHLLSpeeds_Roe_fwd function against finite-difference approximation 3783e3353edSJames Wright static PetscErrorCode TestComputeHLLSpeeds_Roe_fwd(NewtonianIdealGasContext gas, CeedScalar rtol) { 3793e3353edSJames Wright CeedScalar eps = 4e-7; // Finite difference step 3803e3353edSJames Wright char buf[128]; 3813e3353edSJames Wright const CeedScalar T = 200; 3823e3353edSJames Wright const CeedScalar rho = 1.2; 3833e3353edSJames Wright const CeedScalar p = (HeatCapacityRatio(gas) - 1) * rho * gas->cv * T; 3843e3353edSJames Wright const CeedScalar u_base = 40; 3853e3353edSJames Wright const CeedScalar u[3] = {u_base, u_base * 1.1, u_base * 1.2}; 3863e3353edSJames Wright const CeedScalar Y0_left[5] = {p, u[0], u[1], u[2], T}; 3873e3353edSJames Wright const CeedScalar Y0_right[5] = {1.2 * p, 1.2 * u[0], 1.2 * u[1], 1.2 * u[2], 1.2 * T}; 3883e3353edSJames Wright CeedScalar normal[3] = {1, 2, 3}; 3893e3353edSJames Wright 3903e3353edSJames Wright PetscFunctionBeginUser; 3913e3353edSJames Wright State left0 = StateFromY(gas, Y0_left); 3923e3353edSJames Wright State right0 = StateFromY(gas, Y0_right); 39364667825SJames Wright ScaleN(normal, 1 / Norm3(normal), 3); 3943e3353edSJames Wright CeedScalar u_left0 = Dot3(left0.Y.velocity, normal); 3953e3353edSJames Wright CeedScalar u_right0 = Dot3(right0.Y.velocity, normal); 3963e3353edSJames Wright 3973e3353edSJames Wright for (int i = 0; i < 10; i++) { 3983e3353edSJames Wright CeedScalar ds_left, ds_right, ds_left_fd, ds_right_fd; 3993e3353edSJames Wright { // Calculate ds_{left,right} using *_fwd function 4003e3353edSJames Wright CeedScalar dY_right[5] = {0}; 4013e3353edSJames Wright CeedScalar dY_left[5] = {0}; 4023e3353edSJames Wright 4033e3353edSJames Wright if (i < 5) { 4043e3353edSJames Wright dY_left[i] = Y0_left[i]; 4053e3353edSJames Wright } else { 4063e3353edSJames Wright dY_right[i % 5] = Y0_right[i % 5]; 4073e3353edSJames Wright } 4083e3353edSJames Wright State dleft0 = StateFromY_fwd(gas, left0, dY_left); 4093e3353edSJames Wright State dright0 = StateFromY_fwd(gas, right0, dY_right); 4103e3353edSJames Wright CeedScalar du_left = Dot3(dleft0.Y.velocity, normal); 4113e3353edSJames Wright CeedScalar du_right = Dot3(dright0.Y.velocity, normal); 4123e3353edSJames Wright 4133e3353edSJames Wright CeedScalar s_left, s_right; // Throw away 4143e3353edSJames Wright ComputeHLLSpeeds_Roe_fwd(gas, left0, dleft0, u_left0, du_left, right0, dright0, u_right0, du_right, &s_left, &ds_left, &s_right, &ds_right); 4153e3353edSJames Wright } 4163e3353edSJames Wright 4173e3353edSJames Wright { // Calculate ds_{left,right}_fd via finite difference approximation 4183e3353edSJames Wright CeedScalar Y1_left[5] = {Y0_left[0], Y0_left[1], Y0_left[2], Y0_left[3], Y0_left[4]}; 4193e3353edSJames Wright CeedScalar Y1_right[5] = {Y0_right[0], Y0_right[1], Y0_right[2], Y0_right[3], Y0_right[4]}; 4203e3353edSJames Wright 4213e3353edSJames Wright if (i < 5) { 4223e3353edSJames Wright Y1_left[i] *= 1 + eps; 4233e3353edSJames Wright } else { 4243e3353edSJames Wright Y1_right[i % 5] *= 1 + eps; 4253e3353edSJames Wright } 4263e3353edSJames Wright State left1 = StateFromY(gas, Y1_left); 4273e3353edSJames Wright State right1 = StateFromY(gas, Y1_right); 4283e3353edSJames Wright CeedScalar u_left1 = Dot3(left1.Y.velocity, normal); 4293e3353edSJames Wright CeedScalar u_right1 = Dot3(right1.Y.velocity, normal); 4303e3353edSJames Wright 4313e3353edSJames Wright CeedScalar s_left0, s_right0, s_left1, s_right1; 4323e3353edSJames Wright ComputeHLLSpeeds_Roe(gas, left0, u_left0, right0, u_right0, &s_left0, &s_right0); 4333e3353edSJames Wright ComputeHLLSpeeds_Roe(gas, left1, u_left1, right1, u_right1, &s_left1, &s_right1); 4343e3353edSJames Wright ds_left_fd = (s_left1 - s_left0) / eps; 4353e3353edSJames Wright ds_right_fd = (s_right1 - s_right0) / eps; 4363e3353edSJames Wright } 4373e3353edSJames Wright 4383e3353edSJames Wright snprintf(buf, sizeof buf, "ComputeHLLSpeeds_Roe i=%d:", i); 4393e3353edSJames Wright { 4403e3353edSJames Wright CeedScalar divisor_threshold = 10 * CEED_EPSILON; 4413e3353edSJames Wright CeedScalar ds_left_err, ds_right_err; 4423e3353edSJames Wright 4433e3353edSJames Wright ds_left_err = RelativeError(ds_left_fd, ds_left, ds_left_fd, divisor_threshold); 4443e3353edSJames Wright ds_right_err = RelativeError(ds_right_fd, ds_right, ds_right_fd, divisor_threshold); 4453e3353edSJames Wright 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); 4463e3353edSJames Wright 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); 4473e3353edSJames Wright } 4483e3353edSJames Wright } 4493e3353edSJames Wright PetscFunctionReturn(PETSC_SUCCESS); 4503e3353edSJames Wright } 4513e3353edSJames Wright 4523e3353edSJames Wright // @brief Verify TotalSpecificEnthalpy_fwd function against finite-difference approximation 4533e3353edSJames Wright static PetscErrorCode TestTotalSpecificEnthalpy_fwd(NewtonianIdealGasContext gas, CeedScalar rtol) { 4543e3353edSJames Wright CeedScalar eps = 4e-7; // Finite difference step 4553e3353edSJames Wright char buf[128]; 4563e3353edSJames Wright const CeedScalar T = 200; 4573e3353edSJames Wright const CeedScalar rho = 1.2; 4583e3353edSJames Wright const CeedScalar p = (HeatCapacityRatio(gas) - 1) * rho * gas->cv * T; 4593e3353edSJames Wright const CeedScalar u_base = 40; 4603e3353edSJames Wright const CeedScalar u[3] = {u_base, u_base * 1.1, u_base * 1.2}; 4613e3353edSJames Wright const CeedScalar Y0[5] = {p, u[0], u[1], u[2], T}; 4623e3353edSJames Wright 4633e3353edSJames Wright PetscFunctionBeginUser; 4643e3353edSJames Wright State state0 = StateFromY(gas, Y0); 4653e3353edSJames Wright 4663e3353edSJames Wright for (int i = 0; i < 5; i++) { 4673e3353edSJames Wright CeedScalar dH, dH_fd; 4683e3353edSJames Wright { // Calculate dH using *_fwd function 4693e3353edSJames Wright CeedScalar dY[5] = {0}; 4703e3353edSJames Wright 4713e3353edSJames Wright dY[i] = Y0[i]; 4723e3353edSJames Wright State dstate0 = StateFromY_fwd(gas, state0, dY); 4733e3353edSJames Wright dH = TotalSpecificEnthalpy_fwd(gas, state0, dstate0); 4743e3353edSJames Wright } 4753e3353edSJames Wright 4763e3353edSJames Wright { // Calculate dH_fd via finite difference approximation 4773e3353edSJames Wright CeedScalar H0, H1; 4783e3353edSJames Wright CeedScalar Y1[5] = {Y0[0], Y0[1], Y0[2], Y0[3], Y0[4]}; 4793e3353edSJames Wright Y1[i] *= 1 + eps; 4803e3353edSJames Wright State state1 = StateFromY(gas, Y1); 4813e3353edSJames Wright 4823e3353edSJames Wright H0 = TotalSpecificEnthalpy(gas, state0); 4833e3353edSJames Wright H1 = TotalSpecificEnthalpy(gas, state1); 4843e3353edSJames Wright dH_fd = (H1 - H0) / eps; 4853e3353edSJames Wright } 4863e3353edSJames Wright 4873e3353edSJames Wright snprintf(buf, sizeof buf, "TotalSpecificEnthalpy i=%d:", i); 4883e3353edSJames Wright { 4893e3353edSJames Wright CeedScalar divisor_threshold = 10 * CEED_EPSILON; 4903e3353edSJames Wright CeedScalar dH_err; 4913e3353edSJames Wright 4923e3353edSJames Wright dH_err = RelativeError(dH_fd, dH, dH_fd, divisor_threshold); 4933e3353edSJames Wright if (fabs(dH_err) >= rtol) printf("%s dH error %g (expected %.10e, got %.10e)\n", buf, dH_err, dH_fd, dH); 4943e3353edSJames Wright } 4953e3353edSJames Wright } 4963e3353edSJames Wright PetscFunctionReturn(PETSC_SUCCESS); 4973e3353edSJames Wright } 4983e3353edSJames Wright 4993e3353edSJames Wright // @brief Verify RoeSetup_fwd function against finite-difference approximation 5003e3353edSJames Wright static PetscErrorCode TestRowSetup_fwd(NewtonianIdealGasContext gas, CeedScalar rtol) { 5013e3353edSJames Wright CeedScalar eps = 4e-7; // Finite difference step 5023e3353edSJames Wright char buf[128]; 5033e3353edSJames Wright const CeedScalar rho0[2] = {1.2, 1.4}; 5043e3353edSJames Wright 5053e3353edSJames Wright PetscFunctionBeginUser; 5063e3353edSJames Wright for (int i = 0; i < 2; i++) { 5073e3353edSJames Wright RoeWeights dR, dR_fd; 5083e3353edSJames Wright { // Calculate using *_fwd function 5093e3353edSJames Wright CeedScalar drho[5] = {0}; 5103e3353edSJames Wright 5113e3353edSJames Wright drho[i] = rho0[i]; 5123e3353edSJames Wright dR = RoeSetup_fwd(rho0[0], rho0[1], drho[0], drho[1]); 5133e3353edSJames Wright } 5143e3353edSJames Wright 5153e3353edSJames Wright { // Calculate via finite difference approximation 5163e3353edSJames Wright RoeWeights R0, R1; 5173e3353edSJames Wright CeedScalar rho1[5] = {rho0[0], rho0[1]}; 5183e3353edSJames Wright rho1[i] *= 1 + eps; 5193e3353edSJames Wright 5203e3353edSJames Wright R0 = RoeSetup(rho0[0], rho0[1]); 5213e3353edSJames Wright R1 = RoeSetup(rho1[0], rho1[1]); 5223e3353edSJames Wright dR_fd.left = (R1.left - R0.left) / eps; 5233e3353edSJames Wright dR_fd.right = (R1.right - R0.right) / eps; 5243e3353edSJames Wright } 5253e3353edSJames Wright 5263e3353edSJames Wright snprintf(buf, sizeof buf, "RoeSetup i=%d:", i); 5273e3353edSJames Wright { 5283e3353edSJames Wright CeedScalar divisor_threshold = 10 * CEED_EPSILON; 5293e3353edSJames Wright RoeWeights dR_err; 5303e3353edSJames Wright 5313e3353edSJames Wright dR_err.left = RelativeError(dR_fd.left, dR.left, dR_fd.left, divisor_threshold); 5323e3353edSJames Wright dR_err.right = RelativeError(dR_fd.right, dR.right, dR_fd.right, divisor_threshold); 5333e3353edSJames Wright 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); 5343e3353edSJames Wright 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); 5353e3353edSJames Wright } 5363e3353edSJames Wright } 5373e3353edSJames Wright PetscFunctionReturn(PETSC_SUCCESS); 5383e3353edSJames Wright } 5393e3353edSJames Wright 5403e3353edSJames Wright // @brief Test Riemann solver related `*_fwd` functions via finite-difference approximation 5413e3353edSJames Wright static PetscErrorCode RiemannSolverUnitTests(NewtonianIdealGasContext gas, CeedScalar rtol) { 5423e3353edSJames Wright PetscFunctionBeginUser; 5433e3353edSJames Wright PetscCall(TestRiemannHLL_fwd(gas, rtol, rtol, rtol)); 5443e3353edSJames Wright PetscCall(TestRiemannHLLC_fwd(gas, rtol, rtol, rtol)); 5453e3353edSJames Wright PetscCall(TestComputeHLLSpeeds_Roe_fwd(gas, rtol)); 5463e3353edSJames Wright PetscCall(TestTotalSpecificEnthalpy_fwd(gas, rtol)); 5473e3353edSJames Wright PetscCall(TestRowSetup_fwd(gas, rtol)); 5483e3353edSJames Wright PetscFunctionReturn(PETSC_SUCCESS); 5493e3353edSJames Wright } 550