11 char prop_key,
short Q,
double output_scale) {
13 throw std::invalid_argument(
"SuperancillaryBoundaryCurve::build: null SuperAncillary handle");
15 if (!(p_min > 0.0) || !(p_max > p_min)) {
16 throw std::invalid_argument(
"SuperancillaryBoundaryCurve::build: need 0 < p_min < p_max");
18 if (Q != 0 && Q != 1) {
19 throw std::invalid_argument(
"SuperancillaryBoundaryCurve::build: Q must be 0 (liquid) or 1 (vapor)");
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));
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)) {
39 b_min = std::min(b_min, y);
40 b_max = std::max(b_max, y);
48 if (finite_probe_count == 0) {
49 throw std::runtime_error(
"SuperancillaryBoundaryCurve::build: SuperAncillary rejected all probes in the requested p range");
58 const double eps = std::max(std::abs(b_min) * std::numeric_limits<double>::epsilon(), std::numeric_limits<double>::min());
62 return std::make_unique<SuperancillaryBoundaryCurve>(std::move(sa), p_min, p_max, prop_key, Q, output_scale, b_min, b_max);