76inline auto mix_VLE_Tx(
const AbstractModel& model,
double T,
const Eigen::ArrayXd& rhovecL0,
const Eigen::ArrayXd& rhovecV0,
const Eigen::ArrayXd& xspec,
double atol,
double reltol,
double axtol,
double relxtol,
int maxiter) {
77 using Scalar = double;
79 const Eigen::Index N = rhovecL0.size();
80 auto lengths = (Eigen::ArrayX<Eigen::Index>(3) << rhovecL0.size(), rhovecV0.size(), xspec.size()).finished();
81 if (lengths.minCoeff() != lengths.maxCoeff()){
82 throw InvalidArgument(
"lengths of rhovecs and xspec must be the same in mix_VLE_Tx");
84 Eigen::MatrixXd J(2 * N, 2 * N), r(2 * N, 1), x(2 * N, 1);
85 x.col(0).array().head(N) = rhovecL0;
86 x.col(0).array().tail(N) = rhovecV0;
88 Eigen::Map<Eigen::ArrayXd> rhovecL(&(x(0)), N);
89 Eigen::Map<Eigen::ArrayXd> rhovecV(&(x(0 + N)), N);
90 auto RT = model.
get_R(xspec) * T;
94 for (
int iter = 0; iter < maxiter; ++iter) {
98 auto rhoL = rhovecL.sum();
99 auto rhoV = rhovecV.sum();
100 Scalar pL = rhoL * RT - PsirL + (rhovecL.array() * PsirgradL.array()).sum();
101 Scalar pV = rhoV * RT - PsirV + (rhovecV.array() * PsirgradV.array()).sum();
102 auto dpdrhovecL = RT + (hessianL * rhovecL.matrix()).array();
103 auto dpdrhovecV = RT + (hessianV * rhovecV.matrix()).array();
105 bool index0nonzero = rhovecL(0) > 0 && rhovecV(0) > 0;
106 bool index1nonzero = rhovecL(1) > 0 && rhovecV(1) > 0;
109 r(0) = PsirgradL(0) + RT * log(rhovecL(0)) - (PsirgradV(0) + RT * log(rhovecV(0)));
111 r(0) = PsirgradL(0) - PsirgradV(0);
114 r(1) = PsirgradL(1) + RT * log(rhovecL(1)) - (PsirgradV(1) + RT * log(rhovecV(1)));
116 r(1) = PsirgradL(1) - PsirgradV(1);
119 r(3) = rhovecL(0) / rhovecL.sum() - xspec(0);
122 J(0, 0) = hessianL(0, 0) + (index0nonzero ? RT / rhovecL(0) : 0);
123 J(0, 1) = hessianL(0, 1);
124 J(1, 0) = hessianL(1, 0);
125 J(1, 1) = hessianL(1, 1) + (index1nonzero ? RT / rhovecL(1) : 0);
126 J(0, 2) = -(hessianV(0, 0) + (index0nonzero ? RT / rhovecV(0) : 0));
127 J(0, 3) = -(hessianV(0, 1));
128 J(1, 2) = -(hessianV(1, 0));
129 J(1, 3) = -(hessianV(1, 1) + (index1nonzero ? RT / rhovecV(1) : 0));
131 J(2, 0) = dpdrhovecL(0);
132 J(2, 1) = dpdrhovecL(1);
133 J(2, 2) = -dpdrhovecV(0);
134 J(2, 3) = -dpdrhovecV(1);
136 J.row(3).array() = 0.0;
137 J(3, 0) = (rhoL - rhovecL(0)) / (rhoL * rhoL);
138 J(3, 1) = -rhovecL(0) / (rhoL * rhoL);
141 Eigen::ArrayXd dx = J.colPivHouseholderQr().solve(-r);
143 if ((!dx.isFinite()).all()) {
149 if ((x.array() + dx.array() < 0).any()) {
151 Eigen::ArrayXd dxmax = -x;
154 auto f = (dx / dxmax).minCoeff();
159 for (
auto i = 0; i < 2; ++i) {
168 auto xtol_threshold = (axtol + relxtol * x.array().cwiseAbs()).eval();
169 if ((dx.array().cwiseAbs() < xtol_threshold).all()) {
174 auto error_threshold = (atol + reltol * r.array().cwiseAbs()).eval();
175 if ((r.array().cwiseAbs() < error_threshold).all()) {
183 if (((x.array() - dx.array()).cwiseAbs() < std::numeric_limits<Scalar>::min()).all()) {
187 if (iter == maxiter - 1){
191 Eigen::ArrayXd rhovecLfinal = rhovecL, rhovecVfinal = rhovecV;
192 return std::make_tuple(return_code, rhovecLfinal, rhovecVfinal);
287inline auto mix_VLE_Tp(
const AbstractModel& model,
double T,
double pgiven,
const Eigen::ArrayXd& rhovecL0,
const Eigen::ArrayXd& rhovecV0,
const std::optional<MixVLETpFlags>& flags_ = std::nullopt) {
291 const Eigen::Index N = rhovecL0.size();
292 auto lengths = (Eigen::ArrayX<Eigen::Index>(2) << rhovecL0.size(), rhovecV0.size()).finished();
293 if (lengths.minCoeff() != lengths.maxCoeff()) {
294 throw InvalidArgument(
"lengths of rhovecs must be the same in mix_VLE_Tx");
296 Eigen::VectorXd x(2*N, 1);
297 x.col(0).array().head(N) = rhovecL0;
298 x.col(0).array().tail(N) = rhovecV0;
301 std::string message =
"";
304 FunctorType functor(model, T, pgiven);
305 Eigen::VectorXd initial_r(2 * N); initial_r.setZero();
306 functor(x, initial_r);
308 bool success =
false;
322 Eigen::Index niter = 0, nfev = 0;
323 Eigen::MatrixXd J(2 * N, 2 * N);
325 HybridNonLinearSolver<FunctorType> solver(functor);
327 solver.diag.setConstant(2 * N, 1.);
328 solver.useExternalScaling =
true;
329 auto info = solver.solve(x);
331 using e = Eigen::HybridNonLinearSolverSpace::Status;
332 success = (info == e::RelativeErrorTooSmall || info == e::TolTooSmall);
334 case e::ImproperInputParameters:
336 case e::RelativeErrorTooSmall:
338 case e::TooManyFunctionEvaluation:
351 for (
auto iter = 0; iter < flags.maxiter; ++iter) {
352 Eigen::VectorXd rv(2 * N); rv.setZero();
355 Eigen::ArrayXd dx = J.colPivHouseholderQr().solve(-rv);
356 if ((x.array() + dx.array() < 0).any()) {
358 Eigen::ArrayXd dxmax = -x;
361 auto f = (dx/dxmax).minCoeff();
364 x.array() += dx.array();
369 Eigen::VectorXd final_r(2 * N); final_r.setZero();
372 Eigen::Map<const Eigen::ArrayXd> rhovecL(&(x(0)), N);
373 Eigen::Map<const Eigen::ArrayXd> rhovecV(&(x(0 + N)), N);
377 r.
num_iter =
static_cast<int>(niter);
378 r.
num_fev =
static_cast<int>(nfev);
399inline auto mixture_VLE_px(
const AbstractModel& model,
double p_spec,
const Eigen::ArrayXd& xmolar_spec,
double T0,
const Eigen::ArrayXd& rhovecL0,
const Eigen::ArrayXd& rhovecV0,
const std::optional<MixVLEpxFlags>& flags_ = std::nullopt) {
400 using Scalar = double;
404 const Eigen::Index N = rhovecL0.size();
405 auto lengths = (Eigen::ArrayX<Eigen::Index>(3) << rhovecL0.size(), rhovecV0.size(), xmolar_spec.size()).finished();
406 if (lengths.minCoeff() != lengths.maxCoeff()) {
407 throw InvalidArgument(
"lengths of rhovecs and xspec must be the same in mixture_VLE_px");
409 if ((rhovecV0 == 0).any()) {
410 throw InvalidArgument(
"Infinite dilution is not allowed for rhovecV0 in mixture_VLE_px");
412 if ((rhovecL0 == 0).any()) {
413 throw InvalidArgument(
"Infinite dilution is not allowed for rhovecL0 in mixture_VLE_px");
415 Eigen::MatrixXd J(2*N+1, 2*N+1); J.setZero();
416 Eigen::VectorXd r(2*N + 1), x(2*N + 1);
418 x.segment(1, N) = rhovecL0;
419 x.tail(N) = rhovecV0;
421 Eigen::Map<Eigen::ArrayXd> rhovecL(&(x(1)), N);
422 Eigen::Map<Eigen::ArrayXd> rhovecV(&(x(1 + N)), N);
428 for (
int iter = 0; iter < flags.maxiter; ++iter) {
430 auto RL = model.
get_R(xmolar_spec);
440 auto make_diag = [](
const Eigen::ArrayXd& v) -> Eigen::ArrayXXd {
441 Eigen::MatrixXd A = Eigen::MatrixXd::Identity(v.size(), v.size());
445 auto HtotL = (hessianL.array() + make_diag(RLT/rhovecL)).eval();
446 auto HtotV = (hessianV.array() + make_diag(RVT/rhovecV)).eval();
448 auto rhoL = rhovecL.sum();
449 auto rhoV = rhovecV.sum();
450 Scalar pL = rhoL * RLT - PsirL + (rhovecL.array() * PsirgradL.array()).sum();
451 Scalar pV = rhoV * RVT - PsirV + (rhovecV.array() * PsirgradV.array()).sum();
452 auto dpdrhovecL = RLT + (hessianL * rhovecL.matrix()).array();
453 auto dpdrhovecV = RVT + (hessianV * rhovecV.matrix()).array();
455 auto DELTA_dchempot_dT = (DELTAdmu_dT_res + RL*log(rhovecL/rhovecV)).eval();
458 r.head(N) = PsirgradL + RLT*log(rhovecL) - (PsirgradV + RVT*log(rhovecV));
460 r(N) = pL/p_spec - 1;
461 r(N+1) = pV/p_spec - 1;
463 r.tail(N-1) = (rhovecL/rhovecL.sum()).head(N-1) - xmolar_spec.head(N-1);
469 J.block(0, 0, N, 1) = DELTA_dchempot_dT;
470 J.block(0, 1, N, N) = HtotL;
471 J.block(0, N+1, N, N) = -HtotV;
474 J.block(N, 1, 1, N) = dpdrhovecL.transpose()/p_spec;
478 J.block(N+1, N+1, 1, N) = dpdrhovecV.transpose()/p_spec;
482 Eigen::ArrayXXd AA = rhovecL.matrix().reshaped(N, 1).replicate(1, N).array();
483 Eigen::MatrixXd M = ((rhoL * Eigen::MatrixXd::Identity(N, N).array() - AA) / (rhoL * rhoL));
484 J.block(N+2, 1, N-1, N) = M.block(0,0,N-1,N);
487 Eigen::ArrayXd dx = J.colPivHouseholderQr().solve(-r);
489 if ((!dx.isFinite()).all()) {
495 x.tail(2*N).array() += dx.tail(2*N);
497 auto xtol_threshold = (flags.axtol + flags.relxtol * x.array().cwiseAbs()).eval();
498 if ((dx.array().cwiseAbs() < xtol_threshold).all()) {
503 auto error_threshold = (flags.atol + flags.reltol * r.array().cwiseAbs()).eval();
504 if ((r.array().cwiseAbs() < error_threshold).all()) {
512 if (((x.array() - dx.array()).cwiseAbs() < std::numeric_limits<Scalar>::min()).all()) {
516 if (iter == flags.maxiter - 1) {
520 Eigen::ArrayXd rhovecLfinal = rhovecL, rhovecVfinal = rhovecV;
521 return std::make_tuple(return_code, T, rhovecLfinal, rhovecVfinal);
526 using Scalar = double;
532 auto N = rhovecL.size();
533 Eigen::MatrixXd A =
decltype(Hliq)::Zero(N, N);
534 auto b =
decltype(Hliq)::Ones(N, 1);
535 decltype(Hliq) drhodp_liq, drhodp_vap;
536 assert(rhovecL.size() == rhovecV.size());
537 if ((rhovecL != 0).all() && (rhovecV != 0).all()) {
539 A(0, 0) = Hliq.row(0).dot(rhovecV.matrix());
540 A(0, 1) = Hliq.row(1).dot(rhovecV.matrix());
541 A(1, 0) = Hliq.row(0).dot(rhovecL.matrix());
542 A(1, 1) = Hliq.row(1).dot(rhovecL.matrix());
545 drhodp_vap =
linsolve(Hvap, Hliq*drhodp_liq);
551 auto RL = model.
get_R(rhovecL / rhovecL.sum());
552 auto RV = model.
get_R(rhovecV / rhovecV.sum());
555 for (
auto i = 0; i < N; ++i) {
556 for (
auto j = 0; j < N; ++j) {
557 if (rhovecL[j] == 0) {
563 auto Aij = (Hliq.row(j).array().cwiseProduct(((i == 0) ? rhovecV : rhovecL).array().transpose())).eval();
565 bool is_liq = (i == 1);
567 Aij[j] = (is_liq) ? RL*T : RL*T*exp(-(murV[j] - murL[j])/(RL*T));
573 A(i, j) = Hliq.row(j).dot(((i==0) ? rhovecV : rhovecL).matrix());
581 auto diagrhovecL = rhovecL.matrix().asDiagonal();
582 auto PSIVstar = (diagrhovecL*Hvap).eval();
583 auto PSILstar = (diagrhovecL*Hliq).eval();
584 for (
auto j = 0; j < N; ++j) {
585 if (rhovecL[j] == 0) {
586 PSILstar(j, j) = RL*T;
587 PSIVstar(j, j) = RV*T/exp(-(murV[j] - murL[j]) / (RV * T));
590 drhodp_vap =
linsolve(PSIVstar, PSILstar*drhodp_liq);
592 return std::make_tuple(drhodp_liq, drhodp_vap);
599 using Scalar = double;
600 if (rhovecL.size() != 2) {
throw std::invalid_argument(
"Binary mixtures only"); }
601 assert(rhovecL.size() == rhovecV.size());
606 auto N = rhovecL.size();
607 Eigen::MatrixXd A =
decltype(Hliq)::Zero(N, N);
608 Eigen::MatrixXd b =
decltype(Hliq)::Ones(N, 1);
609 decltype(Hliq) drhovecdT_liq, drhovecdT_vap;
610 assert(rhovecL.size() == rhovecV.size());
612 if ((rhovecL != 0).all() && (rhovecV != 0).all()) {
614 A(0, 0) = Hliq.row(0).dot(rhovecV.matrix());
615 A(0, 1) = Hliq.row(1).dot(rhovecV.matrix());
616 A(1, 0) = Hliq.row(0).dot(rhovecL.matrix());
617 A(1, 1) = Hliq.row(1).dot(rhovecL.matrix());
625 drhovecdT_vap =
linsolve(Hvap, ((Hliq*drhovecdT_liq).array() - DELTAdmu_dT.array()).eval());
631 auto RL = model.
get_R(rhovecL / rhovecL.sum());
632 auto RV = model.
get_R(rhovecV / rhovecV.sum());
642 auto DELTAdmu_dT_rhoV_ideal = (rhovecV*(RV*log(rhovecV/rhovecL))).eval();
643 auto DELTAdmu_dT_ideal = (RV*log(rhovecV/rhovecL)).eval();
645 for (
auto i = 0; i < rhovecV.size(); ++i) {
646 if (rhovecV[i] == 0) {
647 DELTAdmu_dT_rhoV_ideal(i) = 0;
648 DELTAdmu_dT_ideal(i) = 0;
651 double DELTAdmu_dT_rhoV = rhovecV.matrix().dot(DELTAdmu_dT_res.matrix()) + DELTAdmu_dT_rhoV_ideal.sum();
657 for (
auto i = 0; i < N; ++i) {
659 bool is_liq = (i == 1);
660 for (
auto j = 0; j < N; ++j) {
661 auto rhovec = (is_liq) ? rhovecL : rhovecV;
663 auto Aij = (Hliq.row(j).array().cwiseProduct(rhovec.array().transpose())).eval();
665 if (!(Hliq.row(j)).array().isFinite().all()) {
667 if (rhovec[j] == 0) {
669 Aij[j] = (is_liq) ? RL * T : RL * T * exp(-(murV[j] - murL[j]) / (RL * T));
682 auto diagrhovecL = rhovecL.matrix().asDiagonal();
683 auto Hvapstar = (diagrhovecL*Hvap).eval();
684 auto Hliqstar = (diagrhovecL*Hliq).eval();
685 for (
auto j = 0; j < N; ++j) {
686 if (rhovecL[j] == 0) {
687 Hliqstar(j, j) = RL*T;
688 Hvapstar(j, j) = RV*T/ exp((murL[j] - murV[j]) / (RV * T));
691 auto diagrhovecL_dot_DELTAdmu_dT = (diagrhovecL*(DELTAdmu_dT_res+DELTAdmu_dT_ideal).matrix()).array();
692 auto RHS = ((Hliqstar * drhovecdT_liq).array() - diagrhovecL_dot_DELTAdmu_dT).eval();
693 drhovecdT_vap =
linsolve(Hvapstar.eval(), RHS);
695 return std::make_tuple(drhovecdT_liq, drhovecdT_vap);
713 using Scalar = double;
714 if (rhovecL.size() != 2) {
throw std::invalid_argument(
"Binary mixtures only"); }
715 assert(rhovecL.size() == rhovecV.size());
717 Eigen::ArrayXd molefracL = rhovecL / rhovecL.sum();
720 Eigen::ArrayXd deltarho = (rhovecV - rhovecL).eval();
725 Eigen::MatrixXd drhodT_liq, drhodT_vap;
726 if ((rhovecL != 0).all() && (rhovecV != 0).all()) {
727 auto num = (deltas.matrix().dot(rhovecV.matrix()) - deltabeta);
728 auto den = (Hliq*(deltarho.matrix())).dot(molefracL.matrix());
729 drhodT_liq = num/den*molefracL;
730 drhodT_vap =
linsolve(Hvap, ((Hliq * drhodT_liq).array() - deltas.array()).eval());
733 throw std::invalid_argument(
"Infinite dilution not yet supported");
735 return std::make_tuple(drhodT_liq, drhodT_vap);
796 auto N = rhovecL0.size();
800 if (rhovecL0.size() != rhovecV0.size()) {
801 throw InvalidArgument(
"Both molar concentration arrays must be of the same size");
804 auto norm = [](
const auto& v) {
return (v * v).sum(); };
807 auto JSONdata = nlohmann::json::array();
810 using namespace boost::numeric::odeint;
811 using state_type = std::vector<double>;
814 euler<state_type> eul;
815 typedef runge_kutta_cash_karp54< state_type > error_stepper_type;
816 typedef controlled_runge_kutta< error_stepper_type > controlled_stepper_type;
819 double abs_err = opt.
abs_err, rel_err = opt.
rel_err, a_x = 1.0, a_dxdt = 1.0;
820 controlled_stepper_type controlled_stepper(default_error_checker< double, range_algebra, default_operations >(abs_err, rel_err, a_x, a_dxdt));
826 state_type x0(2 * N), last_drhodt(2 * N), previous_drhodt(2 * N);
827 auto set_init_state = [&](state_type&
X) {
828 auto rhovecL = Eigen::Map<Eigen::ArrayXd>(&(
X[0]), N);
829 auto rhovecV = Eigen::Map<Eigen::ArrayXd>(&(
X[0]) + N, N);
836 auto xprime = [&](
const state_type&
X, state_type& Xprime,
double ) {
839 auto rhovecL = Eigen::Map<const Eigen::ArrayXd>(&(
X[0]), N);
840 auto rhovecV = Eigen::Map<const Eigen::ArrayXd>(&(
X[0]) + N, N);
841 auto drhovecdtL = Eigen::Map<Eigen::ArrayXd>(&(Xprime[0]), N);
842 auto drhovecdtV = Eigen::Map<Eigen::ArrayXd>(&(Xprime[0]) + N, N);
846 auto dpdt = 1.0/sqrt(norm(drhovecdpL.array()) + norm(drhovecdpV.array()));
848 drhovecdtL = c*drhovecdpL*dpdt;
849 drhovecdtV = c*drhovecdpV*dpdt;
851 if (previous_drhodt.empty()) {
856 auto get_const_view = [&](
const auto& v, Eigen::Index N) {
857 return Eigen::Map<const Eigen::ArrayXd>(&(v[0]), N);
859 if (get_const_view(Xprime, N).matrix().dot(get_const_view(previous_drhodt, N).matrix()) < 0) {
860 auto Xprimeview = Eigen::Map<Eigen::ArrayXd>(&(Xprime[0]), 2*N);
866 double t = 0, dt = opt.
init_dt;
869 xprime(x0, dxdt, -1.0);
870 const auto dXdt = Eigen::Map<const Eigen::ArrayXd>(&(dxdt[0]), dxdt.size());
871 const auto X = Eigen::Map<const Eigen::ArrayXd>(&(x0[0]), x0.size());
873 const Eigen::ArrayXd step =
X + dXdt * dt;
874 Eigen::ArrayX<bool> negativestepvals = (step < 0).eval();
876 if (negativestepvals.any()) {
880 std::string termination_reason;
884 for (
auto istep = 0; istep < opt.
max_steps; ++istep) {
886 auto store_point = [&]() {
888 auto N = x0.size() / 2;
889 auto rhovecL = Eigen::Map<const Eigen::ArrayXd>(&(x0[0]), N);
890 auto rhovecV = Eigen::Map<const Eigen::ArrayXd>(&(x0[0]) + N, N);
891 auto rhototL = rhovecL.sum(), rhototV = rhovecV.sum();
892 double pL = rhototL * model.
get_R(rhovecL / rhovecL.sum())*T + model.
get_pr(T, rhovecL);
893 double pV = rhototV * model.
get_R(rhovecV / rhovecV.sum())*T + model.
get_pr(T, rhovecV);
897 xprime(x0, last_drhodt, -1.0);
900 std::cout <<
"Something bad happened; couldn't calculate xprime in store_point" << std::endl;
904 nlohmann::json point = {
911 {
"rhoL / mol/m^3", rhovecL},
912 {
"rhoV / mol/m^3", rhovecV},
913 {
"xL_0 / mole frac.", rhovecL[0]/rhovecL.sum()},
914 {
"xV_0 / mole frac.", rhovecV[0]/rhovecV.sum()},
915 {
"drho/dt", last_drhodt}
921 JSONdata.push_back(point);
924 if (istep == 0 && retry_count == 0) {
929 auto x0_previous = x0;
932 controlled_step_result res = controlled_step_result::fail;
934 res = controlled_stepper.try_step(xprime, x0, t, dt);
940 if (res != controlled_step_result::success) {
950 dt = std::min(dt, opt.
max_dt);
954 eul.do_step(xprime, x0, t, dt);
964 auto stop_requested = [&]() {
966 auto N = x0.size() / 2;
967 auto rhovecL = Eigen::Map<const Eigen::ArrayXd>(&(x0[0]), N);
968 auto rhovecV = Eigen::Map<const Eigen::ArrayXd>(&(x0[0]) + N, N);
969 auto x = rhovecL / rhovecL.sum();
970 auto y = rhovecV / rhovecV.sum();
971 double p = rhovecL.sum()*model.
get_R(x)*T + model.
get_pr(T, rhovecL);
984 if ((x < 0).any() || (x > 1).any() || (y < 0).any() || (y > 1).any() || (!rhovecL.isFinite()).any() || (!rhovecV.isFinite()).any()) {
991 if (stop_requested()) {
996 auto rhovecL = Eigen::Map<const Eigen::ArrayXd>(&(x0[0]), N).eval();
997 auto rhovecV = Eigen::Map<const Eigen::ArrayXd>(&(x0[0 + N]), N).eval();
998 auto x = (Eigen::ArrayXd(2) << rhovecL(0) / rhovecL.sum(), rhovecL(1) / rhovecL.sum()).finished();
999 auto [return_code, rhovecLnew, rhovecVnew] = model.
mix_VLE_Tx(T, rhovecL, rhovecV, x, 1e-10, 1e-8, 1e-10, 1e-8, 10);
1008 std::cout << msg << std::endl;
1014 auto rhovecLview = Eigen::Map<Eigen::ArrayXd>(&(x0[0]), N);
1015 auto rhovecVview = Eigen::Map<Eigen::ArrayXd>(&(x0[0]) + N, N);
1016 rhovecLview = rhovecLnew;
1017 rhovecVview = rhovecVnew;
1021 std::swap(previous_drhodt, last_drhodt);
1029 nlohmann::json meta{
1030 {
"termination_reason", termination_reason}
1032 return nlohmann::json{
1047auto trace_VLE_isobar_binary(
const Model& model,
double p,
double T0,
const Eigen::ArrayXd& rhovecL0,
const Eigen::ArrayXd& rhovecV0,
const std::optional<PVLEOptions>& options = std::nullopt)
1051 auto N = rhovecL0.size();
1055 if (rhovecL0.size() != rhovecV0.size()) {
1056 throw InvalidArgument(
"Both molar concentration arrays must be of the same size");
1059 auto norm = [](
const auto& v) {
return (v * v).sum(); };
1062 auto JSONdata = nlohmann::json::array();
1065 using namespace boost::numeric::odeint;
1066 using state_type = std::vector<double>;
1069 euler<state_type> eul;
1070 typedef runge_kutta_cash_karp54< state_type > error_stepper_type;
1071 typedef controlled_runge_kutta< error_stepper_type > controlled_stepper_type;
1074 double abs_err = opt.
abs_err, rel_err = opt.
rel_err, a_x = 1.0, a_dxdt = 1.0;
1075 controlled_stepper_type controlled_stepper(default_error_checker< double, range_algebra, default_operations >(abs_err, rel_err, a_x, a_dxdt));
1081 state_type x0(2*N+1), last_drhodt(2*N+1), previous_drhodt(2*N+1);
1082 auto set_init_state = [&](state_type&
X) {
1084 auto rhovecL = Eigen::Map<Eigen::ArrayXd>(&(
X[1]), N);
1085 auto rhovecV = Eigen::Map<Eigen::ArrayXd>(&(
X[1]) + N, N);
1092 auto xprime = [&](
const state_type&
X, state_type& Xprime,
double ) {
1095 const double& T =
X[0];
1096 auto rhovecL = Eigen::Map<const Eigen::ArrayXd>(&(
X[1]), N);
1097 auto rhovecV = Eigen::Map<const Eigen::ArrayXd>(&(
X[1]) + N, N);
1098 auto& dTdt = Xprime[0];
1099 auto drhovecdtL = Eigen::Map<Eigen::ArrayXd>(&(Xprime[1]), N);
1100 auto drhovecdtV = Eigen::Map<Eigen::ArrayXd>(&(Xprime[1]) + N, N);
1104 dTdt = 1.0 / sqrt(norm(drhovecdTL.array()) + norm(drhovecdTV.array()));
1106 drhovecdtL = c * drhovecdTL * dTdt;
1107 drhovecdtV = c * drhovecdTV * dTdt;
1109 if (previous_drhodt.empty()) {
1114 auto get_const_view = [&](
const auto& v, Eigen::Index N) {
1115 return Eigen::Map<const Eigen::ArrayXd>(&(v[0]), N);
1117 if (get_const_view(Xprime, N).matrix().dot(get_const_view(previous_drhodt, N).matrix()) < 0) {
1118 auto Xprimeview = Eigen::Map<Eigen::ArrayXd>(&(Xprime[0]), 2 * N);
1124 double t = 0, dt = opt.
init_dt;
1127 xprime(x0, dxdt, -1.0);
1128 const auto dXdt = Eigen::Map<const Eigen::ArrayXd>(&(dxdt[0]), dxdt.size());
1129 const auto X = Eigen::Map<const Eigen::ArrayXd>(&(x0[0]), x0.size());
1131 const Eigen::ArrayXd step =
X + dXdt * dt;
1132 Eigen::ArrayX<bool> negativestepvals = (step < 0).eval();
1134 if (negativestepvals.any()) {
1138 std::string termination_reason;
1141 int retry_count = 0;
1142 for (
auto istep = 0; istep < opt.
max_steps; ++istep) {
1144 auto store_point = [&]() {
1146 auto N = x0.size() / 2;
1148 auto rhovecL = Eigen::Map<const Eigen::ArrayXd>(&(x0[1]), N);
1149 auto rhovecV = Eigen::Map<const Eigen::ArrayXd>(&(x0[1]) + N, N);
1150 auto rhototL = rhovecL.sum(), rhototV = rhovecV.sum();
1151 double pL = rhototL * model.R(rhovecL / rhovecL.sum()) * T + model.get_pr(T, rhovecL);
1152 double pV = rhototV * model.R(rhovecV / rhovecV.sum()) * T + model.get_pr(T, rhovecV);
1156 xprime(x0, last_drhodt, -1.0);
1159 std::cout <<
"Something bad happened; couldn't calculate xprime in store_point" << std::endl;
1163 nlohmann::json point = {
1170 {
"rhoL / mol/m^3", rhovecL},
1171 {
"rhoV / mol/m^3", rhovecV},
1172 {
"xL_0 / mole frac.", rhovecL[0] / rhovecL.sum()},
1173 {
"xV_0 / mole frac.", rhovecV[0] / rhovecV.sum()},
1174 {
"drho/dt", last_drhodt}
1177 point[
"crit. conditions L"] = model.get_criticality_conditions(T, rhovecL);
1178 point[
"crit. conditions V"] = model.get_criticality_conditions(T, rhovecV);
1180 JSONdata.push_back(point);
1183 if (istep == 0 && retry_count == 0) {
1188 auto x0_previous = x0;
1191 controlled_step_result res = controlled_step_result::fail;
1193 res = controlled_stepper.try_step(xprime, x0, t, dt);
1199 if (res != controlled_step_result::success) {
1209 dt = std::min(dt, opt.
max_dt);
1213 eul.do_step(xprime, x0, t, dt);
1223 auto stop_requested = [&]() {
1225 auto N = (x0.size()-1) / 2;
1227 auto rhovecL = Eigen::Map<const Eigen::ArrayXd>(&(x0[1]), N);
1228 auto rhovecV = Eigen::Map<const Eigen::ArrayXd>(&(x0[1]) + N, N);
1229 auto x = rhovecL / rhovecL.sum();
1230 auto y = rhovecV / rhovecV.sum();
1233 auto condsL = model.get_criticality_conditions(T, rhovecL);
1234 auto condsV = model.get_criticality_conditions(T, rhovecV);
1239 if ((x < 0).any() || (x > 1).any() || (y < 0).any() || (y > 1).any() || (!rhovecL.isFinite()).any() || (!rhovecV.isFinite()).any()) {
1246 if (stop_requested()) {
1252 auto rhovecL = Eigen::Map<const Eigen::ArrayXd>(&(x0[1]), N).eval();
1253 auto rhovecV = Eigen::Map<const Eigen::ArrayXd>(&(x0[1 + N]), N).eval();
1254 auto x = (Eigen::ArrayXd(2) << rhovecL(0) / rhovecL.sum(), rhovecL(1) / rhovecL.sum()).finished();
1255 auto [return_code, Tnew, rhovecLnew, rhovecVnew] = model.mixture_VLE_px(p, x, T, rhovecL, rhovecV);
1264 std::cout << msg << std::endl;
1271 auto rhovecLview = Eigen::Map<Eigen::ArrayXd>(&(x0[1]), N);
1272 auto rhovecVview = Eigen::Map<Eigen::ArrayXd>(&(x0[1]) + N, N);
1273 rhovecLview = rhovecLnew;
1274 rhovecVview = rhovecVnew;
1279 std::swap(previous_drhodt, last_drhodt);