CoolProp 8.0.0
An open-source fluid property and humid air property database
SuperancillaryBoundaryCurve.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cmath>
5#include <stdexcept>
6
7namespace CoolProp {
8namespace region {
9
10std::unique_ptr<SuperancillaryBoundaryCurve> SuperancillaryBoundaryCurve::build(std::shared_ptr<SuperAncillary_t> sa, double p_min, double p_max,
11 char prop_key, short Q, double output_scale) {
12 if (!sa) {
13 throw std::invalid_argument("SuperancillaryBoundaryCurve::build: null SuperAncillary handle");
14 }
15 if (!(p_min > 0.0) || !(p_max > p_min)) {
16 throw std::invalid_argument("SuperancillaryBoundaryCurve::build: need 0 < p_min < p_max");
17 }
18 if (Q != 0 && Q != 1) {
19 throw std::invalid_argument("SuperancillaryBoundaryCurve::build: Q must be 0 (liquid) or 1 (vapor)");
20 }
21
22 // Probe the curve at log-spaced sample points to pre-compute the
23 // (b_min, b_max) AABB. Cheap (Chebyshev eval ~ns each); 32 probes
24 // give a tight bound for any smooth boundary.
25 constexpr std::size_t kProbes = 32;
26 double b_min = std::numeric_limits<double>::infinity();
27 double b_max = -std::numeric_limits<double>::infinity();
28 std::size_t finite_probe_count = 0;
29 const double log_p_min = std::log(p_min);
30 const double log_p_max = std::log(p_max);
31 for (std::size_t k = 0; k < kProbes; ++k) {
32 const double f = static_cast<double>(k) / static_cast<double>(kProbes - 1);
33 const double p = std::exp(log_p_min + f * (log_p_max - log_p_min));
34 try {
35 const double T_sat = sa->get_T_from_p(p);
36 const double y = sa->eval_sat(T_sat, prop_key, Q) * output_scale;
37 if (std::isfinite(y)) {
38 ++finite_probe_count;
39 b_min = std::min(b_min, y);
40 b_max = std::max(b_max, y);
41 }
42 } catch (...) { // NOLINT(bugprone-empty-catch)
43 // SA may reject queries near the critical point or
44 // exotically close to the triple point. Skip and keep
45 // the surrounding probes.
46 }
47 }
48 if (finite_probe_count == 0) {
49 throw std::runtime_error("SuperancillaryBoundaryCurve::build: SuperAncillary rejected all probes in the requested p range");
50 }
51 if (b_min == b_max) {
52 // Pathological but representable: all finite probes yielded
53 // the same value (truly constant curve, or near-flat one
54 // whose variation is below the SA's noise floor). Synthesize
55 // a tiny inflation so the AABB has non-zero b-extent and
56 // downstream Region machinery doesn't trip on degenerate
57 // span checks. 1 ULP of inflation per side is enough.
58 const double eps = std::max(std::abs(b_min) * std::numeric_limits<double>::epsilon(), std::numeric_limits<double>::min());
59 b_min -= eps;
60 b_max += eps;
61 }
62 return std::make_unique<SuperancillaryBoundaryCurve>(std::move(sa), p_min, p_max, prop_key, Q, output_scale, b_min, b_max);
63}
64
65} // namespace region
66} // namespace CoolProp