Skip to content
Snippets Groups Projects
multifluid.cpp 8.81 KiB
Newer Older
  • Learn to ignore specific revisions
  • #include "teqp/core.hpp"
    
    #include "teqp/models/multifluid.hpp"
    
    #include "teqp/critical_tracing.hpp"
    
    #include <optional>
    
    //#include "autodiff/forward.hpp"
    //#include "autodiff/reverse.hpp"
    
    auto build_multifluid_model(const std::vector<std::string>& components, const std::string &coolprop_root, const nlohmann::json &BIPcollection) {
    
        auto [Tc, vc] = MultiFluidReducingFunction::get_Tcvc(coolprop_root, components);
        auto F = MultiFluidReducingFunction::get_F_matrix(BIPcollection, components);
    
        auto funcs = get_departure_function_matrix(coolprop_root, BIPcollection, components);
        auto EOSs = get_EOSs(coolprop_root,  components);
    
        auto [betaT, gammaT, betaV, gammaV] = MultiFluidReducingFunction::get_BIP_matrices(BIPcollection, components);
    
        auto redfunc = MultiFluidReducingFunction(betaT, gammaT, betaV, gammaV, Tc, vc);
        
        return MultiFluid(
                std::move(redfunc), 
                std::move(CorrespondingStatesContribution(std::move(EOSs))), 
                std::move(DepartureContribution(std::move(F), std::move(funcs)))
        );
    
    template<typename ModelType>
    void trace(std::vector<std::string> fluids, const ModelType& model, int i){
    
    Ian Bell's avatar
    Ian Bell committed
        auto rhoc0 = 1.0/model.redfunc.vc[0];
        auto T = model.redfunc.Tc[0];
    
        const auto dT = 1;
    
    Ian Bell's avatar
    Ian Bell committed
        std::valarray<double> rhovec = { rhoc0, 0.0 };
        for (auto iter = 0; iter < 1000; ++iter) {
            auto drhovecdT = get_drhovec_dT_crit(model, T, rhovec);
            rhovec += drhovecdT * dT;
            T += dT;
            int rr = 0;
            auto z0 = rhovec[0] / rhovec.sum();
            std::cout << z0 << " ," << rhovec[0] << "," << T << std::endl;
            if (z0 < 0) {
                break;
            }
        }
    }
    
    template<typename Callable, typename Inputs>
    auto NewtonRaphson(Callable f, const Inputs &args, double tol) {
        // Jacobian matrix
        Eigen::ArrayXd x = args, r0;
        Eigen::MatrixXd J(args.size(), args.size());
        for (int iter = 0; iter < 30; ++iter){
            r0 = f(x);
            for (auto i = 0; i < args.size(); ++i) {
                auto dri = std::max(1e-3*x[i], 1e-8);
                auto argsplus = x;
                argsplus[i] += dri;
                J.col(i) = (f(argsplus) - r0)/dri; // Forward centered diff to avoid negative composition possibility
            }
            Eigen::ArrayXd v = J.colPivHouseholderQr().solve(-r0.matrix());
            x += v;
            auto err = r0.matrix().norm();
            if (err < tol) {
                break;
            }
        }
        return x;
    }
    
    template<typename ModelType>
    
    void trace_arclength(std::vector<std::string> fluids, const ModelType &model, std::size_t i) {
    
    
        auto rhoc0 = 1.0 / model.redfunc.vc[i];
        auto T = model.redfunc.Tc[i];
        double t = 0.0, dt = 100;
    
        std::valarray<double> last_drhodt;
    
        std::valarray<double> rhovec(2); rhovec[i] = { rhoc0 }; rhovec[1L-i] = 0.0;
    
    
        // Non-analytic terms make it impossible to initialize AT the pure components
        if (fluids[0] == "CarbonDioxide" || fluids[1] == "CarbonDioxide"){
            if (i == 0) {
                rhovec[i] *= 0.9999;
    
                rhovec[1L - i] = 0.9999;
    
            }
            else {
                rhovec[i] *= 1.0001;
    
                rhovec[1L-i] = 1.0001;
    
            }
            double zi = rhovec[i]/rhovec.sum();
    
            T = zi* model.redfunc.Tc[i] + (1-zi)* model.redfunc.Tc[1L-i];
    
        auto dot = [](const auto& v1, const auto& v2) { return (v1 * v2).sum(); }; 
    
        auto norm = [](const auto &v){ return sqrt((v*v).sum()); };
    
        std::string filename = fluids[0] + "_" + fluids[1] + ".csv";
        std::ofstream ofs(filename);
        double c = 1.0;
        ofs << "z0 / mole frac.,rho0 / mol/m^3,rho1 / mol/m^3,T / K,p / Pa,c" << std::endl;
    
        for (auto iter = 0; iter < 1000; ++iter) {
    
            auto rhotot = rhovec.sum();
            auto z0 = rhovec[0] / rhotot;
    
    
            if (fluids[0] == "CarbonDioxide" || fluids[1] == "CarbonDioxide") {
                auto polish_x_resid = [&model, &z0](const auto& x) {
                    auto T = x[0];
                    std::valarray<double> rhovec = { x[1], x[2] };
                    auto z0new = rhovec[0] / rhovec.sum();
                    auto derivs = get_derivs(model, T, rhovec);
                    // First two are residuals on critical point, third is residual on composition
                    return (Eigen::ArrayXd(3) << derivs.tot[2], derivs.tot[3], z0new - z0).finished();
                };
                try {
                    Eigen::ArrayXd x0(3); x0 << T, rhovec[0], rhovec[1];
                    auto r0 = polish_x_resid(x0);
                    auto x = NewtonRaphson(polish_x_resid, x0, 1e-10);
                    auto r = polish_x_resid(x);
                    Eigen::ArrayXd change = x0 - x;
                    if (!std::isfinite(T) || !std::isfinite(x[1]) || !std::isfinite(x[2])) {
                        throw std::invalid_argument("Something not finite; aborting polishing");
                    }
                    T = x[0]; rhovec[0] = x[1]; rhovec[1] = x[2];
                }
                catch (std::exception& e) {
                    std::cout << e.what() << std::endl;
                }
            }
    
    
            auto write_line = [&rhovec, &rhotot, &z0, &model, &T, &c, &ofs](){
                std::stringstream out;
                out << z0 << "," << rhovec[0] << "," << rhovec[1] << "," << T << "," << rhotot*model.R*T + get_pr(model, T, rhovec) << "," << c << std::endl;
                std::string sout(out.str());
                ofs << sout;
                std::cout << sout;
            };
            if (iter == 0) {
                write_line();
            }
    
    
            auto drhodT = get_drhovec_dT_crit(model, T, rhovec);
            auto dTdt = 1.0 / norm(drhodT);
            auto drhodt = drhodT * dTdt;
    
            // Flip the sign if the tracing wants to go backwards, or if the first step would take you to negative concentrations
    
            if (iter == 0 && any(rhovec + c*drhodt*dt < 0)) {
                c *= -1;
    
            else if (iter > 0 && dot(c*drhodt, last_drhodt) < 0){
                c *= -1;
    
            rhovec += c*drhodt*dt;
            T += c*dTdt*dt;
    
            z0 = rhovec[0] / rhovec.sum();
    
            if (z0 < 0 || z0 > 1) {
                break;
            }
    
    
            auto polish_x_resid = [&model, &z0](const auto& x) {
                auto T = x[0];
                std::valarray<double> rhovec = { x[1], x[2] };
                auto z0new = rhovec[0] / rhovec.sum();
                auto derivs = get_derivs(model, T, rhovec);
                // First two are residuals on critical point, third is residual on composition
                return (Eigen::ArrayXd(3) << derivs.tot[2], derivs.tot[3], z0new - z0).finished();
            };
            try {
                Eigen::ArrayXd x0(3); x0 << T, rhovec[0], rhovec[1];
                auto r0 = polish_x_resid(x0);
                auto x = NewtonRaphson(polish_x_resid, x0, 1e-10);
                auto r = polish_x_resid(x);
                Eigen::ArrayXd change = x0 - x;
                if (!std::isfinite(T) || !std::isfinite(x[1]) || !std::isfinite(x[2])) {
                    throw std::invalid_argument("Something not finite; aborting polishing");
                }
                T = x[0]; rhovec[0] = x[1]; rhovec[1] = x[2];
            }
            catch (std::exception& e) {
                std::cout << e.what() << std::endl;
            }
    
            rhotot = rhovec.sum(); 
            z0 = rhovec[0] / rhotot;
    
            
            if (z0 < 0 || z0 > 1) {
                break;
            }
    
            last_drhodt = c*drhodt;
            write_line();
    
    int main(){
    
        //trace(); 
        std::string coolprop_root = "C:/Users/ihb/Code/CoolProp";
        coolprop_root = "../mycp";
        auto BIPcollection = nlohmann::json::parse(
            std::ifstream(coolprop_root + "/dev/mixtures/mixture_binary_pairs.json")
        );
    
        std::vector<std::vector<std::string>> pairs = { 
    
            { "CarbonDioxide", "R1234YF" }, { "CarbonDioxide","R1234ZE(E)" }, { "ETHYLENE","R1243ZF" }, 
            { "R1234YF","R1234ZE(E)" }, { "R134A","R1234YF" }, { "R23","R1234YF" }, 
            { "R32","R1123" }, { "R32","R1234YF" }, { "R32","R1234ZE(E)" }
    
        };
        for (auto &pp : pairs) {
            using ModelType = decltype(build_multifluid_model(pp, coolprop_root, BIPcollection));
            std::optional<ModelType> model{std::nullopt};
            try {
                 model.emplace(build_multifluid_model(pp, coolprop_root, BIPcollection));
            }
            catch (std::exception &e) {
                std::cout << e.what() << std::endl;
                std::cout << pp[0] << "&" << pp[1] << std::endl;
                continue;
            }
            for (int i : {0, 1}){
                trace_arclength(pp, model.value(), i);
            }
        }
    
        /*auto model = build_multifluid_model({ "methane", "ethane" });
    
    Ian Bell's avatar
    Ian Bell committed
        std::valarray<double> rhovec = { 1.0, 2.0 };
    
    Ian Bell's avatar
    Ian Bell committed
        double T = 300;
        auto alphar = model.alphar(T, rhovec);
        double h = 1e-100;
        auto alpharcom = model.alphar(std::complex<double>(T, h), rhovec).imag()/h;
        MultiComplex<double> Th{{T, h}};
    
        auto alpharcom2 = model.alphar(Th, rhovec).complex().imag()/h;*/
    
    Ian Bell's avatar
    Ian Bell committed
    
        //autodiff::dual varT;
        //auto dalphardT = derivative([&model, &rhovec](auto &T){return model.alphar(T, rhovec); }, wrt(varT), at(varT));
    
    
        return EXIT_SUCCESS;