16 throw ValueError(
"Mole fractions have not been set yet.");
25 std::vector<CoolPropDbl> Tbp, Qbp;
26 std::vector<std::size_t> Nbp;
40 if (max_sat_T.
rhomolar < crit.rhomolar && max_sat_T.
rhomolar < max_sat_p.rhomolar) {
42 if (max_sat_p.rhomolar < crit.rhomolar) {
46 Tbp.push_back(max_sat_p.T);
47 Tbp.push_back(crit.T);
53 Tbp.push_back(crit.T);
54 Tbp.push_back(max_sat_p.T);
66 for (std::size_t i = 0; i < Tbp.size() - 1; ++i) {
68 std::size_t N = Nbp[i];
74 for (std::size_t k_pe = 0; k_pe < N; ++k_pe) {
85 std::vector<CoolPropDbl>(1, 1.0), std::vector<CoolPropDbl>(1, 1.0));
90 std::vector<CoolPropDbl>(1, 1.0), std::vector<CoolPropDbl>(1, 1.0));
100 std::vector<CriticalState> critpts;
110 std::size_t failure_count = 0;
156 NR.
call(HEOS, IO.
y, IO.
x, IO);
161 bool dont_extrapolate =
false;
166 std::size_t iter = 0,
173 if (failure_count > 5) {
179 if (iter - iter0 > 0) {
182 if (dont_extrapolate) {
185 }
else if (iter - iter0 == 2) {
188 for (std::size_t i = 0; i < IO.
x.size() - 1; ++i)
192 }
else if (iter - iter0 == 3) {
195 for (std::size_t i = 0; i < IO.
x.size() - 1; ++i)
199 }
else if (iter - iter0 > 3) {
208 if (std::abs((T_linear - IO.
T) / IO.
T) > 0.1) {
211 factor = 1 + (factor - 1) / 2;
215 for (std::size_t i = 0; i < IO.
x.size() - 1; ++i)
221 if (IO.
x[i] < 0 || IO.
x[i] > 1) {
224 factor = 1 + (factor - 1) / 2;
232 IO.
x[IO.
x.size() - 1] = 1 - std::accumulate(IO.
x.begin(), IO.
x.end() - 1, 0.0);
239 NR.
call(HEOS, IO.
y, IO.
x, IO);
252 if (!env.T.empty() && std::abs(env.T[env.T.size() - 1] - IO.
T) > 100) {
253 throw ValueError(
"Change in temperature too large");
255 }
catch (std::exception& e) {
257 std::cout << e.what() <<
'\n';
263 throw ValueError(
format(
"Unable to calculate at least 4 points in phase envelope; quitting"));
266 factor = 1 + (factor - 1) / 2;
274 <<
" Ns " << IO.
Nsteps <<
" factor " << factor <<
'\n';
317 dont_extrapolate =
false;
322 factor = 1 + (factor - 1) / 10;
323 }
else if (IO.
Nsteps > 5) {
324 factor = 1 + (factor - 1) / 3;
325 }
else if (IO.
Nsteps <= 4) {
326 factor = 1 + (factor - 1) * 2;
329 factor = std::max(factor,
static_cast<CoolPropDbl>(1.01));
333 factor = std::min(factor,
static_cast<CoolPropDbl>(1.1));
338 CoolPropDbl max_fraction = *std::max_element(IO.
x.begin(), IO.
x.end());
339 if (iter > 4 && (IO.
p < env.p[0] || std::abs(1.0 - max_fraction) < 1e-9)) {
341 env.
closed = (IO.
p < env.p[0]);
343 std::cout <<
format(
"envelope built.\n");
344 std::cout <<
format(
"closest fraction to 1.0: distance %g\n", 1 - max_fraction);
368 double acceptable_pdiff = 0.5;
369 double acceptable_rhodiff = 0.25;
371 if (level ==
"veryfine") {
372 acceptable_pdiff = 0.1;
373 acceptable_rhodiff = 0.1;
375 if (level ==
"none") {
382 if ((std::abs(env.rhomolar_vap[i] / env.rhomolar_vap[i + 1] - 1) < acceptable_rhodiff)
383 && (std::abs(env.p[i] / env.p[i + 1] - 1) < acceptable_pdiff)) {
391 const double rhomolar_vap_start = env.rhomolar_vap[i], rhomolar_vap_end = env.rhomolar_vap[i + 1];
393 double factor = pow(rhomolar_vap_end / rhomolar_vap_start, 1.0 / N);
395 int failure_count = 0;
398 for (
double rhomolar_vap = rhomolar_vap_start * factor; rhomolar_vap < rhomolar_vap_end; rhomolar_vap *= factor) {
400 IO.
x.resize(IO.
y.size());
401 if (i < env.T.size() - 3) {
404 for (std::size_t j = 0; j < IO.
x.size() - 1; ++j) {
410 for (std::size_t j = 0; j < IO.
x.size() - 1; ++j) {
414 IO.
x[IO.
x.size() - 1] = 1 - std::accumulate(IO.
x.begin(), IO.
x.end() - 1, 0.0);
416 NR.
call(HEOS, IO.
y, IO.
x, IO);
435 if (failure_count > 0) {
438 }
while (i < env.T.size() - 1);
441 int _i =
static_cast<int>(i);
442 std::vector<double>
const *x =
nullptr, *y =
nullptr;
452 y = &(env.rhomolar_vap);
455 y = &(env.hmolar_vap);
458 y = &(env.smolar_vap);
461 y = &(env.cpmolar_vap);
464 y = &(env.cvmolar_vap);
467 y = &(env.viscosity_vap);
470 y = &(env.conductivity_vap);
473 y = &(env.speed_sound_vap);
476 throw ValueError(
"Pointer to vector y is unset in is_inside");
479 double inval = value1;
489 x = &(env.rhomolar_vap);
492 x = &(env.hmolar_vap);
495 x = &(env.smolar_vap);
498 throw ValueError(
"Pointer to vector x is unset in is_inside");
500 if (_i + 2 >=
static_cast<int>(y->size())) {
503 if (_i + 1 >=
static_cast<int>(y->size())) {
510 double outval =
CubicInterp(*x, *y, _i - 1, _i, _i + 1, _i + 2, inval);
511 i =
static_cast<std::size_t
>(_i);
525 std::size_t imax = 0;
530 std::size_t iTmax = std::distance(env.T.begin(), std::max_element(env.T.begin(), env.T.end()));
533 std::size_t ipmax = std::distance(env.p.begin(), std::max_element(env.p.begin(), env.p.end()));
538 env.
TypeI = env.p[env.p.size() - 1] < env.p[ipmax];
543 for (
int imaxima = 0; imaxima <= 1; ++imaxima) {
544 maxima_points maxima;
545 if (imaxima == PMAX_SAT) {
547 }
else if (imaxima == TMAX_SAT) {
550 throw ValueError(
"I don't understand your maxima index");
555 if (maxima == TMAX_SAT) {
557 if (iTmax > env.T.size() - 3) {
560 spline.
add_4value_constraints(env.rhomolar_vap[iTmax - 1], env.rhomolar_vap[iTmax], env.rhomolar_vap[iTmax + 1],
561 env.rhomolar_vap[iTmax + 2], env.T[iTmax - 1], env.T[iTmax], env.T[iTmax + 1], env.T[iTmax + 2]);
564 if (ipmax > env.p.size() - 3) {
567 spline.
add_4value_constraints(env.rhomolar_vap[ipmax - 1], env.rhomolar_vap[ipmax], env.rhomolar_vap[ipmax + 1],
568 env.rhomolar_vap[ipmax + 2], env.p[ipmax - 1], env.p[ipmax], env.p[ipmax + 1], env.p[ipmax + 2]);
576 double rho0 = _HUGE, rho1 = _HUGE, rho2 = _HUGE;
577 solve_cubic(0, 3 * spline.
a, 2 * spline.
b, spline.
c, Nsoln, rho0, rho1, rho2);
584 }
else if (Nsoln == 2) {
585 if (
is_in_closed_range(env.rhomolar_vap[imax - 1], env.rhomolar_vap[imax + 1], rho0)) {
588 if (
is_in_closed_range(env.rhomolar_vap[imax - 1], env.rhomolar_vap[imax + 1], rho1)) {
592 throw ValueError(
"More than 2 solutions found");
599 maxima_points maxima;
604 this->HEOS = &HEOS, this->imax = imax;
606 double call(
double rhomolar_vap)
override {
613 if (imax >= env.T.size() - 2) {
618 for (std::size_t i = 0; i < IO.
x.size() - 1; ++i)
622 IO.
x[IO.
x.size() - 1] = 1 - std::accumulate(IO.
x.begin(), IO.
x.end() - 1, 0.0);
623 NR.
call(*HEOS, IO.
y, IO.
x, IO);
624 if (maxima == TMAX_SAT) {
632 solver_resid resid(HEOS, imax, maxima);
638 if (rho > env.rhomolar_vap[imax]) {
642 env.
insert_variables(resid.IO.T, resid.IO.p, resid.IO.rhomolar_liq, resid.IO.rhomolar_vap, resid.IO.hmolar_liq, resid.IO.hmolar_vap,
643 resid.IO.smolar_liq, resid.IO.smolar_vap, resid.IO.x, resid.IO.y, imax);
651 env.
iTsat_max = std::distance(env.T.begin(), std::max_element(env.T.begin(), env.T.end()));
654 env.
ipsat_max = std::distance(env.p.begin(), std::max_element(env.p.begin(), env.p.end()));
659 std::vector<std::pair<std::size_t, std::size_t>> intersections;
661 for (std::size_t i = 0; i < env.p.size() - 1; ++i) {
662 bool matched =
false;
689 intersections.emplace_back(i, i + 1);
692 return intersections;
695 std::size_t& iclosest,
SimpleState& closest_state) {
697 std::vector<std::pair<std::size_t, std::size_t>> intersections =
find_intersections(env, iInput1, value1);
700 std::cout <<
format(
"is_inside(%Lg,%Lg); iTsat_max=%d; ipsat_max=%d\n", value1, value2, env.
iTsat_max, env.
ipsat_max);
711 if (intersections.size() == 0) {
712 throw ValueError(
format(
"Input is out of range for primary value [%Lg], inputs were (%s,%Lg,%s,%Lg); no intersections found", value1,
719 if (intersections.size() % 2 != 0) {
720 throw ValueError(
"Input is weird; odd number of intersections found");
724 if (intersections.size() % 2 == 0) {
725 if (intersections.size() != 2) {
726 throw ValueError(
"for now only even value accepted is 2");
728 std::vector<std::size_t> other_indices(4, 0);
729 std::vector<double>
const* y =
nullptr;
730 std::vector<double> other_values(4, 0);
731 other_indices[0] = intersections[0].first;
732 other_indices[1] = intersections[0].second;
733 other_indices[2] = intersections[1].first;
734 other_indices[3] = intersections[1].second;
744 y = &(env.rhomolar_vap);
747 y = &(env.hmolar_vap);
750 y = &(env.smolar_vap);
753 throw ValueError(
"Pointer to vector y is unset in is_inside");
756 other_values[0] = (*y)[other_indices[0]];
757 other_values[1] = (*y)[other_indices[1]];
758 other_values[2] = (*y)[other_indices[2]];
759 other_values[3] = (*y)[other_indices[3]];
761 CoolPropDbl min_other = *(std::min_element(other_values.begin(), other_values.end()));
762 CoolPropDbl max_other = *(std::max_element(other_values.begin(), other_values.end()));
765 std::cout <<
format(
"is_inside: min: %Lg max: %Lg val: %Lg\n", min_other, max_other, value2);
772 std::vector<CoolPropDbl> d(4, 0);
773 d[0] = std::abs(other_values[0] - value2);
774 d[1] = std::abs(other_values[1] - value2);
775 d[2] = std::abs(other_values[2] - value2);
776 d[3] = std::abs(other_values[3] - value2);
779 std::size_t idist = std::distance(d.begin(), std::min_element(d.begin(), d.end()));
781 iclosest = other_indices[idist];
786 closest_state.
T = env.T[iclosest];
787 closest_state.
p = env.p[iclosest];
788 closest_state.
rhomolar = env.rhomolar_vap[iclosest];
789 closest_state.
hmolar = env.hmolar_vap[iclosest];
790 closest_state.
smolar = env.smolar_vap[iclosest];
791 closest_state.
Q = env.Q[iclosest];
794 std::cout <<
format(
"is_inside: it is not inside") <<
'\n';
804 if (std::abs(y1 - value2) < std::abs(y2 - value2)) {
805 iclosest = intersections[0].first;
807 iclosest = intersections[1].first;
812 closest_state.
T = env.T[iclosest];
813 closest_state.
p = env.p[iclosest];
814 closest_state.
rhomolar = env.rhomolar_vap[iclosest];
815 closest_state.
hmolar = env.hmolar_vap[iclosest];
816 closest_state.
smolar = env.smolar_vap[iclosest];
817 closest_state.
Q = env.Q[iclosest];
824 throw ValueError(
"You have a funny number of intersections in is_inside");