54namespace superancillary {
65double toms748(
const std::function<
double(
double)>& f,
double a,
double b,
double fa,
double fb,
unsigned int bits, std::size_t max_iter);
68template <
typename Matrix>
74 D = Matrix::Identity(A.rows(), A.cols());
75 bool converged =
false;
78 for (Eigen::Index i = 0; i < A.rows(); ++i) {
79 double c = Aprime.col(i).template lpNorm<p>();
80 double r = Aprime.row(i).template lpNorm<p>();
92 if (!std::isfinite(c) || !std::isfinite(r) || c == 0.0 || r == 0.0) {
95 double s = pow(c, p) + pow(r, p);
97 while (c < r / beta) {
102 while (c >= r * beta) {
107 if (pow(c, p) + pow(r, p) < 0.95 * s) {
118 }
while (!converged);
122 auto N = coeffs.size() - 1;
124 throw std::invalid_argument(
"A.rows() != N");
130 A.col(N - 1) = -coeffs.head(N) / (2.0 * coeffs(N));
131 A(N - 2, N - 1) += 0.5;
133 for (
int j = 1; j < N - 1; ++j) {
139 Eigen::ArrayXd coeffs_ = Eigen::Map<const Eigen::ArrayXd>(&coeffs[0], coeffs.size());
152 Eigen::MatrixXd L(N + 1, N + 1);
153 Eigen::MatrixXd U(N + 1, N + 1);
154 for (std::size_t j = 0; j <= N; ++j) {
155 for (std::size_t k = j; k <= N; ++k) {
156 double p_j = (j == 0 || j == N) ? 2 : 1;
157 double p_k = (k == 0 || k == N) ? 2 : 1;
158 double cosjPikN = cos((j * EIGEN_PI * k) / N);
159 L(j, k) = 2.0 / (p_j * p_k * N) * cosjPikN;
168 return std::make_tuple(L, U);
172 Eigen::Map<const Eigen::ArrayXd>
X(&x[0], x.size());
173 return X.tail(M).matrix().norm() /
X.head(M).matrix().norm();
177 return x.tail(M).matrix().norm() / x.head(M).matrix().norm();
184template <
typename Function,
typename Container>
185inline auto dyadic_splitting(
const std::size_t N,
const Function& func,
const double xmin,
const double xmax,
const int M = 3,
186 const double tol = 1e-12,
const int max_refine_passes = 8,
187 const std::optional<std::function<
void(
int,
const Container&)>>& callback = std::nullopt) -> Container {
188 using CE_t = std::decay_t<
decltype(Container().front())>;
189 using ArrayType = std::decay_t<
decltype(CE_t().coeff())>;
191 Eigen::MatrixXd Lmat, Umat;
194 auto builder = [&](
double xmin,
double xmax) -> CE_t {
195 auto get_nodes_realworld = [N, xmin, xmax]() -> Eigen::ArrayXd {
196 Eigen::ArrayXd nodes = (Eigen::ArrayXd::LinSpaced(N + 1, 0,
static_cast<double>(N)).array() * EIGEN_PI / N).cos();
197 return ((xmax - xmin) * nodes + (xmax + xmin)) * 0.5;
200 Eigen::ArrayXd x = get_nodes_realworld();
204 for (
auto j = 0L; j < x.size(); ++j) {
205 x(j) = func(j,
static_cast<long>(x.size()), x(j));
209 Eigen::ArrayXd c = Lmat * x.matrix();
212 if (!c.allFinite()) {
213 throw std::invalid_argument(
"At least one coefficient is non-finite");
215 if constexpr (std::is_same_v<ArrayType, std::vector<double>>) {
216 return {xmin, xmax, std::vector<double>(&c[0], &c[0] + c.size())};
219 return {xmin, xmax, c};
224 Container expansions;
225 expansions.emplace_back(builder(xmin, xmax));
228 for (
int refine_pass = 0; refine_pass < max_refine_passes; ++refine_pass) {
229 bool all_converged =
true;
231 for (
int iexpansion =
static_cast<int>(expansions.size()) - 1; iexpansion >= 0; --iexpansion) {
232 auto& expan = expansions[iexpansion];
237 auto xmid = (expan.xmin() + expan.xmax()) / 2;
238 CE_t newleft{builder(expan.xmin(), xmid)};
239 CE_t newright{builder(xmid, expan.xmax())};
241 expansions.at(iexpansion) = std::move(newleft);
242 expansions.insert(expansions.begin() + iexpansion + 1, newright);
243 all_converged =
false;
248 callback.value()(refine_pass, expansions);
269template <
typename ArrayType>
305 template <
typename T>
308 T xscaled = (2.0 * x - (m_xmax + m_xmin)) / (m_xmax - m_xmin);
309 int Norder =
static_cast<int>(m_coeff.size()) - 1;
310 T u_k = 0, u_kp1 = m_coeff[Norder], u_kp2 = 0;
311 for (
int k = Norder - 1; k > 0; k--) {
313 u_k = 2.0 * xscaled * u_kp1 - u_kp2 + m_coeff[k];
318 T retval = m_coeff[0] + xscaled * u_kp1
324 template <
typename T>
326 if (x.size() != y.size()) {
327 throw std::invalid_argument(
"x and y are not the same size");
329 for (
auto i = 0; i < x.size(); ++i) {
335 template <
typename T>
337 for (std::size_t i = 0; i < N; ++i) {
343 template <
typename T>
345 if (x.size() != y.size()) {
346 throw std::invalid_argument(
"x and y are not the same size");
353 Eigen::Index N = m_coeff.size() - 1;
354 Eigen::ArrayXd nodes = (Eigen::ArrayXd::LinSpaced(N + 1, 0, N).array() * EIGEN_PI / N).cos();
355 return ((m_xmax - m_xmin) * nodes + (m_xmax + m_xmin)) * 0.5;
371 auto solve_for_x_count(
double y,
double a,
double b,
unsigned int bits, std::size_t max_iter,
double boundsytol)
const {
372 std::size_t counter = 0;
373 auto f = [&](
double x) {
378 if (std::abs(fa) < boundsytol) {
379 return std::make_tuple(a, std::size_t{1});
382 if (std::abs(fb) < boundsytol) {
383 return std::make_tuple(b, std::size_t{2});
386 return std::make_tuple(root, counter);
391 auto solve_for_x(
double y,
double a,
double b,
unsigned int bits, std::size_t max_iter,
double boundsytol)
const {
396 template <
typename T,
typename CountsT>
397 auto solve_for_x_many(
const T& y,
double a,
double b,
unsigned int bits, std::size_t max_iter,
double boundsytol,
T& x, CountsT& counts)
const {
398 if (x.size() != y.size()) {
399 throw std::invalid_argument(
"x and y are not the same size");
401 for (
auto i = 0; i < x.size(); ++i) {
402 std::tie(x(i), counts(i)) =
solve_for_x_count(y(i), a, b, bits, max_iter, boundsytol);
407 template <
typename T,
typename CountsT>
408 auto solve_for_x_manyC(
const T y[], std::size_t N,
double a,
double b,
unsigned int bits, std::size_t max_iter,
double boundsytol,
T x[],
409 CountsT counts[])
const {
410 for (std::size_t i = 0; i < N; ++i) {
411 std::tie(x[i], counts[i]) =
solve_for_x_count(y[i], a, b, bits, max_iter, boundsytol);
418 ArrayType c = m_coeff;
419 for (std::size_t deriv_counter = 0; deriv_counter < Nderiv; ++deriv_counter) {
420 std::size_t N = c.size() - 1,
423 for (std::size_t r = 0; r <= Nd; ++r) {
425 for (std::size_t k = r + 1; k <= N; ++k) {
427 if ((k - r) % 2 == 1) {
428 cd[r] += 2 * k * c[k];
436 cd[r] /= (m_xmax - m_xmin) / 2.0;
448static_assert(std::is_move_assignable_v<ChebyshevExpansion<std::vector<double>>>);
488template <
typename ArrayType = Eigen::ArrayXd>
492 const double thresh_imag = 1e-15;
493 std::vector<ChebyshevExpansion<ArrayType>> m_expansions;
494 std::vector<double> m_x_at_extrema;
495 std::vector<IntervalMatch> m_monotonic_intervals;
497 Eigen::ArrayXd head(
const Eigen::ArrayXd& c, Eigen::Index N)
const {
500 std::vector<double> head(
const std::vector<double>& c, Eigen::Index N)
const {
501 return {c.begin(), c.begin() + N};
508 std::vector<double> determine_extrema(
const std::decay_t<
decltype(m_expansions)>& expansions,
double thresh_im)
const {
509 std::vector<double> x_at_extrema;
510 Eigen::MatrixXd companion_matrix, cprime, D;
511 for (
auto& expan : expansions) {
513 auto cd = expan.do_derivs(1);
515 int ilastnonzero =
static_cast<int>(cd.size()) - 1;
516 for (
int i =
static_cast<int>(cd.size()) - 1; i >= 0; --i) {
517 if (std::abs(cd[i]) != 0) {
522 if (ilastnonzero !=
static_cast<int>(cd.size() - 1)) {
523 cd = head(cd, ilastnonzero);
527 if (companion_matrix.rows() !=
static_cast<int>(cd.size() - 1)) {
528 companion_matrix.resize(cd.size() - 1, cd.size() - 1);
529 companion_matrix.setZero();
530 D.resizeLike(companion_matrix);
532 cprime.resizeLike(companion_matrix);
536 cprime = companion_matrix;
539 for (
auto& root : companion_matrix.eigenvalues()) {
541 auto re_n11 = root.real(), im = root.imag();
542 if (std::abs(im) < thresh_im) {
543 if (re_n11 >= -1 && re_n11 <= 1) {
544 x_at_extrema.push_back(((expan.xmax() - expan.xmin()) * re_n11 + (expan.xmax() + expan.xmin())) / 2.0);
549 std::sort(x_at_extrema.begin(), x_at_extrema.end());
557 std::vector<IntervalMatch> build_monotonic_intervals(
const std::vector<double>& x_at_extrema)
const {
558 std::vector<IntervalMatch> intervals;
560 auto sort = [](
double& x,
double& y) {
567 auto xmin = m_expansions.front().xmin();
568 auto xmax = m_expansions.back().xmax();
583 intervals.push_back(im);
585 auto newx = x_at_extrema;
586 newx.insert(newx.begin(), m_expansions.front().xmin());
587 newx.insert(newx.end(), m_expansions.back().xmax());
590 auto interval_intersection = [](
const auto& t1,
const auto& t2) {
591 auto a = std::max(t1.xmin(), t2.xmin);
592 auto b = std::min(t1.xmax(), t2.xmax);
593 return std::make_tuple(a, b);
596 for (
auto j = 0; j < static_cast<int>(newx.size() - 1); ++j) {
597 double xmin = newx[j],
xmax = newx[j + 1];
601 for (
auto i = 0UL; i < m_expansions.size(); ++i) {
606 auto [a, b] = interval_intersection(m_expansions[i], A{
xmin,
xmax});
608 const auto& e = m_expansions[i];
613 double ymin = e.eval(a), ymax = e.eval(b);
624 sort(yminoverall, ymaxoverall);
625 im.
ymin = yminoverall;
626 im.
ymax = ymaxoverall;
627 intervals.push_back(im);
638 : m_expansions(std::move(expansions)),
639 m_x_at_extrema(determine_extrema(m_expansions, thresh_imag)),
640 m_monotonic_intervals(build_monotonic_intervals(m_x_at_extrema)),
645 : m_expansions(other.m_expansions),
646 m_x_at_extrema(other.m_x_at_extrema),
647 m_monotonic_intervals(other.m_monotonic_intervals),
648 m_xmin(other.
xmin()),
649 m_xmax(other.
xmax()) {}
654 std::swap(m_expansions, other.m_expansions);
655 std::swap(m_x_at_extrema, other.m_x_at_extrema);
656 std::swap(m_monotonic_intervals, other.m_monotonic_intervals);
657 std::swap(m_xmin, other.m_xmin);
658 std::swap(m_xmax, other.m_xmax);
669 return m_x_at_extrema;
674 return m_monotonic_intervals;
690 return m_monotonic_intervals.size() == 1;
701 auto midpoint_Knuth = [](
int x,
int y) {
return (x & y) + ((x ^ y) >> 1); };
703 int iL = 0U, iR =
static_cast<int>(m_expansions.size()) - 1, iM;
704 while (iR - iL > 1) {
705 iM = midpoint_Knuth(iL, iR);
706 if (x >= m_expansions[iM].
xmin()) {
712 return (x < m_expansions[iL].
xmax()) ? iL : iR;
720 return m_expansions[
get_index(x)].eval(x);
724 template <
typename Container>
725 const auto eval_many(
const Container& x, Container& y)
const {
726 if (x.size() != y.size()) {
727 throw std::invalid_argument(
"x and y are not the same size");
729 for (
auto i = 0U; i < x.size(); ++i) {
735 template <
typename Container>
736 const auto eval_manyC(
const Container x[], Container y[], std::size_t N)
const {
737 for (
auto i = 0U; i < N; ++i) {
744 std::vector<IntervalMatch> matches;
745 for (
auto& interval : m_monotonic_intervals) {
746 if (y >= interval.ymin && y <= interval.ymax) {
747 matches.push_back(interval);
755 const auto get_x_for_y(
double y,
unsigned int bits, std::size_t max_iter,
double boundsftol)
const {
756 std::vector<std::pair<double, int>> solns;
757 for (
const auto& interval : m_monotonic_intervals) {
761 if (interval.contains_y(y)) {
763 for (
const auto& ei : interval.expansioninfo) {
766 if (ei.contains_y(y)) {
768 auto [xvalue, num_steps] = e.
solve_for_x_count(y, ei.xmin, ei.xmax, bits, max_iter, boundsftol);
769 solns.emplace_back(xvalue,
static_cast<int>(num_steps));
778 template <
typename YContainer,
typename CountContainer>
779 const auto count_x_for_y_many(
const YContainer& y,
unsigned int bits, std::size_t max_iter,
double boundsftol, CountContainer& x)
const {
780 if (x.size() != y.size()) {
781 throw std::invalid_argument(
"x and y are not the same size");
783 for (
auto i = 0U; i < x.size(); ++i) {
784 x(i) =
get_x_for_y(y(i), bits, max_iter, boundsftol).size();
789 template <
typename YContainer,
typename CountContainer>
790 const auto count_x_for_y_manyC(
const YContainer y[],
size_t N,
unsigned int bits, std::size_t max_iter,
double boundsftol,
791 CountContainer x[])
const {
792 for (
auto i = 0U; i < N; ++i) {
793 x[i] =
get_x_for_y(y[i], bits, max_iter, boundsftol).size();
798static_assert(std::is_copy_constructible_v<ChebyshevApproximation1D<std::vector<double>>>);
799static_assert(std::is_copy_assignable_v<ChebyshevApproximation1D<std::vector<double>>>);
816template <
typename ArrayType = Eigen::ArrayXd>
842 std::vector<ChebyshevExpansion<ArrayType>>
rhoL,
rhoV,
p;
855 std::optional<ChebyshevApproximation1D<ArrayType>> m_hL,
865 double m_rhocrit_num;
868 std::vector<CheckPoint> m_check_points;
874 mutable std::mutex m_lazy_build_mutex;
893 std::optional<std::pair<double, double>> m_caloric_alpha0_stamp;
901 mutable std::atomic<unsigned int> m_caloric_build_count{0};
910 : m_rhoL(std::move(d.rhoL)),
911 m_rhoV(std::move(d.rhoV)),
914 m_Tcrit_num(d.Tcrit_num),
915 m_rhocrit_num(d.rhocrit_num),
916 m_pmin(m_p.eval(m_p.xmin())),
917 m_pmax(m_p.eval(m_p.xmax())),
918 m_check_points(std::move(d.check_points)) {}
923 auto make_invlnp(Eigen::Index Ndegree) {
928 const double EPSILON = std::numeric_limits<double>::epsilon();
930 auto func = [&](
long i,
long Npts,
double lnp) ->
double {
934 if (solns.size() != 1) {
935 if ((i == 0 || i == Npts - 1) && (p > pmin * (1 - EPSILON * 1000) && p < pmin * (1 + EPSILON * 1000))) {
938 if ((i == 0 || i == Npts - 1) && (p > pmax * (1 - EPSILON * 1000) && p < pmax * (1 + EPSILON * 1000))) {
941 std::stringstream ss;
942 ss << std::setprecision(20) <<
"Must have one solution for T(p), got " << solns.size() <<
" for " << p <<
" Pa; limits are [" << pmin
943 << +
" Pa , " << pmax <<
" Pa]; i is " << i;
944 throw std::invalid_argument(ss.str());
946 auto [
T, iters] = solns[0];
950 using CE_t = std::vector<ChebyshevExpansion<ArrayType>>;
951 return detail::dyadic_splitting<decltype(func), CE_t>(Ndegree, func, log(pmin), log(pmax), 3, 1e-12, 26);
964 explicit SuperAncillary(
const std::string& s);
972 auto get_or_throw = [&](
const auto& v) ->
const auto& {
976 throw std::invalid_argument(
"unable to get the variable " + std::string(1, k) +
", make sure it has been added to superancillary");
983 return (Q == 0) ? m_rhoL : m_rhoV;
985 return (Q == 0) ? get_or_throw(m_hL) : get_or_throw(m_hV);
987 return (Q == 0) ? get_or_throw(m_sL) : get_or_throw(m_sV);
989 return (Q == 0) ? get_or_throw(m_uL) : get_or_throw(m_uV);
991 throw std::invalid_argument(
"Bad key of '" + std::string(1, k) +
"'");
1001 std::lock_guard<std::mutex> lk(m_lazy_build_mutex);
1008 m_caloric_alpha0_stamp.reset();
1022 const std::function<
double(
double,
double)>& s_callable,
const std::function<
double(
double,
double)>& u_callable) {
1027 std::lock_guard<std::mutex> lk(m_lazy_build_mutex);
1028 if (m_hL.has_value() && m_hV.has_value() && m_sL.has_value() && m_sV.has_value() && m_uL.has_value() && m_uV.has_value()) {
1031 add_variable_locked(
'H', h_callable);
1032 add_variable_locked(
'S', s_callable);
1036 add_variable_locked(
'U', u_callable);
1037 m_caloric_alpha0_stamp = std::make_pair(caller_a1, caller_a2);
1038 ++m_caloric_build_count;
1044 std::lock_guard<std::mutex> lk(m_lazy_build_mutex);
1045 return m_caloric_alpha0_stamp;
1052 return m_caloric_build_count.load();
1065 return m_hL.has_value() && m_hV.has_value();
1067 return m_sL.has_value() && m_sV.has_value();
1069 return m_uL.has_value() && m_uV.has_value();
1079 std::lock_guard<std::mutex> lk(m_lazy_build_mutex);
1083 m_invlnp = make_invlnp(Ndegree);
1105 return m_rhocrit_num;
1109 return m_check_points;
1120 void add_variable(
char var,
const std::function<
double(
double,
double)>& caller) {
1121 std::lock_guard<std::mutex> lk(m_lazy_build_mutex);
1122 add_variable_locked(var, caller);
1127 void add_variable_locked(
char var,
const std::function<
double(
double,
double)>& caller) {
1128 Eigen::MatrixXd Lmat, Umat;
1134 auto coeff_to_eigen = [](
const ArrayType& a) -> Eigen::ArrayXd {
1135 if constexpr (std::is_same_v<ArrayType, std::vector<double>>) {
1136 return Eigen::Map<const Eigen::ArrayXd>(a.data(),
static_cast<Eigen::Index
>(a.size()));
1141 auto eigen_to_arraytype = [](
const Eigen::ArrayXd& e) -> ArrayType {
1142 if constexpr (std::is_same_v<ArrayType, std::vector<double>>) {
1143 return std::vector<double>(e.data(), e.data() + e.size());
1149 auto builder = [&](
char var,
auto& variantL,
auto& variantV) {
1150 std::vector<ChebyshevExpansion<ArrayType>> newexpL, newexpV;
1153 if (expsL.get_expansions().size() != expsV.get_expansions().size()) {
1154 throw std::invalid_argument(
"L&V are not the same size");
1156 for (
auto i = 0U; i < expsL.get_expansions().size(); ++i) {
1157 const auto& expL = expsL.get_expansions()[i];
1158 const auto& expV = expsV.get_expansions()[i];
1159 const auto&
T = expL.get_nodes_realworld();
1161 Eigen::ArrayXd funcL = Umat * coeff_to_eigen(expL.coeff()).matrix();
1162 Eigen::ArrayXd funcV = Umat * coeff_to_eigen(expV.coeff()).matrix();
1165 for (
auto j = 0; j < funcL.size(); ++j) {
1166 funcL(j) = caller(
T(j), funcL(j));
1167 funcV(j) = caller(
T(j), funcV(j));
1170 Eigen::ArrayXd newcoeffL = (Lmat * funcL.matrix()).array();
1171 Eigen::ArrayXd newcoeffV = (Lmat * funcV.matrix()).array();
1172 newexpL.emplace_back(expL.xmin(), expL.xmax(), eigen_to_arraytype(newcoeffL));
1173 newexpV.emplace_back(expV.xmin(), expV.xmax(), eigen_to_arraytype(newcoeffV));
1176 variantL.emplace(std::move(newexpL));
1177 variantV.emplace(std::move(newexpV));
1182 builder(var, m_hL, m_hV);
1185 builder(var, m_sL, m_sV);
1188 builder(var, m_uL, m_uV);
1191 throw std::invalid_argument(
"nope");
1202 if (Q == 1 || Q == 0) {
1205 throw std::invalid_argument(
"invalid_value for Q");
1212 template <
typename Container>
1214 if (
T.size() != y.size()) {
1215 throw std::invalid_argument(
"T and y are not the same size");
1218 for (
auto i = 0; i <
T.size(); ++i) {
1219 y(i) = approx.eval(
T(i));
1226 template <
typename Container>
1227 void eval_sat_manyC(
const Container
T[], std::size_t N,
char k,
short Q, Container y[])
const {
1230 for (std::size_t i = 0; i < N; ++i) {
1231 y[i] = approx.eval(
T[i]);
1244 auto solve_for_T(
double propval,
char k,
bool Q,
unsigned int bits = 64,
unsigned int max_iter = 100,
double boundsftol = 1e-13)
const {
1245 return get_approx1d(k, Q).get_x_for_y(propval, bits, max_iter, boundsftol);
1259 return (1 / propval - v_L) / (v_V - v_L);
1263 return (propval - L) / (V - L);
1289 double v = q * v_V + (1 - q) * v_L;
1294 return q * V + (1 - q) * L;
1299 template <
typename Container>
1300 void get_yval_many(
const Container&
T,
char k,
const Container& q, Container& y)
const {
1301 if (
T.size() != y.size() ||
T.size() != q.size()) {
1302 throw std::invalid_argument(
"T, q, and y are not all the same size");
1309 for (
auto i = 0; i <
T.size(); ++i) {
1311 double v_L = 1.0 / L.eval(
T(i));
1312 double v_V = 1.0 / V.eval(
T(i));
1313 double v = q(i) * v_V + (1 - q(i)) * v_L;
1317 for (
auto i = 0; i <
T.size(); ++i) {
1318 y(i) = q(i) * V.eval(
T(i)) + (1 - q(i)) * L.eval(
T(i));
1329 auto get_all_intersections(
const char k,
const double val,
unsigned int bits, std::size_t max_iter,
double boundsftol)
const {
1332 auto TsatL = L.get_x_for_y(val, bits, max_iter, boundsftol);
1333 const auto TsatV = V.get_x_for_y(val, bits, max_iter, boundsftol);
1334 for (
auto&& el : TsatV) {
1335 TsatL.push_back(el);
1357 std::optional<SuperAncillaryTwoPhaseSolution>
iterate_for_Tq_XY(
double Tmin,
double Tmax,
char ch1,
double val1,
char ch2,
double val2,
1358 unsigned int bits, std::size_t max_iter,
double boundsftol)
const {
1360 std::size_t counter = 0;
1361 auto f = [&](
double T_) {
1364 double resid =
get_yval(T_, q_fromv1, ch2) - val2;
1367 double fTmin = f(Tmin), fTmax = f(Tmax);
1374 auto returner = [&]() {
1382 if (std::abs(fTmin) < boundsftol) {
1386 if (std::abs(fTmax) < boundsftol) {
1390 if (fTmin * fTmax > 0) {
1393 return std::nullopt;
1400 fprintf(stderr,
"fTmin,fTmax: %g,%g\n", fTmin, fTmax);
1414 std::optional<SuperAncillaryTwoPhaseSolution>
solve_for_Tq_DX(
const double rho,
const double propval,
const char k,
unsigned int bits,
1415 std::size_t max_iter,
double boundsftol)
const {
1420 std::size_t rhosat_soln_count = Tsat.size();
1422 std::tuple<double, double> Tsearchrange;
1423 if (rhosat_soln_count == 1) {
1426 Tsearchrange = std::make_tuple(Lrho.xmin * 0.999, std::get<0>(Tsat[0]));
1427 }
else if (rhosat_soln_count == 2) {
1428 double y1 = std::get<0>(Tsat[0]), y2 = std::get<0>(Tsat[1]);
1432 Tsearchrange = std::make_tuple(y1, y2);
1434 throw std::invalid_argument(
"cannot have number of solutions other than 1 or 2; got " + std::to_string(rhosat_soln_count) +
" solutions");
1436 auto [a, b] = Tsearchrange;
1437 return iterate_for_Tq_XY(a, b,
'D', rho, k, propval, bits, max_iter, boundsftol);
1441 template <
typename Container>
1442 void solve_for_Tq_DX_many(
const Container& rho,
const Container& propval,
const char k,
unsigned int bits, std::size_t max_iter,
1443 double boundsftol, Container&
T, Container& q, Container& count) {
1444 if (std::set<std::size_t>({rho.size(), propval.size(),
T.size(), q.size(), count.size()}).size() != 1) {
1445 throw std::invalid_argument(
"rho, propval, T, q, count are not all the same size");
1447 for (
auto i = 0U; i <
T.size(); ++i) {
1448 auto osoln =
solve_for_Tq_DX(rho(i), propval(i), k, bits, max_iter, boundsftol);
1450 const auto& soln = osoln.value();
1453 count(i) = soln.counter;
1651static_assert(!std::is_copy_constructible_v<SuperAncillary<std::vector<double>>>);
1652static_assert(!std::is_copy_assignable_v<SuperAncillary<std::vector<double>>>);