Skip to content
Snippets Groups Projects
VLE.hpp 8.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • #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;
    
    public:
        std::size_t icall = 0;
    
        double Rr, R0;
    
        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);
    
            double R0_over_Rr = R0 / Rr;
    
            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;
        //}
    
    Ian Bell's avatar
    Ian Bell committed
    
    template<typename Residual, typename Scalar>
    
    Ian Bell's avatar
    Ian Bell committed
    Eigen::ArrayXd do_pure_VLE_T(Residual &resid, Scalar rhoL, Scalar rhoV, int maxiter) {
    
    Ian Bell's avatar
    Ian Bell committed
        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){
    
    Ian Bell's avatar
    Ian Bell committed
            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
    
    Ian Bell's avatar
    Ian Bell committed
            // the values are done changing
            if (((rhovecnew - rhovec).cwiseAbs() < std::numeric_limits<Scalar>::min()).all()) {
                break;
            }
    
            rhovec = rhovecnew;
    
    Ian Bell's avatar
    Ian Bell committed
        }
    
    Ian Bell's avatar
    Ian Bell committed
        return (Eigen::ArrayXd(2) << rhovec[0], rhovec[1]).finished();
    
    }
    
    template<typename Model, typename Scalar>
    
    Ian Bell's avatar
    Ian Bell committed
    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's avatar
    Ian Bell committed
    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();
    
    Ian Bell's avatar
    Ian Bell committed
        auto ders = tdx::template get_Ar0n<4>(model, Tc, rhoc, 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]);
    
    Ian Bell's avatar
    Ian Bell committed
        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>;
    
    Ian Bell's avatar
    Ian Bell committed
        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();
    
        //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);
    
    Ian Bell's avatar
    Ian Bell committed
    }