CoolProp 8.0.0
An open-source fluid property and humid air property database
SaturationSurrogate.h
Go to the documentation of this file.
1#ifndef COOLPROP_SBTL_SATURATION_SURROGATE_H
2#define COOLPROP_SBTL_SATURATION_SURROGATE_H
3
4#include <cstddef>
5#include <memory>
6
8
9namespace CoolProp {
10namespace region {
11class CubicSplineCurve;
12} // namespace region
13
14namespace sbtl {
15
16// SaturationSurrogate — a small cubic-spline surrogate for the
17// saturation dome, built by sampling QT_INPUTS on a source backend at
18// construction. Mirrors the eval_sat / get_T_from_p shape of
19// `superancillary::SuperAncillary` so the SVDSBTL backend can use it
20// as a drop-in replacement when the source backend doesn't expose a
21// SuperAncillary (REFPROP today; SRK / PR / etc. in the future).
22//
23// **Why this exists.** Every dome-touching query through
24// SVDSBTL&<source-without-SA> falls through to source.update(PQ_INPUTS
25// / QT_INPUTS) for sat endpoints — REFPROP's PQ flash is ~1-5 ms per
26// call, whereas an SA-style spline eval is ~50 ns. Closing this 4-5
27// orders-of-magnitude gap is the point of the surrogate.
28//
29// **What is sampled.** At each of `n_knots` temperatures spanning
30// [T_triple * (1 + 1e-6), 0.9999 * T_crit], the source is queried via
31// QT_INPUTS at Q=0 and Q=1, capturing P, D, H, S, U on both sides.
32// The additive 1e-6 offset on T_triple keeps the first probe one ULP-
33// class step inside the source's flash range — some backends report
34// T_triple at the boundary of where QT_INPUTS converges, and a bare
35// T_triple probe fails on those.
36// The 0.9999 multiplier on T_crit keeps the highest knot well clear of
37// the sqrt-singularity at the critical point — cubic splines can't
38// represent the singularity, so the surrogate trades accuracy near
39// T_crit for clean interpolation everywhere else.
40//
41// **Spacing.** Chebyshev-Lobatto knot distribution in T over
42// [T_triple, 0.9999 * T_crit] — clusters knots toward both endpoints
43// to absorb the sharp density bend approaching T_crit (saturated-rho
44// is the budget-binding property; log-T spacing missed 1e-6 there).
45// Cubic spline interpolation between the knots.
46//
47// **Accuracy target.** Relative error <= 1e-6 vs source QT_INPUTS on
48// smooth-fluid probes well away from the critical point. Accuracy
49// degrades as T -> T_crit (sqrt-singularity not captured by
50// polynomials); callers querying that slice should use the
51// critical-patch source fallback in SVDSBTLBackend instead.
52//
53// **Units.** Matches SuperAncillary's molar convention: P in Pa, D in
54// mol/m^3, H/U in J/mol, S in J/(mol K). Caller (SVDSBTLBackend)
55// converts to mass basis as needed downstream.
57{
58 public:
59 // Build a surrogate by sampling `src` at `n_knots` temperatures.
60 // Returns nullptr on build failure (degenerate triple/critical pair,
61 // n_knots < 4, or source rejects too many QT_INPUTS probes to form a
62 // valid spline). Mutates `src` state during sampling.
63 //
64 // Source-backend requirements: must support QT_INPUTS, must report
65 // T_triple() and T_critical() correctly. HEOS, REFPROP, IF97 all
66 // satisfy these; mixtures do not (no single sat dome).
67 [[nodiscard]] static std::unique_ptr<SaturationSurrogate> build_from_source(::CoolProp::AbstractState& src, std::size_t n_knots = 96);
68
74
75 // Inverse pressure -> temperature. Same contract as
76 // SuperAncillary::get_T_from_p — argument is pressure in Pa (NOT its
77 // log), returns saturation temperature in K. Throws std::out_of_range
78 // if `p` is outside the surrogate's [p_min, p_max] build range.
79 [[nodiscard]] double get_T_from_p(double p) const;
80
81 // Evaluate a saturated property at (T, side). Matches
82 // SuperAncillary::eval_sat — `what` is in {'P','D','H','S','U'},
83 // `side` is 0 (liquid) or 1 (vapor). Throws std::invalid_argument
84 // on bad keys / sides, std::out_of_range on T outside the build range.
85 [[nodiscard]] double eval_sat(double T, char what, int side) const;
86
87 // Build interval, exposed for diagnostics + tests.
88 [[nodiscard]] double T_min() const noexcept;
89 [[nodiscard]] double T_max() const noexcept;
90 [[nodiscard]] std::size_t n_knots() const noexcept;
91
92 private:
94 struct Impl;
95 std::unique_ptr<Impl> impl_;
96};
97
98} // namespace sbtl
99} // namespace CoolProp
100
101#endif // COOLPROP_SBTL_SATURATION_SURROGATE_H