Skip to content
Snippets Groups Projects
multifluid.cpp 8.81 KiB
Newer Older
#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;