1*9ba83ac0SJeremy L Thompson // Copyright (c) 2017-2026, Lawrence Livermore National Security, LLC and other CEED contributors.
29f844368SJames Wright // All Rights Reserved. See the top-level LICENSE and NOTICE files for details.
39f844368SJames Wright //
49f844368SJames Wright // SPDX-License-Identifier: BSD-2-Clause
59f844368SJames Wright //
69f844368SJames Wright // This file is part of CEED: http://github.com/ceed
79f844368SJames Wright
89f844368SJames Wright /// @file
99f844368SJames Wright /// Utility functions for setting up Freestream boundary condition
109f844368SJames Wright
119f844368SJames Wright #include "../qfunctions/bc_freestream.h"
129f844368SJames Wright
139f844368SJames Wright #include <ceed.h>
149f844368SJames Wright #include <petscdm.h>
159f844368SJames Wright
169f844368SJames Wright #include "../navierstokes.h"
179f844368SJames Wright #include "../qfunctions/newtonian_types.h"
189f844368SJames Wright
19fc39c77eSJames Wright static const char *const RiemannSolverTypes[] = {"HLL", "HLLC", "RiemannSolverTypes", "RIEMANN_", NULL};
209f844368SJames Wright
21aedeac77SJames Wright static PetscErrorCode RiemannSolverUnitTests(NewtonianIdealGasContext gas, CeedScalar rtol);
22aedeac77SJames Wright
FreestreamBCSetup(ProblemData problem,DM dm,void * ctx,NewtonianIdealGasContext newtonian_ig_ctx,const StatePrimitive * reference)23731c13d7SJames Wright PetscErrorCode FreestreamBCSetup(ProblemData problem, DM dm, void *ctx, NewtonianIdealGasContext newtonian_ig_ctx, const StatePrimitive *reference) {
249f844368SJames Wright User user = *(User *)ctx;
259f844368SJames Wright MPI_Comm comm = user->comm;
269f844368SJames Wright Ceed ceed = user->ceed;
279f844368SJames Wright FreestreamContext freestream_ctx;
289f844368SJames Wright CeedQFunctionContext freestream_context;
299f844368SJames Wright RiemannFluxType riemann = RIEMANN_HLLC;
309f844368SJames Wright PetscScalar meter = user->units->meter;
319f844368SJames Wright PetscScalar second = user->units->second;
329f844368SJames Wright PetscScalar Kelvin = user->units->Kelvin;
339f844368SJames Wright PetscScalar Pascal = user->units->Pascal;
349f844368SJames Wright
359f844368SJames Wright PetscFunctionBeginUser;
369f844368SJames Wright // Freestream inherits reference state. We re-dimensionalize so the defaults
379f844368SJames Wright // in -help will be visible in SI units.
389f844368SJames Wright StatePrimitive Y_inf = {.pressure = reference->pressure / Pascal, .velocity = {0}, .temperature = reference->temperature / Kelvin};
399f844368SJames Wright for (int i = 0; i < 3; i++) Y_inf.velocity[i] = reference->velocity[i] * second / meter;
409f844368SJames Wright
419f844368SJames Wright PetscOptionsBegin(comm, NULL, "Options for Freestream boundary condition", NULL);
429f844368SJames Wright PetscCall(PetscOptionsEnum("-freestream_riemann", "Riemann solver to use in freestream boundary condition", NULL, RiemannSolverTypes,
439f844368SJames Wright (PetscEnum)riemann, (PetscEnum *)&riemann, NULL));
449f844368SJames Wright PetscCall(PetscOptionsScalar("-freestream_pressure", "Pressure at freestream condition", NULL, Y_inf.pressure, &Y_inf.pressure, NULL));
459f844368SJames Wright PetscInt narray = 3;
469f844368SJames Wright PetscCall(PetscOptionsScalarArray("-freestream_velocity", "Velocity at freestream condition", NULL, Y_inf.velocity, &narray, NULL));
479f844368SJames Wright PetscCall(PetscOptionsScalar("-freestream_temperature", "Temperature at freestream condition", NULL, Y_inf.temperature, &Y_inf.temperature, NULL));
489f844368SJames Wright PetscOptionsEnd();
499f844368SJames Wright
509f844368SJames Wright switch (user->phys->state_var) {
519f844368SJames Wright case STATEVAR_CONSERVATIVE:
529f844368SJames Wright switch (riemann) {
539f844368SJames Wright case RIEMANN_HLL:
549f844368SJames Wright problem->apply_freestream.qfunction = Freestream_Conserv_HLL;
559f844368SJames Wright problem->apply_freestream.qfunction_loc = Freestream_Conserv_HLL_loc;
569f844368SJames Wright problem->apply_freestream_jacobian.qfunction = Freestream_Jacobian_Conserv_HLL;
579f844368SJames Wright problem->apply_freestream_jacobian.qfunction_loc = Freestream_Jacobian_Conserv_HLL_loc;
589f844368SJames Wright break;
599f844368SJames Wright case RIEMANN_HLLC:
609f844368SJames Wright problem->apply_freestream.qfunction = Freestream_Conserv_HLLC;
619f844368SJames Wright problem->apply_freestream.qfunction_loc = Freestream_Conserv_HLLC_loc;
629f844368SJames Wright problem->apply_freestream_jacobian.qfunction = Freestream_Jacobian_Conserv_HLLC;
639f844368SJames Wright problem->apply_freestream_jacobian.qfunction_loc = Freestream_Jacobian_Conserv_HLLC_loc;
649f844368SJames Wright break;
659f844368SJames Wright }
669f844368SJames Wright break;
679f844368SJames Wright case STATEVAR_PRIMITIVE:
689f844368SJames Wright switch (riemann) {
699f844368SJames Wright case RIEMANN_HLL:
709f844368SJames Wright problem->apply_freestream.qfunction = Freestream_Prim_HLL;
719f844368SJames Wright problem->apply_freestream.qfunction_loc = Freestream_Prim_HLL_loc;
729f844368SJames Wright problem->apply_freestream_jacobian.qfunction = Freestream_Jacobian_Prim_HLL;
739f844368SJames Wright problem->apply_freestream_jacobian.qfunction_loc = Freestream_Jacobian_Prim_HLL_loc;
749f844368SJames Wright break;
759f844368SJames Wright case RIEMANN_HLLC:
769f844368SJames Wright problem->apply_freestream.qfunction = Freestream_Prim_HLLC;
779f844368SJames Wright problem->apply_freestream.qfunction_loc = Freestream_Prim_HLLC_loc;
789f844368SJames Wright problem->apply_freestream_jacobian.qfunction = Freestream_Jacobian_Prim_HLLC;
799f844368SJames Wright problem->apply_freestream_jacobian.qfunction_loc = Freestream_Jacobian_Prim_HLLC_loc;
809f844368SJames Wright break;
819f844368SJames Wright }
829f844368SJames Wright break;
83a2d72b6fSJames Wright case STATEVAR_ENTROPY:
84a2d72b6fSJames Wright switch (riemann) {
85a2d72b6fSJames Wright case RIEMANN_HLL:
86a2d72b6fSJames Wright problem->apply_freestream.qfunction = Freestream_Entropy_HLL;
87a2d72b6fSJames Wright problem->apply_freestream.qfunction_loc = Freestream_Entropy_HLL_loc;
88a2d72b6fSJames Wright problem->apply_freestream_jacobian.qfunction = Freestream_Jacobian_Entropy_HLL;
89a2d72b6fSJames Wright problem->apply_freestream_jacobian.qfunction_loc = Freestream_Jacobian_Entropy_HLL_loc;
90a2d72b6fSJames Wright break;
91a2d72b6fSJames Wright case RIEMANN_HLLC:
92a2d72b6fSJames Wright problem->apply_freestream.qfunction = Freestream_Entropy_HLLC;
93a2d72b6fSJames Wright problem->apply_freestream.qfunction_loc = Freestream_Entropy_HLLC_loc;
94a2d72b6fSJames Wright problem->apply_freestream_jacobian.qfunction = Freestream_Jacobian_Entropy_HLLC;
95a2d72b6fSJames Wright problem->apply_freestream_jacobian.qfunction_loc = Freestream_Jacobian_Entropy_HLLC_loc;
96a2d72b6fSJames Wright break;
97a2d72b6fSJames Wright }
98a2d72b6fSJames Wright break;
999f844368SJames Wright }
1009f844368SJames Wright
1019f844368SJames Wright Y_inf.pressure *= Pascal;
1029f844368SJames Wright for (int i = 0; i < 3; i++) Y_inf.velocity[i] *= meter / second;
1039f844368SJames Wright Y_inf.temperature *= Kelvin;
1049f844368SJames Wright
1059f844368SJames Wright State S_infty = StateFromPrimitive(newtonian_ig_ctx, Y_inf);
1069f844368SJames Wright
1079f844368SJames Wright // -- Set freestream_ctx struct values
1089f844368SJames Wright PetscCall(PetscCalloc1(1, &freestream_ctx));
1099f844368SJames Wright freestream_ctx->newtonian_ctx = *newtonian_ig_ctx;
1109f844368SJames Wright freestream_ctx->S_infty = S_infty;
1119f844368SJames Wright
1129f844368SJames Wright PetscCallCeed(ceed, CeedQFunctionContextCreate(user->ceed, &freestream_context));
1139f844368SJames Wright PetscCallCeed(ceed, CeedQFunctionContextSetData(freestream_context, CEED_MEM_HOST, CEED_USE_POINTER, sizeof(*freestream_ctx), freestream_ctx));
1149f844368SJames Wright PetscCallCeed(ceed, CeedQFunctionContextSetDataDestroy(freestream_context, CEED_MEM_HOST, FreeContextPetsc));
1159f844368SJames Wright problem->apply_freestream.qfunction_context = freestream_context;
1169f844368SJames Wright PetscCallCeed(ceed, CeedQFunctionContextReferenceCopy(freestream_context, &problem->apply_freestream_jacobian.qfunction_context));
117aedeac77SJames Wright
118aedeac77SJames Wright {
119aedeac77SJames Wright PetscBool run_unit_tests = PETSC_FALSE;
120aedeac77SJames Wright
121aedeac77SJames Wright PetscCall(PetscOptionsGetBool(NULL, NULL, "-riemann_solver_unit_tests", &run_unit_tests, NULL));
122aedeac77SJames Wright if (run_unit_tests) PetscCall(RiemannSolverUnitTests(newtonian_ig_ctx, 5e-7));
123aedeac77SJames Wright }
1249f844368SJames Wright PetscFunctionReturn(PETSC_SUCCESS);
1259f844368SJames Wright }
1269f844368SJames Wright
1279f844368SJames Wright static const char *const OutflowTypes[] = {"RIEMANN", "PRESSURE", "OutflowType", "OUTFLOW_", NULL};
1289f844368SJames Wright typedef enum {
1299f844368SJames Wright OUTFLOW_RIEMANN,
1309f844368SJames Wright OUTFLOW_PRESSURE,
1319f844368SJames Wright } OutflowType;
1329f844368SJames Wright
OutflowBCSetup(ProblemData problem,DM dm,void * ctx,NewtonianIdealGasContext newtonian_ig_ctx,const StatePrimitive * reference)133731c13d7SJames Wright PetscErrorCode OutflowBCSetup(ProblemData problem, DM dm, void *ctx, NewtonianIdealGasContext newtonian_ig_ctx, const StatePrimitive *reference) {
1349f844368SJames Wright User user = *(User *)ctx;
1359f844368SJames Wright Ceed ceed = user->ceed;
1369f844368SJames Wright OutflowContext outflow_ctx;
1379f844368SJames Wright OutflowType outflow_type = OUTFLOW_RIEMANN;
1389f844368SJames Wright CeedQFunctionContext outflow_context;
1399f844368SJames Wright const PetscScalar Kelvin = user->units->Kelvin;
1409f844368SJames Wright const PetscScalar Pascal = user->units->Pascal;
1419f844368SJames Wright
1429f844368SJames Wright PetscFunctionBeginUser;
1439f844368SJames Wright CeedScalar pressure = reference->pressure / Pascal;
1449f844368SJames Wright CeedScalar temperature = reference->temperature / Kelvin;
1459f844368SJames Wright CeedScalar recirc = 1, softplus_velocity = 1e-2;
1469f844368SJames Wright PetscOptionsBegin(user->comm, NULL, "Options for Outflow boundary condition", NULL);
1471a8516d0SJames Wright PetscCall(PetscOptionsEnum("-outflow_type", "Type of outflow condition", NULL, OutflowTypes, (PetscEnum)outflow_type, (PetscEnum *)&outflow_type,
1481a8516d0SJames Wright NULL));
1499f844368SJames Wright PetscCall(PetscOptionsScalar("-outflow_pressure", "Pressure at outflow condition", NULL, pressure, &pressure, NULL));
1509f844368SJames Wright if (outflow_type == OUTFLOW_RIEMANN) {
1519f844368SJames Wright PetscCall(PetscOptionsScalar("-outflow_temperature", "Temperature at outflow condition", NULL, temperature, &temperature, NULL));
1521a8516d0SJames Wright PetscCall(PetscOptionsReal("-outflow_recirc", "Fraction of recirculation to allow in exterior velocity state [0,1]", NULL, recirc, &recirc,
1531a8516d0SJames Wright NULL));
1549f844368SJames Wright PetscCall(PetscOptionsReal("-outflow_softplus_velocity", "Characteristic velocity of softplus regularization", NULL, softplus_velocity,
1559f844368SJames Wright &softplus_velocity, NULL));
1569f844368SJames Wright }
1579f844368SJames Wright PetscOptionsEnd();
1589f844368SJames Wright pressure *= Pascal;
1599f844368SJames Wright temperature *= Kelvin;
1609f844368SJames Wright
1619f844368SJames Wright switch (outflow_type) {
1629f844368SJames Wright case OUTFLOW_RIEMANN:
1639f844368SJames Wright switch (user->phys->state_var) {
1649f844368SJames Wright case STATEVAR_CONSERVATIVE:
1659f844368SJames Wright problem->apply_outflow.qfunction = RiemannOutflow_Conserv;
1669f844368SJames Wright problem->apply_outflow.qfunction_loc = RiemannOutflow_Conserv_loc;
1679f844368SJames Wright problem->apply_outflow_jacobian.qfunction = RiemannOutflow_Jacobian_Conserv;
1689f844368SJames Wright problem->apply_outflow_jacobian.qfunction_loc = RiemannOutflow_Jacobian_Conserv_loc;
1699f844368SJames Wright break;
1709f844368SJames Wright case STATEVAR_PRIMITIVE:
1719f844368SJames Wright problem->apply_outflow.qfunction = RiemannOutflow_Prim;
1729f844368SJames Wright problem->apply_outflow.qfunction_loc = RiemannOutflow_Prim_loc;
1739f844368SJames Wright problem->apply_outflow_jacobian.qfunction = RiemannOutflow_Jacobian_Prim;
1749f844368SJames Wright problem->apply_outflow_jacobian.qfunction_loc = RiemannOutflow_Jacobian_Prim_loc;
1759f844368SJames Wright break;
176a2d72b6fSJames Wright case STATEVAR_ENTROPY:
177a2d72b6fSJames Wright problem->apply_outflow.qfunction = RiemannOutflow_Entropy;
178a2d72b6fSJames Wright problem->apply_outflow.qfunction_loc = RiemannOutflow_Entropy_loc;
179a2d72b6fSJames Wright problem->apply_outflow_jacobian.qfunction = RiemannOutflow_Jacobian_Entropy;
180a2d72b6fSJames Wright problem->apply_outflow_jacobian.qfunction_loc = RiemannOutflow_Jacobian_Entropy_loc;
181a2d72b6fSJames Wright break;
1829f844368SJames Wright }
1839f844368SJames Wright break;
1849f844368SJames Wright case OUTFLOW_PRESSURE:
1859f844368SJames Wright switch (user->phys->state_var) {
1869f844368SJames Wright case STATEVAR_CONSERVATIVE:
1879f844368SJames Wright problem->apply_outflow.qfunction = PressureOutflow_Conserv;
1889f844368SJames Wright problem->apply_outflow.qfunction_loc = PressureOutflow_Conserv_loc;
1899f844368SJames Wright problem->apply_outflow_jacobian.qfunction = PressureOutflow_Jacobian_Conserv;
1909f844368SJames Wright problem->apply_outflow_jacobian.qfunction_loc = PressureOutflow_Jacobian_Conserv_loc;
1919f844368SJames Wright break;
1929f844368SJames Wright case STATEVAR_PRIMITIVE:
1939f844368SJames Wright problem->apply_outflow.qfunction = PressureOutflow_Prim;
1949f844368SJames Wright problem->apply_outflow.qfunction_loc = PressureOutflow_Prim_loc;
1959f844368SJames Wright problem->apply_outflow_jacobian.qfunction = PressureOutflow_Jacobian_Prim;
1969f844368SJames Wright problem->apply_outflow_jacobian.qfunction_loc = PressureOutflow_Jacobian_Prim_loc;
1979f844368SJames Wright break;
198a2d72b6fSJames Wright case STATEVAR_ENTROPY:
199a2d72b6fSJames Wright problem->apply_outflow.qfunction = PressureOutflow_Entropy;
200a2d72b6fSJames Wright problem->apply_outflow.qfunction_loc = PressureOutflow_Entropy_loc;
201a2d72b6fSJames Wright problem->apply_outflow_jacobian.qfunction = PressureOutflow_Jacobian_Entropy;
202a2d72b6fSJames Wright problem->apply_outflow_jacobian.qfunction_loc = PressureOutflow_Jacobian_Entropy_loc;
203a2d72b6fSJames Wright break;
2049f844368SJames Wright }
2059f844368SJames Wright break;
2069f844368SJames Wright }
2079f844368SJames Wright PetscCall(PetscCalloc1(1, &outflow_ctx));
2089f844368SJames Wright outflow_ctx->gas = *newtonian_ig_ctx;
2099f844368SJames Wright outflow_ctx->recirc = recirc;
2109f844368SJames Wright outflow_ctx->softplus_velocity = softplus_velocity;
2119f844368SJames Wright outflow_ctx->pressure = pressure;
2129f844368SJames Wright outflow_ctx->temperature = temperature;
2139f844368SJames Wright
2149f844368SJames Wright PetscCallCeed(ceed, CeedQFunctionContextCreate(user->ceed, &outflow_context));
2159f844368SJames Wright PetscCallCeed(ceed, CeedQFunctionContextSetData(outflow_context, CEED_MEM_HOST, CEED_USE_POINTER, sizeof(*outflow_ctx), outflow_ctx));
2169f844368SJames Wright PetscCallCeed(ceed, CeedQFunctionContextSetDataDestroy(outflow_context, CEED_MEM_HOST, FreeContextPetsc));
2179f844368SJames Wright problem->apply_outflow.qfunction_context = outflow_context;
2189f844368SJames Wright PetscCallCeed(ceed, CeedQFunctionContextReferenceCopy(outflow_context, &problem->apply_outflow_jacobian.qfunction_context));
2199f844368SJames Wright PetscFunctionReturn(PETSC_SUCCESS);
2209f844368SJames Wright }
221aedeac77SJames Wright
222aedeac77SJames Wright // @brief Calculate relative error, (A - B) / S
223aedeac77SJames Wright // If S < threshold, then set S=1
RelativeError(CeedScalar S,CeedScalar A,CeedScalar B,CeedScalar threshold)224aedeac77SJames Wright static inline CeedScalar RelativeError(CeedScalar S, CeedScalar A, CeedScalar B, CeedScalar threshold) {
225aedeac77SJames Wright return (A - B) / (fabs(S) > threshold ? S : 1);
226aedeac77SJames Wright }
227aedeac77SJames Wright
228aedeac77SJames Wright // @brief Check errors of a State vector and print if above tolerance
CheckQWithTolerance(const CeedScalar Q_s[5],const CeedScalar Q_a[5],const CeedScalar Q_b[5],const char * name,PetscReal rtol_0,PetscReal rtol_u,PetscReal rtol_4)229aedeac77SJames Wright static PetscErrorCode CheckQWithTolerance(const CeedScalar Q_s[5], const CeedScalar Q_a[5], const CeedScalar Q_b[5], const char *name,
230aedeac77SJames Wright PetscReal rtol_0, PetscReal rtol_u, PetscReal rtol_4) {
231aedeac77SJames Wright CeedScalar relative_error[5]; // relative error
232aedeac77SJames Wright CeedScalar divisor_threshold = 10 * CEED_EPSILON;
233aedeac77SJames Wright
234aedeac77SJames Wright PetscFunctionBeginUser;
235aedeac77SJames Wright relative_error[0] = RelativeError(Q_s[0], Q_a[0], Q_b[0], divisor_threshold);
236aedeac77SJames Wright relative_error[4] = RelativeError(Q_s[4], Q_a[4], Q_b[4], divisor_threshold);
237aedeac77SJames Wright
238aedeac77SJames Wright CeedScalar u_magnitude = sqrt(Square(Q_s[1]) + Square(Q_s[2]) + Square(Q_s[3]));
239aedeac77SJames Wright for (int i = 1; i < 4; i++) {
240aedeac77SJames Wright relative_error[i] = RelativeError(u_magnitude, Q_a[i], Q_b[i], divisor_threshold);
241aedeac77SJames Wright }
242aedeac77SJames Wright
243aedeac77SJames Wright if (fabs(relative_error[0]) >= rtol_0) {
244aedeac77SJames Wright printf("%s[0] error %g (expected %.10e, got %.10e)\n", name, relative_error[0], Q_s[0], Q_a[0]);
245aedeac77SJames Wright }
246aedeac77SJames Wright for (int i = 1; i < 4; i++) {
247aedeac77SJames Wright if (fabs(relative_error[i]) >= rtol_u) {
248aedeac77SJames Wright printf("%s[%d] error %g (expected %.10e, got %.10e)\n", name, i, relative_error[i], Q_s[i], Q_a[i]);
249aedeac77SJames Wright }
250aedeac77SJames Wright }
251aedeac77SJames Wright if (fabs(relative_error[4]) >= rtol_4) {
252aedeac77SJames Wright printf("%s[4] error %g (expected %.10e, got %.10e)\n", name, relative_error[4], Q_s[4], Q_a[4]);
253aedeac77SJames Wright }
254aedeac77SJames Wright PetscFunctionReturn(PETSC_SUCCESS);
255aedeac77SJames Wright }
256aedeac77SJames Wright
257aedeac77SJames Wright // @brief Verify RiemannFlux_HLL_fwd function against finite-difference approximation
TestRiemannHLL_fwd(NewtonianIdealGasContext gas,CeedScalar rtol_0,CeedScalar rtol_u,CeedScalar rtol_4)258aedeac77SJames Wright static PetscErrorCode TestRiemannHLL_fwd(NewtonianIdealGasContext gas, CeedScalar rtol_0, CeedScalar rtol_u, CeedScalar rtol_4) {
259aedeac77SJames Wright CeedScalar eps = 4e-7; // Finite difference step
260aedeac77SJames Wright char buf[128];
261aedeac77SJames Wright const CeedScalar T = 200;
262aedeac77SJames Wright const CeedScalar rho = 1.2;
263aedeac77SJames Wright const CeedScalar p = (HeatCapacityRatio(gas) - 1) * rho * gas->cv * T;
264aedeac77SJames Wright const CeedScalar u_base = 40;
265aedeac77SJames Wright const CeedScalar u[3] = {u_base, u_base * 1.1, u_base * 1.2};
266aedeac77SJames Wright const CeedScalar Y0_left[5] = {p, u[0], u[1], u[2], T};
267aedeac77SJames Wright const CeedScalar Y0_right[5] = {1.2 * p, 1.2 * u[0], 1.2 * u[1], 1.2 * u[2], 1.2 * T};
268aedeac77SJames Wright CeedScalar normal[3] = {1, 2, 3};
269aedeac77SJames Wright
270aedeac77SJames Wright PetscFunctionBeginUser;
271aedeac77SJames Wright State left0 = StateFromY(gas, Y0_left);
272aedeac77SJames Wright State right0 = StateFromY(gas, Y0_right);
273aedeac77SJames Wright ScaleN(normal, 1 / sqrt(Dot3(normal, normal)), 3);
274aedeac77SJames Wright
275aedeac77SJames Wright for (int i = 0; i < 10; i++) {
276aedeac77SJames Wright CeedScalar dFlux[5] = {0.}, dFlux_fd[5] = {0.};
277aedeac77SJames Wright { // Calculate dFlux using *_fwd function
278aedeac77SJames Wright CeedScalar dY_right[5] = {0};
279aedeac77SJames Wright CeedScalar dY_left[5] = {0};
280aedeac77SJames Wright
281aedeac77SJames Wright if (i < 5) {
282aedeac77SJames Wright dY_left[i] = Y0_left[i];
283aedeac77SJames Wright } else {
284aedeac77SJames Wright dY_right[i % 5] = Y0_right[i % 5];
285aedeac77SJames Wright }
286aedeac77SJames Wright State dleft0 = StateFromY_fwd(gas, left0, dY_left);
287aedeac77SJames Wright State dright0 = StateFromY_fwd(gas, right0, dY_right);
288aedeac77SJames Wright
289aedeac77SJames Wright StateConservative dFlux_state = RiemannFlux_HLL_fwd(gas, left0, dleft0, right0, dright0, normal);
290aedeac77SJames Wright UnpackState_U(dFlux_state, dFlux);
291aedeac77SJames Wright }
292aedeac77SJames Wright
293aedeac77SJames Wright { // Calculate dFlux_fd via finite difference approximation
294aedeac77SJames Wright CeedScalar Y1_left[5] = {Y0_left[0], Y0_left[1], Y0_left[2], Y0_left[3], Y0_left[4]};
295aedeac77SJames Wright CeedScalar Y1_right[5] = {Y0_right[0], Y0_right[1], Y0_right[2], Y0_right[3], Y0_right[4]};
296aedeac77SJames Wright CeedScalar Flux0[5], Flux1[5];
297aedeac77SJames Wright
298aedeac77SJames Wright if (i < 5) {
299aedeac77SJames Wright Y1_left[i] *= 1 + eps;
300aedeac77SJames Wright } else {
301aedeac77SJames Wright Y1_right[i % 5] *= 1 + eps;
302aedeac77SJames Wright }
303aedeac77SJames Wright State left1 = StateFromY(gas, Y1_left);
304aedeac77SJames Wright State right1 = StateFromY(gas, Y1_right);
305aedeac77SJames Wright
306aedeac77SJames Wright StateConservative Flux0_state = RiemannFlux_HLL(gas, left0, right0, normal);
307aedeac77SJames Wright StateConservative Flux1_state = RiemannFlux_HLL(gas, left1, right1, normal);
308aedeac77SJames Wright UnpackState_U(Flux0_state, Flux0);
309aedeac77SJames Wright UnpackState_U(Flux1_state, Flux1);
310aedeac77SJames Wright for (int j = 0; j < 5; j++) dFlux_fd[j] = (Flux1[j] - Flux0[j]) / eps;
311aedeac77SJames Wright }
312aedeac77SJames Wright
313aedeac77SJames Wright snprintf(buf, sizeof buf, "RiemannFlux_HLL i=%d: Flux", i);
314aedeac77SJames Wright PetscCall(CheckQWithTolerance(dFlux_fd, dFlux, dFlux_fd, buf, rtol_0, rtol_u, rtol_4));
315aedeac77SJames Wright }
316aedeac77SJames Wright PetscFunctionReturn(PETSC_SUCCESS);
317aedeac77SJames Wright }
318aedeac77SJames Wright
319aedeac77SJames Wright // @brief Verify RiemannFlux_HLLC_fwd function against finite-difference approximation
TestRiemannHLLC_fwd(NewtonianIdealGasContext gas,CeedScalar rtol_0,CeedScalar rtol_u,CeedScalar rtol_4)320aedeac77SJames Wright static PetscErrorCode TestRiemannHLLC_fwd(NewtonianIdealGasContext gas, CeedScalar rtol_0, CeedScalar rtol_u, CeedScalar rtol_4) {
321aedeac77SJames Wright CeedScalar eps = 4e-7; // Finite difference step
322aedeac77SJames Wright char buf[128];
323aedeac77SJames Wright const CeedScalar T = 200;
324aedeac77SJames Wright const CeedScalar rho = 1.2;
325aedeac77SJames Wright const CeedScalar p = (HeatCapacityRatio(gas) - 1) * rho * gas->cv * T;
326aedeac77SJames Wright const CeedScalar u_base = 40;
327aedeac77SJames Wright const CeedScalar u[3] = {u_base, u_base * 1.1, u_base * 1.2};
328aedeac77SJames Wright const CeedScalar Y0_left[5] = {p, u[0], u[1], u[2], T};
329aedeac77SJames Wright const CeedScalar Y0_right[5] = {1.2 * p, 1.2 * u[0], 1.2 * u[1], 1.2 * u[2], 1.2 * T};
330aedeac77SJames Wright CeedScalar normal[3] = {1, 2, 3};
331aedeac77SJames Wright
332aedeac77SJames Wright PetscFunctionBeginUser;
333aedeac77SJames Wright State left0 = StateFromY(gas, Y0_left);
334aedeac77SJames Wright State right0 = StateFromY(gas, Y0_right);
335aedeac77SJames Wright ScaleN(normal, 1 / sqrt(Dot3(normal, normal)), 3);
336aedeac77SJames Wright
337aedeac77SJames Wright for (int i = 0; i < 10; i++) {
338aedeac77SJames Wright CeedScalar dFlux[5] = {0.}, dFlux_fd[5] = {0.};
339aedeac77SJames Wright { // Calculate dFlux using *_fwd function
340aedeac77SJames Wright CeedScalar dY_right[5] = {0};
341aedeac77SJames Wright CeedScalar dY_left[5] = {0};
342aedeac77SJames Wright
343aedeac77SJames Wright if (i < 5) {
344aedeac77SJames Wright dY_left[i] = Y0_left[i];
345aedeac77SJames Wright } else {
346aedeac77SJames Wright dY_right[i % 5] = Y0_right[i % 5];
347aedeac77SJames Wright }
348aedeac77SJames Wright State dleft0 = StateFromY_fwd(gas, left0, dY_left);
349aedeac77SJames Wright State dright0 = StateFromY_fwd(gas, right0, dY_right);
350aedeac77SJames Wright
351aedeac77SJames Wright StateConservative dFlux_state = RiemannFlux_HLLC_fwd(gas, left0, dleft0, right0, dright0, normal);
352aedeac77SJames Wright UnpackState_U(dFlux_state, dFlux);
353aedeac77SJames Wright }
354aedeac77SJames Wright
355aedeac77SJames Wright { // Calculate dFlux_fd via finite difference approximation
356aedeac77SJames Wright CeedScalar Y1_left[5] = {Y0_left[0], Y0_left[1], Y0_left[2], Y0_left[3], Y0_left[4]};
357aedeac77SJames Wright CeedScalar Y1_right[5] = {Y0_right[0], Y0_right[1], Y0_right[2], Y0_right[3], Y0_right[4]};
358aedeac77SJames Wright CeedScalar Flux0[5], Flux1[5];
359aedeac77SJames Wright
360aedeac77SJames Wright if (i < 5) {
361aedeac77SJames Wright Y1_left[i] *= 1 + eps;
362aedeac77SJames Wright } else {
363aedeac77SJames Wright Y1_right[i % 5] *= 1 + eps;
364aedeac77SJames Wright }
365aedeac77SJames Wright State left1 = StateFromY(gas, Y1_left);
366aedeac77SJames Wright State right1 = StateFromY(gas, Y1_right);
367aedeac77SJames Wright
368aedeac77SJames Wright StateConservative Flux0_state = RiemannFlux_HLLC(gas, left0, right0, normal);
369aedeac77SJames Wright StateConservative Flux1_state = RiemannFlux_HLLC(gas, left1, right1, normal);
370aedeac77SJames Wright UnpackState_U(Flux0_state, Flux0);
371aedeac77SJames Wright UnpackState_U(Flux1_state, Flux1);
372aedeac77SJames Wright for (int j = 0; j < 5; j++) dFlux_fd[j] = (Flux1[j] - Flux0[j]) / eps;
373aedeac77SJames Wright }
374aedeac77SJames Wright
375aedeac77SJames Wright snprintf(buf, sizeof buf, "RiemannFlux_HLLC i=%d: Flux", i);
376aedeac77SJames Wright PetscCall(CheckQWithTolerance(dFlux_fd, dFlux, dFlux_fd, buf, rtol_0, rtol_u, rtol_4));
377aedeac77SJames Wright }
378aedeac77SJames Wright PetscFunctionReturn(PETSC_SUCCESS);
379aedeac77SJames Wright }
380aedeac77SJames Wright
381aedeac77SJames Wright // @brief Verify ComputeHLLSpeeds_Roe_fwd function against finite-difference approximation
TestComputeHLLSpeeds_Roe_fwd(NewtonianIdealGasContext gas,CeedScalar rtol)382aedeac77SJames Wright static PetscErrorCode TestComputeHLLSpeeds_Roe_fwd(NewtonianIdealGasContext gas, CeedScalar rtol) {
383aedeac77SJames Wright CeedScalar eps = 4e-7; // Finite difference step
384aedeac77SJames Wright char buf[128];
385aedeac77SJames Wright const CeedScalar T = 200;
386aedeac77SJames Wright const CeedScalar rho = 1.2;
387aedeac77SJames Wright const CeedScalar p = (HeatCapacityRatio(gas) - 1) * rho * gas->cv * T;
388aedeac77SJames Wright const CeedScalar u_base = 40;
389aedeac77SJames Wright const CeedScalar u[3] = {u_base, u_base * 1.1, u_base * 1.2};
390aedeac77SJames Wright const CeedScalar Y0_left[5] = {p, u[0], u[1], u[2], T};
391aedeac77SJames Wright const CeedScalar Y0_right[5] = {1.2 * p, 1.2 * u[0], 1.2 * u[1], 1.2 * u[2], 1.2 * T};
392aedeac77SJames Wright CeedScalar normal[3] = {1, 2, 3};
393aedeac77SJames Wright
394aedeac77SJames Wright PetscFunctionBeginUser;
395aedeac77SJames Wright State left0 = StateFromY(gas, Y0_left);
396aedeac77SJames Wright State right0 = StateFromY(gas, Y0_right);
397aedeac77SJames Wright ScaleN(normal, 1 / sqrt(Dot3(normal, normal)), 3);
398aedeac77SJames Wright CeedScalar u_left0 = Dot3(left0.Y.velocity, normal);
399aedeac77SJames Wright CeedScalar u_right0 = Dot3(right0.Y.velocity, normal);
400aedeac77SJames Wright
401aedeac77SJames Wright for (int i = 0; i < 10; i++) {
402aedeac77SJames Wright CeedScalar ds_left, ds_right, ds_left_fd, ds_right_fd;
403aedeac77SJames Wright { // Calculate ds_{left,right} using *_fwd function
404aedeac77SJames Wright CeedScalar dY_right[5] = {0};
405aedeac77SJames Wright CeedScalar dY_left[5] = {0};
406aedeac77SJames Wright
407aedeac77SJames Wright if (i < 5) {
408aedeac77SJames Wright dY_left[i] = Y0_left[i];
409aedeac77SJames Wright } else {
410aedeac77SJames Wright dY_right[i % 5] = Y0_right[i % 5];
411aedeac77SJames Wright }
412aedeac77SJames Wright State dleft0 = StateFromY_fwd(gas, left0, dY_left);
413aedeac77SJames Wright State dright0 = StateFromY_fwd(gas, right0, dY_right);
414aedeac77SJames Wright CeedScalar du_left = Dot3(dleft0.Y.velocity, normal);
415aedeac77SJames Wright CeedScalar du_right = Dot3(dright0.Y.velocity, normal);
416aedeac77SJames Wright
417aedeac77SJames Wright CeedScalar s_left, s_right; // Throw away
418aedeac77SJames 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);
419aedeac77SJames Wright }
420aedeac77SJames Wright
421aedeac77SJames Wright { // Calculate ds_{left,right}_fd via finite difference approximation
422aedeac77SJames Wright CeedScalar Y1_left[5] = {Y0_left[0], Y0_left[1], Y0_left[2], Y0_left[3], Y0_left[4]};
423aedeac77SJames Wright CeedScalar Y1_right[5] = {Y0_right[0], Y0_right[1], Y0_right[2], Y0_right[3], Y0_right[4]};
424aedeac77SJames Wright
425aedeac77SJames Wright if (i < 5) {
426aedeac77SJames Wright Y1_left[i] *= 1 + eps;
427aedeac77SJames Wright } else {
428aedeac77SJames Wright Y1_right[i % 5] *= 1 + eps;
429aedeac77SJames Wright }
430aedeac77SJames Wright State left1 = StateFromY(gas, Y1_left);
431aedeac77SJames Wright State right1 = StateFromY(gas, Y1_right);
432aedeac77SJames Wright CeedScalar u_left1 = Dot3(left1.Y.velocity, normal);
433aedeac77SJames Wright CeedScalar u_right1 = Dot3(right1.Y.velocity, normal);
434aedeac77SJames Wright
435aedeac77SJames Wright CeedScalar s_left0, s_right0, s_left1, s_right1;
436aedeac77SJames Wright ComputeHLLSpeeds_Roe(gas, left0, u_left0, right0, u_right0, &s_left0, &s_right0);
437aedeac77SJames Wright ComputeHLLSpeeds_Roe(gas, left1, u_left1, right1, u_right1, &s_left1, &s_right1);
438aedeac77SJames Wright ds_left_fd = (s_left1 - s_left0) / eps;
439aedeac77SJames Wright ds_right_fd = (s_right1 - s_right0) / eps;
440aedeac77SJames Wright }
441aedeac77SJames Wright
442aedeac77SJames Wright snprintf(buf, sizeof buf, "ComputeHLLSpeeds_Roe i=%d:", i);
443aedeac77SJames Wright {
444aedeac77SJames Wright CeedScalar divisor_threshold = 10 * CEED_EPSILON;
445aedeac77SJames Wright CeedScalar ds_left_err, ds_right_err;
446aedeac77SJames Wright
447aedeac77SJames Wright ds_left_err = RelativeError(ds_left_fd, ds_left, ds_left_fd, divisor_threshold);
448aedeac77SJames Wright ds_right_err = RelativeError(ds_right_fd, ds_right, ds_right_fd, divisor_threshold);
449aedeac77SJames 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);
450aedeac77SJames 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);
451aedeac77SJames Wright }
452aedeac77SJames Wright }
453aedeac77SJames Wright PetscFunctionReturn(PETSC_SUCCESS);
454aedeac77SJames Wright }
455aedeac77SJames Wright
456aedeac77SJames Wright // @brief Verify TotalSpecificEnthalpy_fwd function against finite-difference approximation
TestTotalSpecificEnthalpy_fwd(NewtonianIdealGasContext gas,CeedScalar rtol)457aedeac77SJames Wright static PetscErrorCode TestTotalSpecificEnthalpy_fwd(NewtonianIdealGasContext gas, CeedScalar rtol) {
458aedeac77SJames Wright CeedScalar eps = 4e-7; // Finite difference step
459aedeac77SJames Wright char buf[128];
460aedeac77SJames Wright const CeedScalar T = 200;
461aedeac77SJames Wright const CeedScalar rho = 1.2;
462aedeac77SJames Wright const CeedScalar p = (HeatCapacityRatio(gas) - 1) * rho * gas->cv * T;
463aedeac77SJames Wright const CeedScalar u_base = 40;
464aedeac77SJames Wright const CeedScalar u[3] = {u_base, u_base * 1.1, u_base * 1.2};
465aedeac77SJames Wright const CeedScalar Y0[5] = {p, u[0], u[1], u[2], T};
466aedeac77SJames Wright
467aedeac77SJames Wright PetscFunctionBeginUser;
468aedeac77SJames Wright State state0 = StateFromY(gas, Y0);
469aedeac77SJames Wright
470aedeac77SJames Wright for (int i = 0; i < 5; i++) {
471aedeac77SJames Wright CeedScalar dH, dH_fd;
472aedeac77SJames Wright { // Calculate dH using *_fwd function
473aedeac77SJames Wright CeedScalar dY[5] = {0};
474aedeac77SJames Wright
475aedeac77SJames Wright dY[i] = Y0[i];
476aedeac77SJames Wright State dstate0 = StateFromY_fwd(gas, state0, dY);
477aedeac77SJames Wright dH = TotalSpecificEnthalpy_fwd(gas, state0, dstate0);
478aedeac77SJames Wright }
479aedeac77SJames Wright
480aedeac77SJames Wright { // Calculate dH_fd via finite difference approximation
481aedeac77SJames Wright CeedScalar H0, H1;
482aedeac77SJames Wright CeedScalar Y1[5] = {Y0[0], Y0[1], Y0[2], Y0[3], Y0[4]};
483aedeac77SJames Wright Y1[i] *= 1 + eps;
484aedeac77SJames Wright State state1 = StateFromY(gas, Y1);
485aedeac77SJames Wright
486aedeac77SJames Wright H0 = TotalSpecificEnthalpy(gas, state0);
487aedeac77SJames Wright H1 = TotalSpecificEnthalpy(gas, state1);
488aedeac77SJames Wright dH_fd = (H1 - H0) / eps;
489aedeac77SJames Wright }
490aedeac77SJames Wright
491aedeac77SJames Wright snprintf(buf, sizeof buf, "TotalSpecificEnthalpy i=%d:", i);
492aedeac77SJames Wright {
493aedeac77SJames Wright CeedScalar divisor_threshold = 10 * CEED_EPSILON;
494aedeac77SJames Wright CeedScalar dH_err;
495aedeac77SJames Wright
496aedeac77SJames Wright dH_err = RelativeError(dH_fd, dH, dH_fd, divisor_threshold);
497aedeac77SJames Wright if (fabs(dH_err) >= rtol) printf("%s dH error %g (expected %.10e, got %.10e)\n", buf, dH_err, dH_fd, dH);
498aedeac77SJames Wright }
499aedeac77SJames Wright }
500aedeac77SJames Wright PetscFunctionReturn(PETSC_SUCCESS);
501aedeac77SJames Wright }
502aedeac77SJames Wright
503aedeac77SJames Wright // @brief Verify RoeSetup_fwd function against finite-difference approximation
TestRowSetup_fwd(NewtonianIdealGasContext gas,CeedScalar rtol)504aedeac77SJames Wright static PetscErrorCode TestRowSetup_fwd(NewtonianIdealGasContext gas, CeedScalar rtol) {
505aedeac77SJames Wright CeedScalar eps = 4e-7; // Finite difference step
506aedeac77SJames Wright char buf[128];
507aedeac77SJames Wright const CeedScalar rho0[2] = {1.2, 1.4};
508aedeac77SJames Wright
509aedeac77SJames Wright PetscFunctionBeginUser;
510aedeac77SJames Wright for (int i = 0; i < 2; i++) {
511aedeac77SJames Wright RoeWeights dR, dR_fd;
512aedeac77SJames Wright { // Calculate using *_fwd function
513aedeac77SJames Wright CeedScalar drho[5] = {0};
514aedeac77SJames Wright
515aedeac77SJames Wright drho[i] = rho0[i];
516aedeac77SJames Wright dR = RoeSetup_fwd(rho0[0], rho0[1], drho[0], drho[1]);
517aedeac77SJames Wright }
518aedeac77SJames Wright
519aedeac77SJames Wright { // Calculate via finite difference approximation
520aedeac77SJames Wright RoeWeights R0, R1;
521aedeac77SJames Wright CeedScalar rho1[5] = {rho0[0], rho0[1]};
522aedeac77SJames Wright rho1[i] *= 1 + eps;
523aedeac77SJames Wright
524aedeac77SJames Wright R0 = RoeSetup(rho0[0], rho0[1]);
525aedeac77SJames Wright R1 = RoeSetup(rho1[0], rho1[1]);
526aedeac77SJames Wright dR_fd.left = (R1.left - R0.left) / eps;
527aedeac77SJames Wright dR_fd.right = (R1.right - R0.right) / eps;
528aedeac77SJames Wright }
529aedeac77SJames Wright
530aedeac77SJames Wright snprintf(buf, sizeof buf, "RoeSetup i=%d:", i);
531aedeac77SJames Wright {
532aedeac77SJames Wright CeedScalar divisor_threshold = 10 * CEED_EPSILON;
533aedeac77SJames Wright RoeWeights dR_err;
534aedeac77SJames Wright
535aedeac77SJames Wright dR_err.left = RelativeError(dR_fd.left, dR.left, dR_fd.left, divisor_threshold);
536aedeac77SJames Wright dR_err.right = RelativeError(dR_fd.right, dR.right, dR_fd.right, divisor_threshold);
537aedeac77SJames 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);
538aedeac77SJames 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);
539aedeac77SJames Wright }
540aedeac77SJames Wright }
541aedeac77SJames Wright PetscFunctionReturn(PETSC_SUCCESS);
542aedeac77SJames Wright }
543aedeac77SJames Wright
544aedeac77SJames Wright // @brief Test Riemann solver related `*_fwd` functions via finite-difference approximation
RiemannSolverUnitTests(NewtonianIdealGasContext gas,CeedScalar rtol)545aedeac77SJames Wright static PetscErrorCode RiemannSolverUnitTests(NewtonianIdealGasContext gas, CeedScalar rtol) {
546aedeac77SJames Wright PetscFunctionBeginUser;
547aedeac77SJames Wright PetscCall(TestRiemannHLL_fwd(gas, rtol, rtol, rtol));
548aedeac77SJames Wright PetscCall(TestRiemannHLLC_fwd(gas, rtol, rtol, rtol));
549aedeac77SJames Wright PetscCall(TestComputeHLLSpeeds_Roe_fwd(gas, rtol));
550aedeac77SJames Wright PetscCall(TestTotalSpecificEnthalpy_fwd(gas, rtol));
551aedeac77SJames Wright PetscCall(TestRowSetup_fwd(gas, rtol));
552aedeac77SJames Wright PetscFunctionReturn(PETSC_SUCCESS);
553aedeac77SJames Wright }
554