Newer
Older
#pragma once
#include "teqp/derivs.hpp"
#include <Eigen/Dense>
template<typename Model, typename TYPE = double>
class IsothermPureVLEResiduals {
typedef Eigen::Array<TYPE, 2, 1> EigenArray;
typedef Eigen::Array<TYPE, 1, 1> EigenArray1;
typedef Eigen::Array<TYPE, 2, 2> EigenMatrix;
private:
const Model& m_model;
TYPE m_T;
EigenMatrix J;
EigenArray y;
IsothermPureVLEResiduals(const Model& model, TYPE T) : m_model(model), m_T(T) {
std::valarray<double> molefrac = { 1.0 };
Rr = m_model.R(molefrac);
R0 = m_model.R(molefrac);
const auto& get_errors() { return y; };
auto call(const EigenArray& rhovec) {
assert(rhovec.size() == 2);
const EigenArray1 rhovecL = rhovec.head(1);
const EigenArray1 rhovecV = rhovec.tail(1);
const auto rhomolarL = rhovecL.sum(), rhomolarV = rhovecV.sum();
const auto molefracs = (EigenArray1() << 1.0).finished();
using id = IsochoricDerivatives<Model,TYPE,EigenArray1>;
using tdx = TDXDerivatives<Model,TYPE,EigenArray1>;
const TYPE &T = m_T;
const TYPE R = m_model.R(molefracs);
auto derL = tdx::template get_Ar0n<2>(m_model, T, rhomolarL, molefracs);
auto pRTL = rhomolarL*(R0_over_Rr + derL[1]); // p/(R*T)
auto dpRTLdrhoL = R0_over_Rr + 2*derL[1] + derL[2];
auto hatmurL = derL[1] + derL[0] + R0_over_Rr*log(rhomolarL);
auto dhatmurLdrho = (2*derL[1] + derL[2])/rhomolarL + R0_over_Rr/rhomolarL;
auto derV = tdx::template get_Ar0n<2>(m_model, T, rhomolarV, molefracs);
auto pRTV = rhomolarV*(R0_over_Rr + derV[1]); // p/(R*T)
auto dpRTVdrhoV = R0_over_Rr + 2*derV[1] + derV[2];
auto hatmurV = derV[1] + derV[0] + R0_over_Rr *log(rhomolarV);
auto dhatmurVdrho = (2*derV[1] + derV[2])/rhomolarV + R0_over_Rr/rhomolarV;
y(0) = pRTL - pRTV;
J(0, 0) = dpRTLdrhoL;
J(0, 1) = -dpRTVdrhoV;
y(1) = hatmurL - hatmurV;
J(1, 0) = dhatmurLdrho;
J(1, 1) = -dhatmurVdrho;
icall++;
return y;
}
auto Jacobian(const EigenArray& rhovec){
return J;
}
//auto numJacobian(const EigenArray& rhovec) {
// EigenArray plus0 = rhovec, plus1 = rhovec;
// double dr = 1e-6 * rhovec[0];
// plus0[0] += dr; plus1[1] += dr;
// EigenMatrix J;
// J.col(0) = (call(plus0) - call(rhovec))/dr;
// J.col(1) = (call(plus1) - call(rhovec))/dr;
// return J;
//}
Eigen::ArrayXd do_pure_VLE_T(Residual &resid, Scalar rhoL, Scalar rhoV, int maxiter) {
auto rhovec = (Eigen::ArrayXd(2) << rhoL, rhoV).finished();
auto r0 = resid.call(rhovec);
auto J = resid.Jacobian(rhovec);
for (int iter = 0; iter < maxiter; ++iter){
if (iter > 0) {
r0 = resid.call(rhovec);
J = resid.Jacobian(rhovec);
}
auto v = J.matrix().colPivHouseholderQr().solve(-r0.matrix()).array().eval();
auto rhovecnew = (rhovec + v).eval();
// If the solution has stopped improving, stop. The change in rhovec is equal to v in infinite precision, but
// not when finite precision is involved, use the minimum non-denormal float as the determination of whether
// the values are done changing
if (((rhovecnew - rhovec).cwiseAbs() < std::numeric_limits<Scalar>::min()).all()) {
break;
}
return (Eigen::ArrayXd(2) << rhovec[0], rhovec[1]).finished();
}
template<typename Model, typename Scalar>
Eigen::ArrayXd pure_VLE_T(const Model& model, Scalar T, Scalar rhoL, Scalar rhoV, int maxiter) {
auto res = IsothermPureVLEResiduals(model, T);
return do_pure_VLE_T(res, rhoL, rhoV, maxiter);
Ian Bell
committed
}
template<typename Model, typename Scalar>
Eigen::ArrayXd extrapolate_from_critical(const Model& model, const Scalar Tc, const Scalar rhoc, const Scalar T) {
using tdx = TDXDerivatives<Model>;
auto z = (Eigen::ArrayXd(1) << 1.0).finished();
auto R = model.R(z);
auto dpdrho = R*Tc*(1 + 2 * ders[1] + ders[2]); // Should be zero
auto d2pdrho2 = R*Tc/rhoc*(2 * ders[1] + 4 * ders[2] + ders[3]); // Should be zero
auto d3pdrho3 = R*Tc/(rhoc*rhoc)*(6 * ders[2] + 6 * ders[3] + ders[4]);
auto Ar11 = tdx::template get_Ar11(model, Tc, rhoc, z);
auto Ar12 = tdx::template get_Ar12(model, Tc, rhoc, z);
auto d2pdrhodT = R * (1 + 2 * ders[1] + ders[2] - 2 * Ar11 - Ar12);
auto Brho = sqrt(6*d2pdrhodT*Tc/d3pdrho3);
auto drhohat_dT = Brho / Tc;
auto dT = T - Tc;
auto drhohat = dT * drhohat_dT;
auto rholiq = -drhohat/sqrt(1 - T/Tc) + rhoc;
auto rhovap = drhohat/sqrt(1 - T/Tc) + rhoc;
return (Eigen::ArrayXd(2) << rholiq, rhovap).finished();
}
template<class A, class B>
auto linsolve(const A &a, const B& b) {
return a.matrix().colPivHouseholderQr().solve(b.matrix()).array().eval();
}
template<class Model, class Scalar, class VecType>
std::tuple< Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>, Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>>
get_drhovecdp_Tsat(const Model& model, const Scalar &T, const VecType& rhovecL, const VecType& rhovecV) {
//tic = timeit.default_timer();
using id = IsochoricDerivatives<Model, Scalar, VecType>;
Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> Hliq = id::build_Psi_Hessian_autodiff(model, T, rhovecL).eval();
Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> Hvap = id::build_Psi_Hessian_autodiff(model, T, rhovecV).eval();
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
//Hvap[~np.isfinite(Hvap)] = 1e20;
//Hliq[~np.isfinite(Hliq)] = 1e20;
auto N = rhovecL.size();
Eigen::MatrixXd A = decltype(Hliq)::Zero(N, N);
auto b = decltype(Hliq)::Ones(N, 1);
decltype(Hliq) drhodp_liq, drhodp_vap;
assert(rhovecL.size() == rhovecV.size());
if ((rhovecL != 0).all() && (rhovecV != 0).all()) {
// Normal treatment for all concentrations not equal to zero
A(0, 0) = Hliq.row(0).dot(rhovecV.matrix());
A(0, 1) = Hliq.row(1).dot(rhovecV.matrix());
A(1, 0) = Hliq.row(0).dot(rhovecL.matrix());
A(1, 1) = Hliq.row(1).dot(rhovecL.matrix());
drhodp_liq = linsolve(A, b);
drhodp_vap = linsolve(Hvap, Hliq*drhodp_liq);
}
else{
// Special treatment for infinite dilution
auto murL = id::build_Psir_gradient_autodiff(model, T, rhovecL);
auto murV = id::build_Psir_gradient_autodiff(model, T, rhovecV);
auto RL = model.R(rhovecL / rhovecL.sum());
auto RV = model.R(rhovecV / rhovecV.sum());
// First, for the liquid part
for (auto i = 0; i < N; ++i) {
for (auto j = 0; j < N; ++j) {
if (rhovecL[j] == 0) {
// Analysis is special if j is the index that is a zero concentration.If you are multiplying by the vector
// of liquid concentrations, a different treatment than the case where you multiply by the vector
// of vapor concentrations is required
// ...
// Initial values
auto Aij = (Hliq.row(j).array().cwiseProduct(((i == 0) ? rhovecV : rhovecL).array().transpose())).eval(); // coefficient - wise product
// A throwaway boolean for clarity
bool is_liq = (i == 1);
// Apply correction to the j term (RT if liquid, RT*phi for vapor)
Aij[j] = (is_liq) ? RL*T : RL*T*exp(-(murV[j] - murL[j])/(RL*T));
// Fill in entry
A(i, j) = Aij.sum();
}
else{
// Normal
A(i, j) = Hliq.row(j).dot(((i==0) ? rhovecV : rhovecL).matrix());
}
}
}
drhodp_liq = linsolve(A, b);
// Then, for the vapor part, also requiring special treatment
// Left - multiplication of both sides of equation by diagonal matrix with liquid concentrations along diagonal, all others zero
auto diagrhovecL = rhovecL.matrix().asDiagonal();
auto PSIVstar = (diagrhovecL*Hvap).eval();
auto PSILstar = (diagrhovecL*Hliq).eval();
for (auto j = 0; j < N; ++j) {
if (rhovecL[j] == 0) {
PSILstar(j, j) = RL*T;
PSIVstar(j, j) = RV*T/exp(-(murV[j] - murL[j]) / (RV * T));
}
}
drhodp_vap = linsolve(PSIVstar, PSILstar*drhodp_liq);
}
return std::make_tuple(drhodp_liq, drhodp_vap);