20#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
33static constexpr real g_gamma = 1.4;
38 Eigen::Vector<real, 5> U;
39 real E = p / (g_gamma - 1.0) + 0.5 * rho * (u * u +
v *
v + w * w);
40 U << rho, rho * u, rho *
v, rho * w, E;
45[[maybe_unused]]
static Eigen::Vector<real, 4> primToCons2D(
real rho,
real u,
real v,
real p)
47 Eigen::Vector<real, 4> U;
48 real E = p / (g_gamma - 1.0) + 0.5 * rho * (u * u +
v *
v);
49 U << rho, rho * u, rho *
v, E;
60 real rho = 1.0, p_expected = 1.0 / g_gamma;
61 real E = p_expected / (g_gamma - 1.0);
67 CHECK(p == doctest::Approx(p_expected).epsilon(1e-14));
68 CHECK(asqr == doctest::Approx(1.0).epsilon(1e-14));
69 CHECK(H == doctest::Approx((E + p) / rho).epsilon(1e-14));
74 real rho = 1.0, u = 2.0, p_expected = 1.0;
76 real E = p_expected / (g_gamma - 1.0) + rho * 0.5 * vSqr;
81 CHECK(p == doctest::Approx(p_expected).epsilon(1e-14));
82 CHECK(asqr == doctest::Approx(g_gamma * p_expected / rho).epsilon(1e-14));
83 real H_expected = (E + p_expected) / rho;
84 CHECK(H == doctest::Approx(H_expected).epsilon(1e-14));
93 Eigen::Vector<real, 5> prim0;
94 prim0 << 1.225, 100.0, 50.0, -30.0, 101325.0;
96 Eigen::Vector<real, 5> U, prim1;
97 IdealGasThermalPrimitive2Conservative<3>(prim0, U, g_gamma);
98 IdealGasThermalConservative2Primitive<3>(U, prim1, g_gamma);
100 for (
int i = 0; i < 5; i++)
103 CHECK(prim1(i) == doctest::Approx(prim0(i)).epsilon(1e-12));
109 Eigen::Vector<real, 4> prim0;
110 prim0 << 0.5, 300.0, -100.0, 50000.0;
112 Eigen::Vector<real, 4> U, prim1;
113 IdealGasThermalPrimitive2Conservative<2>(prim0, U, g_gamma);
114 IdealGasThermalConservative2Primitive<2>(U, prim1, g_gamma);
116 for (
int i = 0; i < 4; i++)
119 CHECK(prim1(i) == doctest::Approx(prim0(i)).epsilon(1e-12));
127 Eigen::Vector<real, 5> prim;
128 prim << 2.0, 3.0, 0.0, 0.0, 10.0;
129 Eigen::Vector<real, 5> U;
130 IdealGasThermalPrimitive2Conservative<3>(prim, U, g_gamma);
132 CHECK(U(0) == doctest::Approx(2.0));
133 CHECK(U(1) == doctest::Approx(6.0));
134 CHECK(U(2) == doctest::Approx(0.0));
135 CHECK(U(3) == doctest::Approx(0.0));
136 CHECK(U(4) == doctest::Approx(34.0));
146 Eigen::Vector<real, 5> prim;
147 prim << 1.0, 0.0, 0.0, 0.0, 100000.0;
150 auto [p0, T0] = IdealGasThermalPrimitiveGetP0T0<3>(prim, g_gamma, Rgas);
151 real T = prim(4) / (prim(0) * Rgas);
153 CHECK(p0 == doctest::Approx(100000.0).epsilon(1e-10));
154 CHECK(T0 == doctest::Approx(T).epsilon(1e-10));
159 Eigen::Vector<real, 5> prim;
160 prim << 1.225, 300.0, 0.0, 0.0, 101325.0;
163 auto [p0, T0] = IdealGasThermalPrimitiveGetP0T0<3>(prim, g_gamma, Rgas);
164 real T = prim(4) / (prim(0) * Rgas);
176 Eigen::Vector3d
velo(1.5, -0.3, 0.7);
179 real rho = 1.0, p = 1.0;
180 real E = p / (g_gamma - 1.0) + 0.5 * rho * Vsqr;
181 real a = std::sqrt(g_gamma * p / rho);
182 real H = (E + p) / rho;
184 Eigen::Matrix<real, 5, 5> R, L;
187 EulerGasRightEigenVector<3>(
velo, Vsqr, H, a, R);
188 EulerGasLeftEigenVector<3>(
velo, Vsqr, H, a, g_gamma, L);
190 Eigen::Matrix<real, 5, 5> LR = L * R;
191 Eigen::Matrix<real, 5, 5> I = Eigen::Matrix<real, 5, 5>::Identity();
193 for (
int i = 0; i < 5; i++)
194 for (
int j = 0; j < 5; j++)
198 CHECK(LR(i, j) == doctest::Approx(I(i, j)).epsilon(1e-12));
202TEST_CASE(
"EulerGas eigenvectors: L*R = I for non-trivial velocity")
204 Eigen::Vector3d
velo(300.0, -150.0, 75.0);
206 real rho = 1.225, p = 101325.0;
207 real E = p / (g_gamma - 1.0) + 0.5 * rho * Vsqr;
208 real a = std::sqrt(g_gamma * p / rho);
209 real H = (E + p) / rho;
211 Eigen::Matrix<real, 5, 5> R, L;
214 EulerGasRightEigenVector<3>(
velo, Vsqr, H, a, R);
215 EulerGasLeftEigenVector<3>(
velo, Vsqr, H, a, g_gamma, L);
217 Eigen::Matrix<real, 5, 5> LR = L * R;
218 Eigen::Matrix<real, 5, 5> I = Eigen::Matrix<real, 5, 5>::Identity();
220 real maxErr = (LR - I).cwiseAbs().maxCoeff();
221 CHECK(maxErr < 1e-10);
224TEST_CASE(
"IdealGas convenience eigenvector wrappers produce L*R=I")
226 auto U = primToCons3D(1.225, 100.0, -50.0, 25.0, 101325.0);
228 auto R = IdealGas_EulerGasRightEigenVector<3>(U, g_gamma);
229 auto L = IdealGas_EulerGasLeftEigenVector<3>(U, g_gamma);
232 real maxErr = (LR - Eigen::Matrix<real, 5, 5>::Identity()).cwiseAbs().maxCoeff();
233 CHECK(maxErr < 1e-10);
243 auto U = primToCons3D(1.0, 0.0, 0.0, 0.0, 1.0);
244 Eigen::Vector3d
velo(0, 0, 0),
vg(0, 0, 0);
247 Eigen::Vector<real, 5> F;
250 CHECK(F(0) == doctest::Approx(0.0).epsilon(1e-14));
251 CHECK(F(1) == doctest::Approx(p).epsilon(1e-14));
252 CHECK(F(2) == doctest::Approx(0.0).epsilon(1e-14));
253 CHECK(F(3) == doctest::Approx(0.0).epsilon(1e-14));
254 CHECK(F(4) == doctest::Approx(0.0).epsilon(1e-14));
259 real rho = 2.0, u = 3.0, p = 10.0;
260 auto U = primToCons3D(rho, u, 0.0, 0.0, p);
261 Eigen::Vector3d
velo(u, 0, 0),
vg(0, 0, 0);
263 Eigen::Vector<real, 5> F;
268 CHECK(F(0) == doctest::Approx(rho * u).epsilon(1e-12));
269 CHECK(F(1) == doctest::Approx(rho * u * u + p).epsilon(1e-12));
270 CHECK(F(2) == doctest::Approx(0.0).epsilon(1e-12));
271 CHECK(F(3) == doctest::Approx(0.0).epsilon(1e-12));
272 CHECK(F(4) == doctest::Approx((E + p) * u).epsilon(1e-12));
279TEST_CASE(
"GasInviscidFlux_XY: n=(1,0,0) equals GasInviscidFlux")
281 auto U = primToCons3D(1.225, 100.0, -50.0, 25.0, 101325.0);
282 Eigen::Vector3d
velo = U.segment<3>(1) / U(0);
283 Eigen::Vector3d
vg(0, 0, 0);
292 Eigen::Vector3d
nx(1, 0, 0);
295 for (
int i = 0; i < 5; i++)
298 CHECK(
Fn(i) == doctest::Approx(
Fx(i)).epsilon(1e-10));
306TEST_CASE(
"IdealGasUIncrement: zero increment gives zero output")
308 auto U = primToCons3D(1.0, 100.0, -50.0, 25.0, 101325.0);
309 Eigen::Vector<real, 5>
dU = Eigen::Vector<real, 5>::Zero();
310 Eigen::Vector3d
velo = U.segment<3>(1) / U(0);
311 Eigen::Vector3d dVelo;
314 IdealGasUIncrement<3>(U,
dU,
velo, g_gamma, dVelo, dp);
316 CHECK(dVelo.norm() == doctest::Approx(0.0).epsilon(1e-14));
317 CHECK(dp == doctest::Approx(0.0).epsilon(1e-10));
320TEST_CASE(
"IdealGasUIncrement: finite-difference verification")
324 auto U0 = primToCons3D(1.225, 100.0, -50.0, 25.0, 101325.0);
325 Eigen::Vector3d velo0 = U0.segment<3>(1) / U0(0);
328 Eigen::Vector<real, 5> prim0;
329 IdealGasThermalConservative2Primitive<3>(U0, prim0, g_gamma);
331 Eigen::Vector<real, 5> prim1;
332 prim1 << prim0(0) + 0.01 * eps, prim0(1) + 0.5 * eps,
333 prim0(2) - 0.3 * eps, prim0(3) + 0.1 * eps,
334 prim0(4) + 100.0 * eps;
335 Eigen::Vector<real, 5> U1;
336 IdealGasThermalPrimitive2Conservative<3>(prim1, U1, g_gamma);
338 Eigen::Vector<real, 5>
dU = U1 - U0;
339 Eigen::Vector3d dVelo;
341 IdealGasUIncrement<3>(U0,
dU, velo0, g_gamma, dVelo, dp);
343 Eigen::Vector3d dVeloExpected = prim1.segment<3>(1) - prim0.segment<3>(1);
344 real dpExpected = prim1(4) - prim0(4);
346 for (
int i = 0; i < 3; i++)
349 CHECK(dVelo(i) == doctest::Approx(dVeloExpected(i)).epsilon(1e-4));
351 CHECK(dp == doctest::Approx(dpExpected).epsilon(1e-3));
358TEST_CASE(
"GetRoeAverage: identical states give same state")
360 auto U = primToCons3D(1.225, 100.0, -50.0, 25.0, 101325.0);
362 Eigen::Vector3d veloRoe;
363 real vsqrRoe, aRoe, asqrRoe, HRoe;
364 Eigen::Vector<real, 5> URoe;
366 GetRoeAverage<3>(U, U, g_gamma, veloRoe, vsqrRoe, aRoe, asqrRoe, HRoe, URoe);
368 Eigen::Vector3d
velo = U.segment<3>(1) / U(0);
372 for (
int i = 0; i < 3; i++)
375 CHECK(veloRoe(i) == doctest::Approx(
velo(i)).epsilon(1e-10));
377 CHECK(HRoe == doctest::Approx(H_val).epsilon(1e-10));
378 CHECK(aRoe == doctest::Approx(std::sqrt(asqr_val)).epsilon(1e-10));
383 auto UL = primToCons3D(1.0, 100.0, 0.0, 0.0, 100000.0);
384 auto UR = primToCons3D(4.0, 100.0, 0.0, 0.0, 100000.0);
386 Eigen::Vector3d veloRoe;
387 real vsqrRoe, aRoe, asqrRoe, HRoe;
388 Eigen::Vector<real, 5> URoe;
390 GetRoeAverage<3>(UL, UR, g_gamma, veloRoe, vsqrRoe, aRoe, asqrRoe, HRoe, URoe);
393 CHECK(URoe(0) == doctest::Approx(2.0).epsilon(1e-10));
400TEST_CASE(
"GradientCons2Prim: zero gradient produces zero")
402 auto U = primToCons3D(1.225, 100.0, -50.0, 25.0, 101325.0);
403 Eigen::Matrix<real, 3, 5> GradU = Eigen::Matrix<real, 3, 5>::Zero();
404 Eigen::Matrix<real, 3, 5> GradPrim;
406 GradientCons2Prim_IdealGas<3>(U, GradU, GradPrim, g_gamma);
408 CHECK(GradPrim.cwiseAbs().maxCoeff() < 1e-12);
411TEST_CASE(
"GradientCons2Prim: finite-difference verification")
414 Eigen::Vector<real, 5> prim0;
415 prim0 << 1.225, 100.0, -50.0, 25.0, 101325.0;
416 Eigen::Vector<real, 5> U0;
417 IdealGasThermalPrimitive2Conservative<3>(prim0, U0, g_gamma);
421 Eigen::Vector<real, 5> prim1;
422 prim1 << 1.225 + 0.01 * dx, 100.0 + 0.5 * dx, -50.0 - 0.3 * dx, 25.0 + 0.1 * dx, 101325.0 + 100.0 * dx;
423 Eigen::Vector<real, 5> U1;
424 IdealGasThermalPrimitive2Conservative<3>(prim1, U1, g_gamma);
427 Eigen::Matrix<real, 3, 5> GradU = Eigen::Matrix<real, 3, 5>::Zero();
428 GradU.row(0) = (U1 - U0).transpose() / dx;
430 Eigen::Matrix<real, 3, 5> GradPrim;
431 GradientCons2Prim_IdealGas<3>(U0, GradU, GradPrim, g_gamma);
434 Eigen::RowVector<real, 5> dPrimdx_expected = (prim1 - prim0).transpose() / dx;
436 for (
int j = 0; j < 5; j++)
439 CHECK(GradPrim(0, j) == doctest::Approx(dPrimdx_expected(j)).epsilon(1e-4));
447TEST_CASE(
"CompressionRatio: zero increment gives alpha=0 (no compression needed)")
451 auto U = primToCons3D(1.225, 100.0, -50.0, 25.0, 101325.0);
452 Eigen::Vector<real, 5>
dU = Eigen::Vector<real, 5>::Zero();
454 real alpha = IdealGasGetCompressionRatioPressure<3, 0, 5>(U,
dU, 0.0);
460 auto U = primToCons3D(1.0, 0.0, 0.0, 0.0, 1.0);
462 Eigen::Vector<real, 5>
dU;
463 dU << 0, 0, 0, 0, -100.0;
465 real alpha = IdealGasGetCompressionRatioPressure<3, 0, 5>(U,
dU, 0.0);
470 real vSqr = (Unew.segment<3>(1) / Unew(0)).squaredNorm();
471 real eInternal = Unew(4) - 0.5 * Unew(0) * vSqr;
472 CHECK(eInternal >= -1e-10);
479TEST_CASE(
"ViscousFlux: zero gradient produces zero flux")
481 auto U = primToCons3D(1.225, 100.0, -50.0, 25.0, 101325.0);
482 Eigen::Matrix<real, 3, 5> GradPrim = Eigen::Matrix<real, 3, 5>::Zero();
483 Eigen::Vector3d norm(1, 0, 0);
484 real mu = 1.8e-5, k = 0.025, Cp = 1005.0;
486 Eigen::Vector<real, 5> Flux;
487 ViscousFlux_IdealGas<3>(U, GradPrim, norm,
false, g_gamma, mu, 0.0,
false, k, Cp, Flux);
489 for (
int i = 0; i < 5; i++)
492 CHECK(Flux(i) == doctest::Approx(0.0).epsilon(1e-10));
Ideal-gas Riemann solvers, flux functions, and thermodynamic utilities for the compressible Euler / N...
void IdealGasThermal(real E, real rho, real vSqr, real gamma, real &p, real &asqr, real &H)
Thin wrapper delegating to IdealGas::IdealGasThermal.
the host side operators are provided as implemented
double real
Canonical floating-point scalar used throughout DNDSR (double precision).
Eigen::Matrix< real, 5, 1 > v
CHECK(alpha==doctest::Approx(0.0).epsilon(1e-14))
Eigen::Vector3d nx(1, 0, 0)
GasInviscidFlux_XY< 3 >(U, velo, vg, nx, p_val, Fn)
Eigen::Vector3d vg(0, 0, 0)
TEST_CASE("EulerGas eigenvectors: L*R = I for 3D")
Eigen::Vector< real, 5 > dU
GasInviscidFlux< 3 >(U, velo, vg, p_val, Fx)
Eigen::Vector< real, 5 > Fx
Eigen::Vector< real, 5 > Fn