19 if (!
H.has_melting_line()) {
20 throw ValueError(
"MeltingCaloric::sample: fluid has no melting line");
23 const double p_lo =
H.calc_melting_line(
iP_min,
iT, 0.0);
24 const double p_hi =
H.calc_melting_line(
iP_max,
iT, 0.0);
25 const double lo = std::log(p_lo), hi = std::log(p_hi);
37 for (std::size_t i = 0; i < n; ++i) {
38 const double lnp = lo + (hi - lo) *
static_cast<double>(i) /
static_cast<double>(n - 1);
39 const double p = std::exp(lnp);
42 Tm =
H.calc_melting_line(
iT,
iP, p);
51 const double rho =
H.rhomolar(), h =
H.hmolar(), s =
H.smolar();
52 if (!std::isfinite(rho) || !std::isfinite(h) || !std::isfinite(s))
continue;
64using CE_t = std::vector<CoolProp::superancillary::ChebyshevExpansion<Eigen::ArrayXd>>;
72 const double span = ln_hi - ln_lo;
73 const double eps_rel = 1e-9;
74 const double adj_lo = ln_lo + eps_rel * span;
75 const double adj_hi = ln_hi - eps_rel * span;
76 auto func = [&](
long ,
long ,
double lnp) ->
double {
77 const double p = std::exp(lnp);
78 const double Tm =
H.calc_melting_line(
iT,
iP, p);
82 auto exps = superancillary::detail::dyadic_splitting<decltype(func), CE_t>(8, func, adj_lo, adj_hi, 3, 1e-10, 2);
84 out.push_back(std::move(e));
90 const double p = std::exp(lnp);
93 return std::isfinite(
H.T()) && std::isfinite(
H.rhomolar()) && std::isfinite(
H.hmolar()) && std::isfinite(
H.smolar());
101 const std::vector<std::pair<double, double>>& pranges) {
102 std::vector<std::pair<double, double>> valid;
103 for (
const auto& pr : pranges) {
104 const double ln_lo = std::log(pr.first), ln_hi = std::log(pr.second);
105 if (!(ln_hi > ln_lo))
continue;
106 const double eps = 1e-9 * (ln_hi - ln_lo);
107 if (can_eval_all(
H, ln_lo + eps) && can_eval_all(
H, ln_hi - eps)) {
119 for (
const auto& pr : pranges) {
120 const double ln_lo = std::log(pr.first), ln_hi = std::log(pr.second);
121 if (!(ln_hi > ln_lo))
continue;
122 fit_part(
H, ln_lo, ln_hi, pick, exps);
124 std::sort(exps.begin(), exps.end(), [](
const auto& a,
const auto& b) { return a.xmin() < b.xmin(); });
125 return {std::move(exps)};
131 const double lo = approx.
xmin(), hi = approx.
xmax();
132 constexpr int Nscan = 200;
133 double best_lnp = lo, best_gap = 1e300;
134 double prev_lnp = lo, prev_h = approx.
eval(lo) - h_cache;
135 for (
int k = 1; k <= Nscan; ++k) {
136 const double cur_lnp = lo + (hi - lo) * k / Nscan;
137 const double cur_h = approx.
eval(cur_lnp) - h_cache;
138 if (prev_h * cur_h <= 0.0) {
139 double a = prev_lnp, b = cur_lnp, fa = prev_h;
140 for (
int it = 0; it < 60; ++it) {
141 const double m = 0.5 * (a + b);
142 const double fm = approx.
eval(m) - h_cache;
143 if (fa * fm <= 0.0) {
149 if ((b - a) < 1e-14 * (std::abs(a) + std::abs(b) + 1e-30))
break;
151 const double root_lnp = 0.5 * (a + b);
152 const double gap = std::abs(approx.
eval(root_lnp) - h_cache);
153 if (std::isfinite(gap) && gap < best_gap) {
173 std::vector<double> cand_lnp;
174 for (
const auto& pr :
m_h_approx->get_x_for_y(h_cache, 48, 100, 1e-12)) {
175 const double lnp = pr.first;
176 if (lnp >=
m_h_approx->xmin() && lnp <= m_h_approx->xmax()) cand_lnp.push_back(lnp);
178 if (cand_lnp.empty()) {
181 const double fb_lnp = scan_bisect_h(*
m_h_approx, h_cache);
182 const double fb_gap = std::abs(
m_h_approx->eval(fb_lnp) - h_cache);
184 if (std::isfinite(fb_gap) && fb_gap < std::abs(h_cache) * 0.01 + 1.0) {
185 cand_lnp.push_back(fb_lnp);
188 if (cand_lnp.empty())
return false;
190 double best_lnp = cand_lnp.front(), best_gap = 1e300;
192 for (
double lnp : cand_lnp) {
193 const double sgap = std::abs(
m_s_approx->eval(lnp) - s_cache);
194 if (std::isfinite(sgap) && sgap < best_gap) {
200 if (!found)
return false;
203 return std::isfinite(T0) && std::isfinite(rho0) && rho0 > 0;
208 if (!
H.has_melting_line())
return;
209 auto pranges =
H.get_melting_line_part_pranges();
210 const double p_lo_eos =
H.calc_melting_line(
iP_min,
iT, 0.0);
211 const double p_hi_eos =
H.calc_melting_line(
iP_max,
iT, 0.0);
212 for (
auto& pr : pranges) {
213 pr.first = std::max(pr.first, p_lo_eos);
214 pr.second = std::min(pr.second, p_hi_eos);
216 pranges.erase(std::remove_if(pranges.begin(), pranges.end(), [](
const std::pair<double, double>& pr) { return !(pr.second > pr.first); }),
218 if (pranges.empty())
return;
222 pranges = filter_valid_parts(
H, pranges);
223 if (pranges.empty())
return;
235 const int Nscan = 257;
236 for (
int k = 0; k <= Nscan; ++k) {
237 const double lnp = lo + (hi - lo) * k / Nscan;
239 if (std::isfinite(
T) &&
T < tmin) tmin =
T;
241 if (std::isfinite(tmin) && tmin < 1e299)
m_curve_Tmin = tmin;
249 static std::map<std::string, std::shared_ptr<MeltingCaloric>> cache;
250 static std::mutex mtx;
252 std::scoped_lock lk(mtx);
253 auto it = cache.find(key);
254 if (it != cache.end())
return it->second;
255 auto mc = std::make_shared<MeltingCaloric>();
259 cache[key] =
nullptr;
262 if (!mc->built()) mc =
nullptr;