1#ifndef COOLPROP_REGION_SUPERANCILLARY_BOUNDARY_CURVE_H
2#define COOLPROP_REGION_SUPERANCILLARY_BOUNDARY_CURVE_H
63 double b_min,
double b_max)
64 : sa_(std::move(sa)), p_min_(p_min), p_max_(p_max), prop_key_(prop_key), Q_(Q), output_scale_(output_scale), b_min_(b_min), b_max_(b_max) {
73 throw std::invalid_argument(
"SuperancillaryBoundaryCurve: null SuperAncillary handle");
81 if (!(p_min_ > 0.0) || !(p_max_ > p_min_)) {
82 throw std::invalid_argument(
"SuperancillaryBoundaryCurve: need 0 < p_min < p_max");
97 static std::unique_ptr<SuperancillaryBoundaryCurve>
build(std::shared_ptr<SuperAncillary_t> sa,
double p_min,
double p_max,
char prop_key,
98 short Q,
double output_scale);
100 [[nodiscard]]
double eval(
double p)
const noexcept override {
102 const double T_sat = sa_->get_T_from_p(p);
103 return sa_->eval_sat(T_sat, prop_key_, Q_) * output_scale_;
109 [[nodiscard]]
double eval_da(
double p)
const noexcept override {
121 const double h = std::max(1e-7 * std::abs(p), 1.0);
122 const double p_hi = std::min(p + h, p_max_);
123 const double p_lo = std::max(p - h, p_min_);
124 if (!(p_hi > p_lo)) {
127 const double y_plus =
eval(p_hi);
128 const double y_minus =
eval(p_lo);
129 if (!std::isfinite(y_plus) || !std::isfinite(y_minus)) {
132 return (y_plus - y_minus) / (p_hi - p_lo);
146 [[nodiscard]]
double eval_fast(
double p)
const noexcept override {
147 const double lp = std::log(p);
148 const double idx_f = (lp - log_p_min_) * inv_log_p_step_;
149 if (!(idx_f > 0.0)) {
150 return surrogate_table_.front();
152 const auto last =
static_cast<double>(kSurrogatePoints - 1);
154 return surrogate_table_.back();
156 const auto i =
static_cast<std::size_t
>(idx_f);
157 const double frac = idx_f -
static_cast<double>(i);
158 const double y0 = surrogate_table_[i];
159 const double y1 = surrogate_table_[i + 1];
160 return y0 + frac * (y1 - y0);
163 [[nodiscard]] std::pair<double, double>
bounds() const noexcept
override {
164 return {b_min_, b_max_};
166 [[nodiscard]] std::pair<double, double>
a_range() const noexcept
override {
167 return {p_min_, p_max_};
185 return State{p_min_, p_max_, prop_key_, Q_, output_scale_, b_min_, b_max_};
187 static std::unique_ptr<SuperancillaryBoundaryCurve>
from_state(
State s, std::shared_ptr<SuperAncillary_t> sa) {
193 throw std::invalid_argument(
"SuperancillaryBoundaryCurve::from_state: null SuperAncillary handle");
204 static constexpr std::size_t kSurrogatePoints = 1024;
217 void build_surrogate_() {
218 surrogate_table_.resize(kSurrogatePoints);
219 log_p_min_ = std::log(p_min_);
220 const double log_p_max = std::log(p_max_);
221 const double log_p_step = (log_p_max - log_p_min_) /
static_cast<double>(kSurrogatePoints - 1);
222 inv_log_p_step_ = 1.0 / log_p_step;
223 for (std::size_t k = 0; k < kSurrogatePoints; ++k) {
224 const double lp = log_p_min_ +
static_cast<double>(k) * log_p_step;
225 surrogate_table_[k] =
eval(std::exp(lp));
229 std::shared_ptr<SuperAncillary_t> sa_;
234 double output_scale_;
239 std::vector<double> surrogate_table_;
240 double log_p_min_ = 0.0;
241 double inv_log_p_step_ = 0.0;