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