CoolProp 8.0.0
An open-source fluid property and humid air property database
CoolProp-Tests-AirCritical.cpp
Go to the documentation of this file.
1// Dense round-trip consistency coverage of Air's critical region.
2//
3// Air is modelled as a PSEUDO-PURE fluid, so its property surfaces are not
4// perfectly continuous near the critical point (the underlying mixture has a
5// temperature glide that the pseudo-pure approximation collapses). That makes
6// the flash routines fragile in this region; in particular the D+{H,S,U}
7// back-flashes (HSU_D_flash) used to throw "p is not a valid number" at
8// near-critical density because pseudo-pure fluids cannot take the
9// is_pure()-gated superancillary happy path and fall onto the legacy
10// saturation_D_pure path, which is unstable near rho_c.
11//
12// This harness sweeps a dense (T, p) grid spanning the critical region
13// (T/Tc in [0.96, 2.0], p/pc in [0.5, 5.0]) and, for every single-phase grid
14// point, requires that each of the seven non-trivial back-flashes recovers the
15// forward (T, rho). Grid points that land in the two-phase dome are skipped:
16// PT inputs there are genuinely unsupported for a pseudo-pure fluid.
17//
18// Built into CatchTestRunner when COOLPROP_CATCH_MODULE=ON. Run via
19// ./build_catch/CatchTestRunner "[air_critical]"
20
22#include "CoolProp/CoolProp.h"
24
25#if defined(ENABLE_CATCH)
26# include <catch2/catch_all.hpp>
27
28# include <memory>
29# include <cmath>
30# include <cstdio>
31# include <array>
32
33TEST_CASE("Air critical-region round-trip consistency (pseudo-pure)", "[air_critical][HSU_D]") {
34 using namespace CoolProp;
35
36 auto fwd = std::shared_ptr<AbstractState>(AbstractState::factory("HEOS", "Air"));
37 auto rt = std::shared_ptr<AbstractState>(AbstractState::factory("HEOS", "Air"));
38
39 const double Tc = fwd->T_critical();
40 const double pc = fwd->p_critical();
41
42 // Round-trip tolerance. The critical region is ill-conditioned (dp/drho -> 0
43 // near rho_c), so a small relative slack is used; the production solvers stay
44 // well inside it everywhere on this grid.
45 const double tol = 2e-4;
46
47 const int NT = 60, NP = 40;
48 std::size_t single_phase = 0, two_phase_skipped = 0;
49
50 for (int i = 0; i < NT; ++i) {
51 const double T = (0.96 + (2.0 - 0.96) * i / (NT - 1)) * Tc;
52 for (int j = 0; j < NP; ++j) {
53 const double p = (0.5 + (5.0 - 0.5) * j / (NP - 1)) * pc;
54
55 // Forward PT defines the reference single-phase state. A throw here
56 // means the point fell in/at the two-phase dome (PT not supported for
57 // a pseudo-pure fluid) -- not a back-flash defect -- so skip it.
58 try {
59 fwd->update(PT_INPUTS, p, T);
60 } catch (...) {
61 ++two_phase_skipped;
62 continue;
63 }
64 const double rho = fwd->rhomolar();
65 const double h = fwd->hmolar();
66 const double s = fwd->smolar();
67 const double u = fwd->umolar();
68 if (!ValidNumber(rho) || !ValidNumber(h) || !ValidNumber(s) || !ValidNumber(u)) {
69 continue;
70 }
71 ++single_phase;
72
73 // Each back-flash must recover the forward (T, rho).
74 struct Case
75 {
76 const char* name;
77 input_pairs pair;
78 double v1, v2;
79 };
80 const std::array<Case, 7> cases = {{
81 {"DmolarHmolar", DmolarHmolar_INPUTS, rho, h},
82 {"DmolarSmolar", DmolarSmolar_INPUTS, rho, s},
83 {"DmolarUmolar", DmolarUmolar_INPUTS, rho, u},
84 {"HmolarP", HmolarP_INPUTS, h, p},
85 {"PSmolar", PSmolar_INPUTS, p, s},
86 {"PUmolar", PUmolar_INPUTS, p, u},
87 {"DmolarP", DmolarP_INPUTS, rho, p},
88 }};
89 for (const auto& c : cases) {
90 CAPTURE(c.name, T, p, T / Tc, p / pc, rho);
91 bool threw = false;
92 try {
93 rt->update(c.pair, c.v1, c.v2);
94 } catch (const std::exception& e) {
95 threw = true;
96 FAIL_CHECK("back-flash threw: " << e.what());
97 }
98 if (threw) {
99 continue;
100 }
101 CHECK(rt->T() == Catch::Approx(T).epsilon(tol));
102 CHECK(rt->rhomolar() == Catch::Approx(rho).epsilon(tol));
103 }
104 }
105 }
106
107 std::printf("[air_critical] Air: %zu single-phase points round-tripped (x7 back-flashes), %zu two-phase PT points skipped\n", single_phase,
108 two_phase_skipped);
109 // Guard against the grid silently collapsing (e.g. forward PT failing wholesale).
110 CHECK(single_phase > 2000);
111}
112
113#endif // ENABLE_CATCH