39 static auto eigen_problem(
const AbstractModel& model,
const Scalar T,
const VecType& rhovec,
const std::optional<VecType>& alignment_v0 = std::nullopt) {
43 auto N = rhovec.size();
44 Eigen::ArrayX<bool> mask = (rhovec != 0).eval();
47#if defined(USE_AUTODIFF)
51 auto H = id::build_Psir_Hessian_mcx(model, T, rhovec);
54 for (
auto i = 0; i < N; ++i) {
56 H(i, i) += model.
R(rhovec/rhovec.sum()) * T / rhovec[i];
60 Eigen::Index nonzero_count = mask.count();
61 auto zero_count = N - nonzero_count;
63 if (zero_count == 0) {
69 rhovec.minCoeff(&ind);
74 else if (zero_count == 1) {
76 std::vector<int> indicesToKeep;
79 for (
auto i = 0; i < N; ++i) {
81 indicesToKeep.push_back(i);
87 Eigen::MatrixXd Hprime = H(indicesToKeep, indicesToKeep);
94 Eigen::MatrixXd U = H; U.setZero();
97 for (
auto i = 0; i < N - nonzero_count; ++i) {
98 U(i, indicesToKeep) = eigenvectors.col(i);
103 U.row(U.rows() - 1)(badindex) = 1.0;
109 throw std::invalid_argument(
"More than one non-zero concentration value found; not currently supported");
111 if (alignment_v0 && alignment_v0.value().size() > 0 && ed.
eigenvectorscols.col(0).matrix().dot(alignment_v0.value().matrix()) < 0) {
129 static auto get_derivs(
const AbstractModel& model,
const double T,
const VecType& rhovec,
const std::optional<VecType>& alignment_v0 = std::nullopt) {
130 auto molefrac = rhovec / rhovec.sum();
131 auto R = model.
R(molefrac);
137 Eigen::ArrayXd psi0_derivs(5); psi0_derivs.setZero();
140 for (
auto i = 0; i < rhovec.size(); ++i) {
141 if (rhovec[i] != 0) {
142 psi0_derivs[2] += R * T *
pow(ei.v0[i], 2) / rhovec[i];
143 psi0_derivs[3] += -R * T *
pow(ei.v0[i], 3) /
pow(rhovec[i], 2);
144 psi0_derivs[4] += 2 * R * T *
pow(ei.v0[i], 4) /
pow(rhovec[i], 3);
148#if defined(USE_AUTODIFF)
167 Eigen::Vector<MultiComplex<double>, Eigen::Dynamic> v0(ei.v0.size());
for (
auto i = 0; i < ei.v0.size(); ++i) { v0[i] = ei.v0[i]; }
168 Eigen::Vector<MultiComplex<double>, Eigen::Dynamic> rhovecmcx(rhovec.size());
for (
auto i = 0; i < rhovec.size(); ++i) { rhovecmcx[i] = rhovec[i]; }
169 using fcn_t = std::function<MultiComplex<double>(
const MultiComplex<double>&)>;
170 fcn_t wrapper = [&rhovecmcx, &v0, &T, &model](
const MultiComplex<double>& sigma_1) {
171 Eigen::Vector<MultiComplex<double>, Eigen::Dynamic> rhovecused = rhovecmcx + sigma_1 * v0;
172 auto rhotot = rhovecused.sum();
173 auto molefrac = (rhovecused / rhotot).eval();
174 return model.alphar(T, rhotot, molefrac) * model.
R(molefrac) * T * rhotot;
176 auto psir_derivs_ = diff_mcx1(wrapper, 0.0, 4,
true);
177 Eigen::ArrayXd psir_derivs; psir_derivs.resize(5);
178 for (
auto i = 0; i < 5; ++i) { psir_derivs[i] = psir_derivs_[i]; }
189 psi1.
psir = psir_derivs;
190 psi1.
psi0 = psi0_derivs;
191 psi1.
tot = psi0_derivs + psir_derivs;
209 auto all_derivs =
get_derivs(model, T, rhovec, std::nullopt);
210 auto derivs = all_derivs.tot;
214 auto plusT =
get_derivs(model, T + dT, rhovec, all_derivs.ei.v0).tot;
215 auto minusT =
get_derivs(model, T - dT, rhovec, all_derivs.ei.v0).tot;
216 auto derivT = (plusT - minusT) / (2.0 * dT);
219 auto ei = all_derivs.ei;
221 auto sigma2 = 2e-5 * rhovec.sum();
223 auto rhovec_plus = (rhovec + ei.v1 * sigma2).eval();
224 auto rhovec_minus = (rhovec - ei.v1 * sigma2).eval();
225 std::string stepping_desc =
"";
226 auto deriv_sigma2 = all_derivs.tot;
227 auto eval = [](
const auto& ex) {
return ex.eval(); };
228 if (
all(eval(rhovec_minus > 0)) &&
all(eval(rhovec_plus > 0))) {
230 auto plus_sigma2 =
get_derivs(model, T, rhovec_plus, ei.v0);
231 auto minus_sigma2 =
get_derivs(model, T, rhovec_minus, ei.v0);
232 deriv_sigma2 = (plus_sigma2.tot - minus_sigma2.tot) / (2.0 * sigma2);
233 stepping_desc =
"conventional centered";
235 else if (
all(eval(rhovec_plus > 0))) {
237 auto plus_sigma2 =
get_derivs(model, T, rhovec_plus, ei.v0);
238 auto rhovec_2plus = (rhovec + 2 * ei.v1 * sigma2).eval();
239 auto plus2_sigma2 =
get_derivs(model, T, rhovec_2plus, ei.v0);
240 deriv_sigma2 = (-3 * derivs + 4 * plus_sigma2.tot - plus2_sigma2.tot) / (2.0 * sigma2);
241 stepping_desc =
"forward";
243 else if (
all(eval(rhovec_minus > 0))) {
245 auto minus_sigma2 =
get_derivs(model, T, rhovec_minus, ei.v0);
246 auto rhovec_2minus = (rhovec - 2 * ei.v1 * sigma2).eval();
247 auto minus2_sigma2 =
get_derivs(model, T, rhovec_2minus, ei.v0);
248 deriv_sigma2 = (-3 * derivs + 4 * minus_sigma2.tot - minus2_sigma2.tot) / (-2.0 * sigma2);
249 stepping_desc =
"backwards";
252 throw std::invalid_argument(
"This is not possible I think.");
256 Eigen::MatrixXd b(2, 2);
257 b << derivs[3], derivs[4],
258 deriv_sigma2[2], deriv_sigma2[3];
260 auto LHS = (ei.eigenvectorscols * b).transpose();
261 Eigen::MatrixXd RHS(2, 1); RHS << -
derivT[2], -
derivT[3];
262 Eigen::MatrixXd drhovec_dT = LHS.colPivHouseholderQr().solve(RHS);
264#if defined(DEBUG_get_drhovec_dT_crit)
265 std::cout <<
"LHS: " << LHS << std::endl;
266 std::cout <<
"RHS: " << RHS << std::endl;
267 std::cout <<
"b: " << b << std::endl;
268 std::cout <<
"stepping_desc: " << stepping_desc << std::endl;
269 std::cout <<
"deriv_sigma2: " << deriv_sigma2 << std::endl;
270 std::cout <<
"rhovec_plus: " << rhovec_plus << std::endl;
271 std::cout <<
"all_derivs.tot:" << all_derivs.tot << std::endl;
272 std::cout <<
"all_derivs.psir:" << all_derivs.psir << std::endl;
273 auto plus_sigma2 =
get_derivs(model, T, rhovec_plus, ei.v0);
274 std::cout <<
"plus_sigma2.tot:" << plus_sigma2.tot << std::endl;
275 std::cout <<
"plus_sigma2.psir:" << plus_sigma2.psir << std::endl;
276 std::cout <<
"dot of v0: " << plus_sigma2.ei.v0 * ei.v0 << std::endl;
391 static auto trace_critical_arclength_binary(
const AbstractModel& model,
const Scalar& T0,
const VecType& rhovec0,
const std::optional<std::string>& filename_ = std::nullopt,
const std::optional<TCABOptions> &options_ = std::nullopt) -> nlohmann::json {
392 std::string filename = filename_.value_or(
"");
398 using state_type = std::vector<double>;
399 using namespace boost::numeric::odeint;
400 euler<state_type> eul;
401 typedef runge_kutta_cash_karp54< state_type > error_stepper_type;
402 typedef controlled_runge_kutta< error_stepper_type > controlled_stepper_type;
404 auto dot = [](
const auto& v1,
const auto& v2) {
return (v1 * v2).sum(); };
405 auto norm = [](
const auto& v) {
return sqrt((v * v).sum()); };
407 auto JSONdata = nlohmann::json::array();
408 std::ofstream ofs = (filename.empty()) ? std::ofstream() : std::ofstream(filename);
410 double c = options.
init_c;
414 auto xprime = [&](
const state_type& x, state_type& dxdt,
const double )
417 const double T = x[0];
418 const auto rhovec = Eigen::Map<const Eigen::ArrayXd>(&(x[0]) + 1, x.size() - 1);
420 throw std::invalid_argument(
"Density is negative");
424 auto dTdt = 1.0 / norm(drhodT);
425 Eigen::ArrayXd drhodt = c * (drhodT * dTdt).eval();
429 auto drhodtview = Eigen::Map<Eigen::ArrayXd>(&(dxdt[0]) + 1, dxdt.size() - 1);
432 if (last_drhodt.size() > 0) {
433 auto rhodot = dot(drhodt, last_drhodt);
435 Eigen::Map<Eigen::ArrayXd>(&(dxdt[0]), dxdt.size()) *= -1;
439 auto get_dxdt = [&](
const state_type& x) {
440 auto dxdt = state_type(x.size());
445 auto extract_drhodt = [](
const state_type& dxdt) -> Eigen::ArrayXd {
446 return Eigen::Map<const Eigen::ArrayXd>(&(dxdt[0]) + 1, dxdt.size() - 1);
449 auto extract_dTdt = [](
const state_type& dxdt) ->
double {
454 double abs_err = options.
abs_err, rel_err = options.
rel_err, a_x = 1.0, a_dxdt = 1.0;
455 controlled_stepper_type controlled_stepper(default_error_checker< double, range_algebra, default_operations >(abs_err, rel_err, a_x, a_dxdt));
457 double t = 0, dt = options.
init_dt;
460 std::vector<double> x0(rhovec0.size() + 1);
462 Eigen::Map<Eigen::ArrayXd>(&(x0[0]) + 1, x0.size() - 1) = rhovec0;
467 auto rhovec = Eigen::Map<Eigen::ArrayXd>(&(x0[0]) + 1, x0.size() - 1);
469 auto store_drhodt = [&](
const state_type& x0) {
470 last_drhodt = extract_drhodt(get_dxdt(x0));
473 auto store_point = [&]() {
476 auto rhotot = rhovec.sum();
477 double p = rhotot * model.R(rhovec / rhovec.sum()) * T + model.get_pr(T, rhovec);
479 double splus = model.get_splus(T, rhovec);
481 xprime(x0, dxdt, -1.0);
484 nlohmann::json point = {
487 {
"rho0 / mol/m^3",
static_cast<double>(rhovec[0])},
488 {
"rho1 / mol/m^3",
static_cast<double>(rhovec[1])},
493 {
"drho0/dt", dxdt[1]},
494 {
"drho1/dt", dxdt[2]},
495 {
"lambda1", conditions[0]},
496 {
"dirderiv(lambda1)/dalpha", conditions[1]},
501 JSONdata.push_back(point);
505 auto write_line = [&]() {
506 std::stringstream out;
507 auto rhotot = rhovec.sum();
508 double z0 = rhovec[0] / rhotot;
510 out << z0 <<
"," << rhovec[0] <<
"," << rhovec[1] <<
"," << T <<
"," << rhotot * model.R(rhovec / rhovec.sum()) * T + model.get_pr(T, rhovec) <<
"," << c <<
"," << dt <<
"," << conditions(0) <<
"," << conditions(1) << std::endl;
511 std::string sout(out.str());
518 int counter_T_converged = 0, retry_count = 0;
519 ofs <<
"z0 / mole frac.,rho0 / mol/m^3,rho1 / mol/m^3,T / K,p / Pa,c,dt,condition(1),condition(2)" << std::endl;
523 const auto step = (rhovec + extract_drhodt(get_dxdt(x0))*dt).eval();
524 Eigen::ArrayX<bool> negativestepvals = (step < 0).eval();
526 if (negativestepvals.any()) {
531 if (!filename.empty()) {
538 auto dxdt_start_step = get_dxdt(x0);
539 auto x_start_step = x0;
541 if (iter == 0 && retry_count == 0) {
545 auto res = controlled_step_result::fail;
547 res = controlled_stepper.try_step(xprime, x0, t, dt);
549 catch (
const std::exception &e) {
551 std::cout << e.what() << std::endl;
556 if (res != controlled_step_result::success) {
571 dt = std::min(dt, options.
max_dt);
575 eul.do_step(xprime, x0, t, dt);
578 catch (
const std::exception &e) {
579 std::cout << e.what() << std::endl;
584 throw std::invalid_argument(
"integration order is invalid:" + std::to_string(options.
integration_order));
587 auto z0 = rhovec[0] / rhovec.sum();
588 if (z0 < 0 || z0 > 1) {
590 std::cout <<
"Termination because z0 of " + std::to_string(z0) +
" is outside [0, 1]" << std::endl;
596 using PolisherType = std::function<std::tuple<double, VecType>(
const AbstractModel&,
const Scalar,
const VecType&)>;
597 std::vector<PolisherType> polishers = {
598 [&](
const AbstractModel& model,
const Scalar T,
const VecType& rhovec) {
600 return std::make_tuple(Tnew, rhovecnew);
602 [&](
const AbstractModel& model,
const Scalar T,
const VecType& rhovec) {
604 return std::make_tuple(T, rhovecnew);
606 [&](
const AbstractModel& model,
const Scalar T,
const VecType& rhovec) {
609 return std::make_tuple(Tnew, rhovecnew);
611 [&](
const AbstractModel& model,
const Scalar T,
const VecType& rhovec) {
614 return std::make_tuple(Tnew, rhovecnew);
619 bool polish_ok =
false;
620 for (
auto &polisher : polishers){
622 auto [Tnew, rhovecnew] = polisher(model, T, rhovec);
634 catch (std::exception& e) {
636 std::cout <<
"Tracing problem: " << e.what() << std::endl;
637 std::cout <<
"Starting:: T:" << T <<
"; rhovec: " << rhovec << std::endl;
639 std::cout <<
"Starting crit. cond.: " << conditions << std::endl;
641 auto [Tnew, rhovecnew] = polisher(model, T, rhovec);
642 std::cout <<
"Ending:: T:" << Tnew <<
"; rhovec: " << rhovecnew << std::endl;
653 std::cout <<
"Termination because polishing failed" << std::endl;
668 store_drhodt(x_start_step); }
670 auto actualstep = (Eigen::Map<Eigen::ArrayXd>(&(x0[0]), x0.size()) - Eigen::Map<Eigen::ArrayXd>(&(x_start_step[0]), x_start_step.size())).eval();
673 if (std::abs(actualstep[0]) < options.
T_tol) {
674 counter_T_converged++;
677 counter_T_converged = 0;
680 auto rhotot = rhovec.sum();
681 z0 = rhovec[0] / rhotot;
682 if (z0 < 0 || z0 > 1) {
684 std::cout <<
"Termination because z0 of " + std::to_string(z0) +
" is outside [0, 1]" << std::endl;
689 if (!filename.empty()) { write_line(); }
694 std::cout <<
"Termination because maximum number of small steps were taken" << std::endl;
703 auto dxdt = get_dxdt(x0);
704 auto drhodt = extract_drhodt(dxdt);
705 const auto step = (rhovec + drhodt*dt).eval();
706 if ((step * rhovec > 0).any()) {
708 auto step_sizes = ((-rhovec) / drhodt).eval();
710 rhovec.maxCoeff(&ipure);
712 auto new_step_size = step_sizes(ipure);
714 const auto new_rhovec = (rhovec + drhodt*new_step_size).eval();
715 const double new_T = T + extract_dTdt(dxdt)*new_step_size;
723 nlohmann::json flags = { {
"alternative_pure_index", ipure}, {
"alternative_length", 2} };
728 rhovec[ipure] = rhorho;
729 rhovec[1 - ipure] = 0;
732 if (!filename.empty()) { write_line(); }