From 184ada1b7fa287d9c08de9b6dd4440f20b659510 Mon Sep 17 00:00:00 2001 From: Sven <sven.pohl11991@gmail.com> Date: Tue, 8 Aug 2023 17:45:15 +0200 Subject: [PATCH] Added properties.hpp for statistics --- include/teqp/algorithms/properties.hpp | 639 +++++++++++++ include/teqp/algorithms/vle_trend.hpp | 558 ++++++++++++ include/teqp/models/mie/mie.hpp | 1164 ++++++++++++++++++++++-- include/teqp/types.hpp | 6 + interface/CPP/teqp_impl_factory.cpp | 2 +- 5 files changed, 2305 insertions(+), 64 deletions(-) create mode 100644 include/teqp/algorithms/properties.hpp create mode 100644 include/teqp/algorithms/vle_trend.hpp diff --git a/include/teqp/algorithms/properties.hpp b/include/teqp/algorithms/properties.hpp new file mode 100644 index 0000000..5813fce --- /dev/null +++ b/include/teqp/algorithms/properties.hpp @@ -0,0 +1,639 @@ +#pragma once +#pragma once +#include "teqp/types.hpp" +#include "teqp/derivs.hpp" +#include "teqp/algorithms/VLE.hpp" +#include "teqp/models/multifluid_ancillaries.hpp" +#include <Eigen/Dense> +#include <fstream> +#include <vector> +#include <iostream> +#include <string> +#include <algorithm> +#include <teqp/models/cubics.hpp> +#include "teqp/algorithms/vle_trend.hpp" +#include "teqp/cpp/teqpcpp.hpp" +#include "teqp/json_builder.hpp" +#include <teqp/cpp/deriv_adapter.hpp> +#include "teqp/ideal_eosterms.hpp" +using namespace std; +using namespace teqp; +using namespace Mie; + +namespace teqp { + namespace cppinterface { + + std::unique_ptr<teqp::cppinterface::AbstractModel> make_SAFTVRMie(const nlohmann::json& j); + + using makefunc = std::function<std::unique_ptr<teqp::cppinterface::AbstractModel>(const nlohmann::json& j)>; + using namespace teqp::cppinterface::adapter; + + static std::unordered_map<std::string, makefunc> pointer_factory = { + {"MieElong", [](const nlohmann::json& spec) { return make_owned(Mie::MieElong(spec.at("model_path"),spec.at("components"),spec.at("combination"))); }} + }; + + std::unique_ptr<teqp::cppinterface::AbstractModel> build_model_ptr(const nlohmann::json& json) { + + // Extract the name of the model and the model parameters + std::string kind = json.at("kind"); + auto spec = json.at("model"); + + auto itr = pointer_factory.find(kind); + if (itr != pointer_factory.end()) { + return (itr->second)(spec); + } + else { + throw std::invalid_argument("Don't understand \"kind\" of: " + kind); + } + } + + std::unique_ptr<AbstractModel> make_multifluid_model(const std::vector<std::string>& components, const std::string& coolprop_root, const std::string& BIPcollectionpath, const nlohmann::json& flags, const std::string& departurepath) { + return make_owned(build_multifluid_model(components, coolprop_root, BIPcollectionpath, flags, departurepath)); + } + + std::unique_ptr<AbstractModel> make_model(const nlohmann::json& j) { + return build_model_ptr(j); + } + } +} + +namespace teqp { + + + struct data_point { + double T, P, D, SND; + std::vector<double> x; + std::string author, mix_name; + int nr; + int id; + double dense_v, dense_l, snd_l, snd_v; + std::vector<double> dense_v_pure; + std::vector<double> dense_l_pure; + double x1, x2, y1, y2; + // excess volumina of vapor and liquid phase + double ve_v, ve_l; + double deviation; + }; + + + + inline auto get_compare_settings(std::string& path) { + std::string filepath = std::filesystem::is_regular_file(path) ? path : path; + nlohmann::json j = load_a_JSON_file(filepath); + compare_settings set; + auto spec = j.at("model"); + set.model_path = spec.at("model_path"); + set.srk_path = spec.at("srk_path"); + set.BIPcollection = spec.at("BIP"); + set.depcollection = spec.at("departure"); + set.coolprop_root = spec.at("coolprop_root"); + std::vector<std::string> mixture = spec.at("components"); + std::vector<std::string> dev_files = spec.at("dev_files"); + set.dev_files = dev_files; + set.mix = mixture; + set.combination = spec.at("combination"); + set.dev_dir = spec.at("dev_dir"); + return set; + } + + + template <typename ModelType> + inline auto check_phase(const ModelType& model, const double& T, const double& rho, const std::vector<double>& molefrac, teqp::phase& phase_est) { + + // Check if pressure is positive (phase doesnt matter) + auto is_correct_phase = false; + using tdx = TDXDerivatives<ModelType, double, decltype(molefrac)>; + auto p = 0.0; auto dpdd = 0.0; auto d2pdd2 = 0.0; + auto ar01 = 0.0; auto ar02 = 0.0; auto ar03 = 0.0; + ar01 = tdx::get_Ar01(model, T, rho, molefrac); + ar02 = tdx::get_Ar02(model, T, rho, molefrac); + ar03 = tdx::get_Ar03(model, T, rho, molefrac); + p = (1.0 + ar01) * T * rho * model.R(molefrac); + dpdd = (1.0 + 2.0 * ar01 + ar02) * model.R(molefrac) * T; + d2pdd2 = (2.0 * ar01 + 4.0 * ar02 + ar03) * model.R(molefrac) * T / rho; + + + if (p > 0.0 && dpdd > 0.0 && d2pdd2 < 0.0 && phase_est == teqp::VAPOR) { + is_correct_phase = true; + } + + if (!(is_correct_phase) && p > 0.0 && dpdd > 0.0 && d2pdd2 > 0.0 && phase_est == teqp::LIQUID) { + is_correct_phase = true; + } + + return is_correct_phase; + } + + template <typename ModelType> + std::tuple<double, double, double, double> get_rho_bounds(const ModelType& model, const double& T, const double& rho_est, const std::vector<double>& molefrac) { + + using tdx = TDXDerivatives<ModelType, double, decltype(molefrac)>; + auto rhomax = 1.100 * rho_est; + auto rhomin = 0.900 * rho_est; + auto rhomax_allowed = 2.0 * rho_est; + auto rhomin_allowed = 0.70 * rho_est; + auto ptest = 0.0; + ptest = (1.0 + tdx::get_Ar01(model, T, rho_est, molefrac)) * T * rho_est * model.R(molefrac); + + if (ptest < 1E-12) { + rhomax = (rhomin_allowed + rhomax_allowed) / 2.0; + rhomin = rhomax * 0.80; + } + else if (isnan(ptest)) { + rhomax = rhomax * 0.70; + rhomin = rhomax * 0.50; + rhomin_allowed = rhomin * 0.10; + } + return std::make_tuple(rhomin, rhomax, rhomin_allowed, rhomax_allowed); + } + + template <typename ModelType> + std::tuple<double, double, double, double> get_rho_bounds_extended(const ModelType& model, const double& T, const double& rho_est, const std::vector<double>& molefrac) { + + using tdx = TDXDerivatives<ModelType, double, decltype(molefrac)>; + auto rhomax = 2.00 * rho_est; + auto rhomin = 0.500 * rho_est; + auto rhomax_allowed = 3.0 * rho_est; + auto rhomin_allowed = 0.2 * rho_est; + auto ptest = 0.0; + ptest = (1.0 + tdx::get_Ar01(model, T, rho_est, molefrac)) * T * rho_est * model.R(molefrac); + + if (ptest < 1E-12) { + rhomax = (rhomin_allowed + rhomax_allowed) / 2.0; + rhomin = rhomax * 0.80; + } + else if (isnan(ptest)) { + rhomax = rhomax * 0.70; + rhomin = rhomax * 0.50; + rhomin_allowed = rhomin * 0.10; + } + return std::make_tuple(rhomin, rhomax, rhomin_allowed, rhomax_allowed); + } + + + + template <typename ModelType, typename ScalarT, typename ScalarP> + auto get_rho_from_t_and_p(const ModelType& model, psrk& psrk_s, ScalarT& T, ScalarP& P, const std::vector<double>& molefrac) { + + + auto rho_est = 0.0; + using tdx = TDXDerivatives<ModelType, ScalarT, decltype(molefrac)>; + + // Assume phase is liquid + teqp::phase phase = LIQUID; + + // Get estimate from pressure srk model + rho_est = psrk_s.get_density(T, P, molefrac, model.R(molefrac), phase); + + + // Define deviation pressure function + auto p_diff = [T, P, molefrac, model](double rho) { + auto A01 = 0.0; + A01 = tdx::get_Ar01(model, T, rho, molefrac); + return P - (1.0 + A01) * T * rho * model.R(molefrac); + }; + + // Initialize the iteration parameters + auto iter = 0; + auto max_iter = 50; + + // Get bounds for regular falsi + auto b = get_rho_bounds(model, T, rho_est, molefrac); + auto rho_l = 0.0; + rho_l = regular_falsi(p_diff, std::get<0>(b), std::get<1>(b), 1E-8, std::get<2>(b), std::get<3>(b), max_iter, iter); + auto is_correct = check_phase(model, T, rho_l, molefrac, phase); + + //if (!(is_correct)) { + phase = VAPOR; + rho_est = psrk_s.get_density(T, P, molefrac, model.R(molefrac), phase); + b = get_rho_bounds(model, T, rho_est, molefrac); + auto rho_v = 0.0; + rho_v = regular_falsi(p_diff, std::get<0>(b), std::get<1>(b), 1E-8, std::get<2>(b), std::get<3>(b), max_iter, iter); + is_correct = check_phase(model, T, rho_v, molefrac, phase); + if (!(is_correct)) { + b = get_rho_bounds_extended(model, T, rho_est, molefrac); + rho_v = regular_falsi(p_diff, std::get<0>(b), std::get<1>(b), 1E-8, std::get<2>(b), std::get<3>(b), max_iter, iter); + is_correct = check_phase(model, T, rho_v, molefrac, phase); + } + //} + + return std::make_tuple(rho_v, rho_l); + } + + auto get_fluid_model(compare_settings& s) { + /* if (s.model_type.compare("Pohl") == 0) {*/ + const auto mix_residual = MieElong(s.model_path, s.mix, s.combination); + + // get all pures a seperate fluids + std::vector<MieElong> pures_residual; + for (size_t i = 0; i < s.mix.size(); i++) { + pures_residual.push_back(MieElong(s.model_path, { s.mix[i] }, s.combination)); + } + + auto pure_data = nlohmann::json::array(); + for (auto f : s.mix) { + std::string path = s.coolprop_root + "/dev/fluids/" + f + ".json"; + auto j = convert_CoolProp_idealgas(path, 0); + pure_data.push_back(j); + } + + IdealHelmholtz mix_ideal(pure_data); + + return std::make_tuple(pures_residual, mix_residual, mix_ideal); + //return +// //nlohmann::json fluid_i = load_a_JSON_file(s.model_path); + + //// get ideal contribution from Multifluid coolprop files + //pure_data = ideal_term(s.mix, fluid_i); + + //} + //if (s.model_type.compare("Multi-Fluid") == 0) { + //const auto mixture_model = build_multifluid_model(s.mix, s.coolprop_root, s.BIPcollection, s.depcollection); + //auto jj = nlohmann::json::parse(mixture_model.get_meta()); + //auto ancillaries = jj.at("pures")[0].at("ANCILLARIES"); + //teqp::MultiFluidVLEAncillaries anc(ancillaries); + ////return build_multifluid_model({ mixture[1] }, coolprop_root, BIPcollection, depcollection); + //return mixture_model; + //} + } + + struct vle_info + { + std::vector<double> pvap, pliq, xL_0, xV_0; + }; + + + class data_set { + + private: + std::vector<data_point> points; + std::vector<string> head; + prop_t prop; + std::vector<double> isotherms; + std::vector<vle_info> vle; + public: + + + // get dev file PVT for mixtures and add to data points + void get_pvt_mix_line(std::string& line) { + std::stringstream ss; + data_point p; + double x1 = 0.0; double x2 = 0.0; + std::string name_mix, add_info; + ss << line; + ss >> p.P >> p.D >> p.T >> x1 >> x2; + std::stringstream rest_line; + if (line.length() > 6) { + add_info = line.substr(51, line.length()); + rest_line << add_info.substr(7, add_info.length()); + p.author = add_info.substr(0, 7); + rest_line >> p.id >> p.mix_name >> p.nr; + p.x.push_back(x1); + p.x.push_back(x2); + if (p.P > 0.0 || (p.D > 0.0 && p.T > 0.0)) { + points.push_back(p); + } + } + + } + + void get_snd_mix_line(std::string& line) { + std::stringstream ss; + data_point p; + double x1 = 0.0; double x2 = 0.0; + std::string name_mix, add_info; + ss << line; + ss >> p.P >> p.T >> p.SND >> x1 >> x2; + std::stringstream rest_line; + if (line.length() > 6) { + add_info = line.substr(62, line.length()); + rest_line << add_info.substr(7, add_info.length()); + p.author = add_info.substr(0, 7); + rest_line >> p.id >> p.mix_name >> p.nr; + p.x.push_back(x1); + p.x.push_back(x2); + if (p.P > 0.0 || (p.SND > 0.0 && p.T > 0.0)) { + points.push_back(p); + } + } + } + + void get_vle_mix_line(std::string& line) { + std::stringstream ss; + data_point p; + double x1 = 0.0; double x2 = 0.0; + std::string name_mix, add_info; + ss << line; + ss >> p.P >> p.T >> p.x1 >> p.x2 >> p.y1 >> p.y2; + std::stringstream rest_line; + if (line.length() > 6) { + add_info = line.substr(51, line.length()); + rest_line << add_info.substr(7, add_info.length()); + p.author = add_info.substr(0, 7); + rest_line >> p.id >> p.mix_name >> p.nr; + p.x.push_back(x1); + p.x.push_back(x2); + if (p.P > 0.0 || (p.SND > 0.0 && p.T > 0.0)) { + points.push_back(p); + } + } + } + + // read dev file by path and assign data + void read_dev_file(std::string& path) { + std::ifstream file; + std::string line; + file.open(path); + bool head_end = false; + + prop = PVT; + + if (path.find("PVT") != std::string::npos) { + prop = PVT; + } + else if (path.find("VE") != std::string::npos) { + prop = VE; + } + else if (path.find("SND") != std::string::npos) { + prop = SND; + } + else if (path.find("VLE") != std::string::npos) { + prop = VLE; + } + + + while (std::getline(file, line)) { + if (line.find("@PCL5") != std::string::npos) { + head.push_back(line); + head_end = true; + } + else if (!(head_end)) { + head.push_back(line); + } + else // numbers + { + if (prop == PVT) { + get_pvt_mix_line(line); + } + else if (prop == VE) { + get_pvt_mix_line(line); + } + else if (prop == SND) { + get_snd_mix_line(line); + } + else if (prop == VLE) { + get_vle_mix_line(line); + } + } + } + file.close(); + + // get isotherms + std::vector<double> tmp; + std::transform(points.begin(), points.end(), std::back_inserter(tmp), + [](data_point const& p) -> double { return p.T; }); + + for (size_t i = 0; i < tmp.size(); ++i) { + if (count(isotherms.begin(), isotherms.end(), tmp.at(i)) == 0) { + isotherms.push_back(tmp.at(i)); + } + } + + + + } + + // get the p-x relation for all isotherms in the dev file + template <typename ModelType, typename PureType> + void get_binary_vle(ModelType& mixture, PureType& pure) { + + for (auto t : isotherms) { + auto [rho_vap, rho_liq] = get_vle_pure_start(pure[0], t, 0); + auto vle_pure_1 = pure_VLE_T(pure[0], t, 1E3 * rho_liq, 1E3 * rho_vap, 50); + auto rhovecV0 = (Eigen::ArrayXd(2) << vle_pure_1(1), 0.0).finished(); + auto rhovecL0 = (Eigen::ArrayXd(2) << vle_pure_1(0), 0.0).finished(); + auto res = trace_VLE_isotherm_binary(mixture, t, rhovecL0, rhovecV0); + + auto vec_packed = [](const auto& res, const auto& key) { + std::vector<double> x; + for (auto res_point : res) { + x.push_back(res_point[key]); + } + return x; + }; + vle_info vle_i; + vle_i.pvap = vec_packed(res, "pL / Pa"); + vle_i.pliq = vec_packed(res, "pV / Pa"); + vle_i.xL_0 = vec_packed(res, "xL_0 / mole frac."); + vle_i.xV_0 = vec_packed(res, "xV_0 / mole frac."); + vle.push_back(vle_i); + } + } + + // Function for PVT mixture stuff + // function to calculate the density from temperature and pressure + template <typename ModelType> + void pvt_fill(const ModelType& model, psrk& helper) { + for (size_t i = 0; i < points.size(); i++) { + auto p_exp = points[i].P * 1E6; + tie(points[i].dense_v, points[i].dense_l) = get_rho_from_t_and_p(model, helper, points[i].T, p_exp, points[i].x); + } + } + + template <typename ModelType, typename PureT> + void ve_fill(const ModelType& model, const PureT& pures, psrk& helper) { + for (size_t i = 0; i < points.size(); i++) { + auto p_exp = points[i].P * 1E6; + tie(points[i].dense_v, points[i].dense_l) = get_rho_from_t_and_p(model, helper, points[i].T, p_exp, points[i].x); + } + std::vector<double> x_pure = { 1.0 }; + for (size_t i = 0; i < points.size(); i++) { + auto p_exp = points[i].P * 1E6; + auto [rho1_v, rho1_l] = get_rho_from_t_and_p(pures[0], helper, points[i].T, p_exp, x_pure); + auto [rho2_v, rho2_l] = get_rho_from_t_and_p(pures[1], helper, points[i].T, p_exp, x_pure); + points[i].ve_v = 1.0 / points[i].dense_v - points[i].x[0] / rho1_v - points[i].x[1] / rho2_v; + points[i].ve_l = 1.0 / points[i].dense_l - points[i].x[0] / rho1_l - points[i].x[1] / rho2_l; + } + } + + template <typename ModelType, typename IdealT> + void snd_fill(const ModelType& model, const IdealT& ideal, psrk& helper) { + // First calculate density for all state points + for (size_t i = 0; i < points.size(); i++) { + auto p_exp = points[i].P * 1E6; + tie(points[i].dense_v, points[i].dense_l) = get_rho_from_t_and_p(model, helper, points[i].T, p_exp, points[i].x); + } + // get speed of sound for each phase + auto snd = [model, ideal](const double& T, const double& rho, std::vector<double>& molefrac, const auto& mw) { + using tdx = TDXDerivatives<ModelType, double, std::vector<double>>; + auto A01 = tdx::get_Ar01(model, T, rho, molefrac); + auto A02 = tdx::get_Ar02(model, T, rho, molefrac); + auto A11 = tdx::get_Ar11(model, T, rho, molefrac); + auto A20 = tdx::get_Ar20(model, T, rho, molefrac); + + auto wih = AlphaCallWrapper<AlphaWrapperOption::idealgas, decltype(ideal)>(ideal); + using tdxi = TDXDerivatives<IdealT, double, std::vector<double> >; + auto A20_id = 0.0; + A20_id = tdxi::get_Agenxy<2, 0, ADBackends::autodiff>(wih, T, rho, molefrac); + return sqrt((1.0 + 2.0 * A01 + A02 - pow(1.0 + A01 - A11, 2.0) / (A20_id + A20)) * model.R(molefrac) / mw * T); + }; + for (size_t i = 0; i < points.size(); i++) { + auto mw = 0.0; + mw = helper.get_mw(points[i].x); + points[i].snd_l = snd(points[i].T, points[i].dense_l, points[i].x, mw); + points[i].snd_v = snd(points[i].T, points[i].dense_v, points[i].x, mw); + } + } + + // Selector for which data type should be calculated + template <typename ModelType, typename PureT, typename IdealT> + void perform_dev_calc(const ModelType& model, const PureT& pures, const IdealT& ideal, psrk& helper) { + std::string file_out = "FILE.PLT"; + switch (prop) + { + case teqp::PVT: + pvt_fill(model, helper); + snd_fill(model, ideal, helper); + calc_dev_pvt(); + write_pvt_dev(file_out); + break; + case teqp::VE: + ve_fill(model, pures, helper); + calc_dev_ve(); + write_pvt_dev(file_out); + case teqp::SND: + snd_fill(model, ideal, helper); + calc_dev_snd(); + write_pvt_dev(file_out); + case teqp::VLE: + calc_dev_vle(); + break; + default: + break; + } + } + + + + auto get_per_dev(double& one, double& two) { + return 100 * (one - two) / two; + } + + // VLE DEV CALC + auto find_iso_idx(std::vector<double>& vec, double& key) { + auto itr = std::find(vec.begin(), vec.end(), key); + return std::distance(vec.begin(), itr); + } + + auto find_closest(std::vector<double>& vec, double& value) { + auto itr = std::lower_bound(vec.begin(), vec.end(), value); + return std::distance(vec.begin(), itr); + } + + + + void calc_dev_vle() { + for (size_t i = 0; i < points.size(); i++) { + auto idx = find_iso_idx(isotherms,points[i].T); + // get closes pvap to p + auto vle_p = vle[idx]; + auto p_exp = points[i].P*1E6; + auto pvap_idx = find_closest(vle_p.pvap,p_exp); + auto x_vap_idx = vle_p.xV_0[pvap_idx]; + } + } + + // calculate deviation of pvt + void calc_dev_pvt() { + for (size_t i = 0; i < points.size(); i++) { + auto dexp = points[i].D * 1E3; + auto dev1 = get_per_dev(dexp, points[i].dense_l); + auto dev2 = get_per_dev(dexp, points[i].dense_v); + if (abs(dev1) < abs(dev2)) { + points[i].deviation = dev1; + points[i].D = points[i].dense_l; + } + else + { + points[i].deviation = dev2; + points[i].D = points[i].dense_v; + } + } + } + + void calc_dev_snd() { + for (size_t i = 0; i < points.size(); i++) { + auto dev1 = get_per_dev(points[i].SND, points[i].snd_l); + auto dev2 = get_per_dev(points[i].SND, points[i].snd_v); + if (abs(dev1) < abs(dev2)) { + points[i].deviation = dev1; + } + else + { + points[i].deviation = dev2; + } + } + } + + // calculate deviation of excess volume + void calc_dev_ve() { + for (size_t i = 0; i < points.size(); i++) { + auto dexp = points[i].D; + auto dev1 = get_per_dev(dexp, points[i].ve_l); + auto dev2 = get_per_dev(dexp, points[i].ve_v); + if (abs(dev1) < abs(dev2)) { + points[i].deviation = dev1; + } + else + { + points[i].deviation = dev2; + } + } + } + + void write_pvt_dev(std::string& file_out) { + ofstream myfile; + myfile.open(file_out); + for (size_t i = 2; i < head.size(); i++) { + myfile << head[i] << std::endl; + } + for (size_t i = 0; i < points.size(); i++) { + std::string formatted_str = std::format("{0:>10.5f}{1:>10.5f}{2:>10.4f}{3:>10.4f}{4:>10.4f}{5:>8s}{6:>6d}{7:>12s}{8:>4d}", points[i].P, points[i].D, points[i].T, points[i].x[0], points[i].deviation, points[i].author, points[i].id, points[i].mix_name, points[i].nr); + myfile << formatted_str << std::endl; + } + myfile.close(); + } + + + }; + + inline auto perform_plt() { + + std::string set_path = "D:/OneDrivePrivat/OneDrive/Work/Diss/PHD/mixtures/compare/settings.json"; + auto settings = get_compare_settings(set_path); + nlohmann::json j = load_a_JSON_file(set_path); + //auto m = make_model(j); + auto [pures, mixture, mix_ideal] = get_fluid_model(settings); + auto helper = psrk(settings); + + std::vector< std::vector<data_point>> data; + for (size_t i = 0; i < settings.dev_files.size(); i++) { + data_set dat; + std::string dev_path = settings.dev_dir + "/" + settings.dev_files[i]; + dat.read_dev_file(dev_path); + dat.get_binary_vle(mixture, pures); + dat.perform_dev_calc(mixture, pures, mix_ideal, helper); + } + + + + return 0; + } + + +} + + + + + diff --git a/include/teqp/algorithms/vle_trend.hpp b/include/teqp/algorithms/vle_trend.hpp new file mode 100644 index 0000000..7c58f7f --- /dev/null +++ b/include/teqp/algorithms/vle_trend.hpp @@ -0,0 +1,558 @@ +#pragma once + +#include "teqp/models/model_potentials/2center_ljf.hpp" +#include "teqp/derivs.hpp" +#include "teqp/models/multifluid_ancillaries.hpp" +#include <Eigen/Dense> +#include <iostream> +#include <fstream> +#include <string> +#include <teqp/models/cubics.hpp> +#include <teqp/cpp/teqpcpp.hpp> +#include "teqp/algorithms/VLE.hpp" +#include "teqp/types.hpp" + +using namespace teqp; + +namespace teqp { + + bool has_any_digits(const std::string& s) { + return std::any_of(s.begin(), s.end(), ::isdigit); + } + + template <typename T> + typename std::enable_if<std::is_unsigned<T>::value, int>::type + inline constexpr signum(T const x) { + return T(0) < x; + } + + template <typename T> + typename std::enable_if<std::is_signed<T>::value, int>::type + inline constexpr signum(T const x) { + return (T(0) < x) - (x < T(0)); + } + + + enum phase { VAPOR, LIQUID }; + enum prop_t { PVT, VLE , VE , SND}; + + struct compare_settings { + std::string model_type; + std::string model_path; + std::string srk_path; + std::string BIPcollection; + std::string depcollection; + std::string coolprop_root; + std::vector<std::string> mix; + std::vector<std::string> dev_files; + std::string dev_dir; + std::string combination; + }; + + + struct fluid_info { + std::vector<double> tc, pc, dc, w; + }; + + struct phase_result + { + + }; + + class psrk { + + public: + std::vector<double> TC, PC, DC, w , mw; + psrk(compare_settings& s) { + std::string filepath = std::filesystem::is_regular_file(s.srk_path) ? s.srk_path : s.srk_path; + nlohmann::json j = load_a_JSON_file(filepath); + for (size_t i = 0; i < s.mix.size(); i++) { + TC.push_back(j.at("srk").at(s.mix[i]).at("tc")); + PC.push_back(j.at("srk").at(s.mix[i]).at("pc")); + DC.push_back(j.at("srk").at(s.mix[i]).at("dc")); + w.push_back(j.at("srk").at(s.mix[i]).at("omega")); + mw.push_back(j.at("srk").at(s.mix[i]).at("mw")); + } + } + + + auto get_mw(std::vector<double>& molefrac) { + auto m = 0.0; + for (size_t i = 0; i < molefrac.size(); i++) + { + m = m + molefrac[i] * mw[i]; + } + return m; + } + + std::vector<double> root_psrk(std::vector<double>& p) { + + auto pie = 3.14159265358979; + std::vector<double> r(3); + auto a = p[0]; + auto b = p[1]; + auto c = p[2]; + auto nr = 0; + auto nn = 0.0; + auto qq = 0.0; + auto pp = 0.0; + auto aa = 0.0; + auto bb = 0.0; + + pp = (a * a - 3. * b) / 9.; + qq = (2. * pow(a, 3.0) - 9. * a * b + 27. * c) / 54.0; + if (pow(pp, 3.0) > pow(qq, 2.0)) { + nn = acos(-qq / pow(pp, 1.5)); + nr = 3; + r[0] = 2. * sqrt(pp) * cos(nn / 3.) - a / 3.; + r[1] = 2. * sqrt(pp) * cos((nn + 2. * pie) / 3.) - a / 3.; + r[2] = 2. * sqrt(pp) * cos((nn + 4. * pie) / 3.) - a / 3.; + } + else { + nr = -1; + aa = -signum(qq) * pow((abs(qq) + sqrt(pow(qq, 2.0) - pow(pp, 3.0))), 1.0 / 3.0); + if (aa == 0) { + bb = 0.0; + } + else { + bb = pp / aa; + } + + r[0] = (aa + bb) - a / 3.0; + r[1] = 0.0; + r[2] = 0.0; + } + + // --creating output vector------------------------------------------------------------------ - + std::vector<double> volume(3); + if (nr < 0) { + volume[0] = -1.0; + volume[1] = r[0]; + volume[2] = 0.0; + } + + + else { + volume[0] = 3.0; + auto idx = max_element(std::begin(r), std::end(r)); + volume[1] = r[*idx]; + auto qq = 1.0; + pp = 1.0; + nn = 1.0; + if (r[0] > 0) { qq = r[0]; } + + if (r[1] > 0) { pp = r[1]; }; + if (r[2] > 0) { nn = r[2]; }; + std::vector<double> r_vec = { qq, pp, nn }; + idx = min_element(std::begin(r_vec), std::end(r_vec)); + volume[2] = *idx; + } + + return volume; + } + + + + auto get_density(const double& T, const double& P, const std::vector<double>& molefrac, const double& REQ, teqp::phase& phase_est) { + auto ncomp = molefrac.size(); + std::vector<double> aa(ncomp), bb(ncomp), cc(3), msrk(ncomp), alfa_lc(ncomp); + std::vector<double> ai(ncomp), bi(ncomp), ci(ncomp); + auto a1 = 0.0; + auto c = 0.0; + auto b = 0.0; + for (auto i = 0; i < molefrac.size(); i++) { + auto tr = T / TC[i]; + aa[i] = 0.42748 * REQ * REQ * TC[i] * TC[i] / PC[i]; + msrk[i] = 0.48 + 1.574 * w[i] - 0.176 * pow(w[i], 2.0); + alfa_lc[i] = pow(1.0 + msrk[i] * (1.0 - sqrt(tr)), 2.0); + ai[i] = aa[i] * alfa_lc[i]; + bi[i] = 0.08664 * REQ * TC[i] / PC[i]; + ci[i] = 0.40768 * REQ * TC[i] / PC[i] * (0.29441 - PC[i] * 1.0 / DC[i] / REQ / TC[i]); + a1 = a1 + molefrac[i] * sqrt(ai[i]); + b = b + molefrac[i] * bi[i]; + c = c + molefrac[i] * ci[i]; + } + + auto a = 0.0; + if (ncomp == 1) { + a = ai[0]; + b = bi[0]; + c = ci[0]; + cc[0] = -REQ * T / P; + cc[1] = -b * b - REQ * T * b / P + a / P; + cc[2] = -a * b / P; + } + else { + a = pow(a1, 2.0); + cc[0] = -REQ * T / P; + cc[1] = -b * b - REQ * T * b / P + a / P; //rmix + cc[2] = -a * b / P; + } + + + + std::vector<double> volume = root_psrk(cc); + + auto psrk_val = 0.0; + auto VSRK = 0.0; + if (volume[0] < -10.0) { + psrk_val = -1.0; + } + else { + if (volume[0] < -0.0) { + VSRK = 1. / (volume[1]); + } + else { + if (phase_est == LIQUID) { + VSRK = 1. / (volume[2]); + } + else { + VSRK = 1. / (volume[1]); + } + } + } + + auto vf1 = 0.0; + if (phase_est == LIQUID) { + vf1 = 1.0 / VSRK; + vf1 = vf1 - c; + VSRK = 1 / vf1; + } + + psrk_val = VSRK; + return psrk_val; + } + + }; + + template<typename Callable> + inline auto regular_falsi(Callable zerofunction, const double& Xstart_min, const double& Xstart_max, const double& Delta_Allowed, const double& Xmin_allowed, const double& Xmax_allowed, int& Max_iterations, int& Iterations) { + + auto Xmin = Xstart_min; + auto Xmax = Xstart_max; + auto Xmin_alt = 0.0; + auto Xmax_alt = 0.0; + auto F_Xmin_alt = 0.0; + auto F_Xmax_alt = 0.0; + auto F_Xmin = 0.0; + auto F_Xmax = 0.0; + auto Stop_min = 0; + auto Stop_max = 0; + auto Int_Errorflag = 0; + bool root_in = false; + bool Root_Found = false; + bool additional_checks = false; + auto Count_enlarge = 0; + auto Count_reduce = 0; + auto result = 0.0; + auto Xakt = 0.0; + auto DERIV = 0.0; + auto F_Xakt = 0.0; + auto Deviation_X = 0.0; + auto Xroot = 0.0; + auto Xtest = 0.0; + auto F_Xtest = 0.0; + auto xmin = 0.0; + auto xmax = 0.0; + auto xakt = 0.0; + + if (Xmin < Xmin_allowed) { Int_Errorflag = 1; } + if (Xmin < Xmin_allowed) { Xmin = Xmin_allowed; } + if (Xmax > Xmax_allowed) { Int_Errorflag = Int_Errorflag + 1; } + if (Xmax > Xmax_allowed) { Xmax = Xmax_allowed; } + if (Int_Errorflag == 1) { Int_Errorflag = 0; } + if (Int_Errorflag == 2) { Int_Errorflag = 1; } + + if (Int_Errorflag == 0) { + F_Xmin = zerofunction(Xmin); + } + + if (Int_Errorflag == 0) { + F_Xmax = zerofunction(Xmax); + } + + if ((F_Xmin * F_Xmax) < 0) { root_in = true; } + + // main loop + while (!(root_in) && Int_Errorflag == 0 && Count_enlarge <= 10) { + Xmin_alt = Xmin; + Xmax_alt = Xmax; + F_Xmin_alt = F_Xmin; + F_Xmax_alt = F_Xmax; + if (Stop_min == 0) { Xmin = Xmin - (Xstart_max - Xstart_min) * 0.5; } + if (Stop_max == 0) { Xmax = Xmax + (Xstart_max - Xstart_min) * 0.5; } + Count_enlarge = Count_enlarge + 1; + if ((Xmin - Xmin_allowed) < -1e-11) { Int_Errorflag = 1; } + if ((Xmax - Xmax_allowed) > 1e-11) { Int_Errorflag = 1; } + if ((Int_Errorflag == 0) && (Stop_min == 0)) { F_Xmin = zerofunction(Xmin); } + if ((Int_Errorflag == 0) && (Stop_min == 0)) { F_Xmax = zerofunction(Xmax); } + if ((abs(F_Xmin_alt) < abs(F_Xmin)) && ((F_Xmin_alt * F_Xmin) > 0)) { Stop_min = 1; } + if (Stop_min == 1) { Xmin = Xmin_alt; } + if (Stop_min == 1) { F_Xmin = F_Xmin_alt; } + if ((abs(F_Xmax_alt) < abs(F_Xmax)) && ((F_Xmax_alt * F_Xmax) > 0)) { Stop_max = 1; } + if (Stop_max == 1) { Xmax = Xmax_alt; } + if (Stop_max == 1) { F_Xmax = F_Xmax_alt; } + if ((F_Xmin * F_Xmax) < 0) { root_in = true; } + } + + // If no root was found by enlarging the interval, there may be two roots within the initial interval. + // To check out this posibility, the original interval is subdivided starting in the midle of the original interval. + // The size of the new interval starts with 1 / 10 of the original interval and is enlarged then. + + while (!(root_in) && (Int_Errorflag == 0) && (Count_reduce < 9)) { + Count_reduce = Count_reduce + 1; + Xmin = (Xstart_max + Xstart_min) / 2.0 - (Xstart_max - Xstart_min) / 20.0 * Count_reduce; + Xmax = (Xstart_max + Xstart_min) / 2.0 + (Xstart_max - Xstart_min) / 20.0 * Count_reduce; + //Check, whether Xmin and Xmax get out of the allowed range + if (Xmin < Xmin_allowed) { Int_Errorflag = 1; } + if (Xmax > Xmax_allowed) { Int_Errorflag = 1; } + if (Int_Errorflag == 0) { F_Xmin = zerofunction(Xmin); } + if (Int_Errorflag == 0) { F_Xmax = zerofunction(Xmax); } + //Check, whether a root is within the new limits + if ((F_Xmin * F_Xmax) < 0.0) { root_in = true; } + } + + //If no root could be found in the original interval, by enlarging or by reducing of the interval the + //routine returns with Errorflag = 2 + + if (!(root_in)) { result = -2; } + + if (result == 0) { // no error occured + + + while (!(Root_Found) && (Iterations <= Max_iterations)) { + + Iterations = Iterations + 1; //Count iteration steps + auto Deriv = (F_Xmax - F_Xmin) / (Xmax - Xmin); //Calculate the "derivative" for the current interval; + + if (abs(Deriv) > 1E-12) { + Deviation_X = F_Xakt / Deriv;//Deviation_X is the distance to the linear approximated root from the akt position on the x - axis + //SH 10 / 2018: If derivative is large make additional checks(see line 300) if root found or not + if (abs(Deriv) > 1E10) { + additional_checks = true; + } + else { + additional_checks = false; + } + } + else { + Deriv = signum(Deriv); + } + + //Calculate Xakt starting from the X value with the smaller function value + if (abs(F_Xmin) < abs(F_Xmax)) { + // Calculate Xakt starting from the lower limit of the interval + if (abs(Deriv) > 0 && Iterations % 5 != 0) { + Xakt = Xmin + abs(F_Xmin / Deriv); + // Use this algorithm only three out of four times to avoid problems in regions // with strong curvature + } + else { + Xakt = (Xmin + Xmax) / 2.; + } + } + else { + if (abs(Deriv) > 0 && Iterations % 5 != 0) { // Calculate Xakt starting from the upper limit of the interval + Xakt = Xmax - abs(F_Xmax / Deriv); + } // Use this algorithm only three out of four times to avoid problems in regions + else { // with strong curvature + Xakt = (Xmin + Xmax) / 2.0; + } + } + + + F_Xakt = zerofunction(Xakt); + + + //Check whether the root has been found + //The criterion Delta_allowed is applied to the deviation in X + + if (abs(Deriv) > 1E-12) { + Deviation_X = F_Xakt / Deriv; + if (abs(Deriv) > 1E10) { + additional_checks = true; + } + else { + additional_checks = false; + } + } + else { + Deriv = signum(Deriv); + } + + if (((abs(Deviation_X) < Delta_Allowed) && (!(additional_checks))) || ((abs(Deviation_X) < Delta_Allowed) && (additional_checks) && ((abs((xakt - xmin) / xakt) < Delta_Allowed) || (abs((xakt - xmax) / xakt) < Delta_Allowed)))) { + Root_Found = true; + Xroot = Xakt + Deviation_X; // Xroot usually is a better guess for the root then Xakt + } + else //Root has not yet been found, new interval has to be defined + { + if (abs(Deviation_X) < ((Xmax - Xmin) / 10.0)) { + + if (((Xakt - Xmin) < (Xmax - Xakt)) && ((Xakt - Xmin) > 0.0)) { Deriv = (F_Xakt - F_Xmin) / (Xakt - Xmin); } + if (((Xakt - Xmin) > (Xmax - Xakt)) && ((Xmax - Xakt) > 0.0)) { Deriv = (F_Xmax - F_Xakt) / (Xmax - Xakt); } + + if (abs(Deriv) > 1E-12) { + Deviation_X = F_Xakt / Deriv; + } + else { + Deriv = signum(Deriv); + } + + if ((F_Xmin * F_Xakt) < 0.0) { + Xtest = Xakt - 2.0 * abs(Deviation_X); + if (Xtest < ((Xmin + Xakt) / 2.0)) { Xtest = (Xmin + Xakt) / 2.0; } + F_Xtest = zerofunction(Xtest); + if ((F_Xtest * F_Xakt) < 0.0) { Xmin = Xtest; } + if ((F_Xtest * F_Xakt) < 0.0) { F_Xmin = F_Xtest; } + } + else if ((F_Xmax * F_Xakt) < 0.0) { + Xtest = Xakt + 2.0 * abs(Deviation_X); + if (Xtest > ((Xmax + Xakt) / 2.0)) { Xtest = (Xmax + Xakt) / 2.0; } + F_Xtest = zerofunction(Xtest); + if ((F_Xtest * F_Xakt) < 0.0) { Xmax = Xtest; } + if ((F_Xtest * F_Xakt) < 0.0) { F_Xmax = F_Xtest; } + } + } + + if ((F_Xmin * F_Xakt) < 0.0) { + Xmax = Xakt; + F_Xmax = F_Xakt; + } + else if ((F_Xmax * F_Xakt) < 0.0) { + Xmin = Xakt; + F_Xmin = F_Xakt; + } + } + + } // end while + } //end if + + if ((Count_enlarge + Count_reduce) > 0) { Xroot = -1000; } + if (!(Root_Found)) { Xroot = -1000; } + + return Xroot; + + } + + + template <typename TypeT, typename TypeX, typename TypeK> + inline auto rache_rich(psrk& fldi, TypeK& K_val, TypeX& x_known, TypeT& vapfrac, int errval) { + auto ncomp = x_known.size(); + auto sumx_bubble = 0.0; + auto sumx_dew = 0.0; + auto sumx_2phase = 0.0; + std::vector<double> x_vap(ncomp); + std::vector<double> x_liq(ncomp); + bool found = false; + auto frac_type = 0; + auto Delta_allowed = 1E-8; + auto frac_min_allowed = -1E-16; + auto frac_min = -1E-16; + auto frac_max = 0.5; + auto frac_max_allowed = 1.0 + 1E-16; + auto Max_iterations = 50; + auto frac = 0.0; + + auto rache_diff = [frac_type, ncomp, K_val, x_known](double frac) { + auto rac_func = 0.0; + if (frac_type == 0) { + for (size_t i = 0; i < ncomp; i++) { + rac_func = rac_func + x_known[i] * (K_val[i] - 1.0) / (1.0 - frac + frac * K_val[i]); + } + } + else { + for (size_t i = 0; i < ncomp; i++) + { + rac_func = rac_func + x_known[i] * (K_val[i] - 1.0) / (frac + (1.0 - frac) * K_val[i]); + } + } + return rac_func; + }; + + for (size_t i = 0; i < ncomp; i++) { + sumx_bubble = sumx_bubble + x_known[i] * K_val[i]; + sumx_dew = sumx_dew + x_known[i] / K_val[i]; + sumx_2phase = sumx_2phase + x_known[i] * (K_val[i] - 1.0) / (K_val[i] + 1.0); + } + + if (sumx_bubble < 1.0 + 1E-15) { + vapfrac = 0; + for (size_t i = 0; i < ncomp; i++) { + x_vap[i] = x_known[i] * K_val[i] / sumx_bubble; + } + x_liq = x_known; + found = true; + } + + if (sumx_dew < 1.0 + 1E-15) { + vapfrac = 0; + for (size_t i = 0; i < ncomp; i++) { + x_liq[i] = x_known[i] / K_val[i] / sumx_dew; + } + x_vap = x_known; + found = true; + } + + //The mixture is in the 2 phase region with 0.5 < vapfrac < 1. + //Instead of vapfrac the liquid fraction alpha is used, since vapfrac near unity might cause round off errors(see Michelsen& Mollerup) + if (!(found)) { + //The liquid fraction is chosen as independent variable + if (sumx_2phase > 0.0) { + frac_type = 1; + } + else { + frac_type = 0; + } + + auto Iterations = 0; + frac = 0.0; + auto res = regular_falsi(rache_diff, frac_min, frac_max, Delta_allowed, frac_min_allowed, frac_max_allowed, Max_iterations, Iterations); + if (errval == 3) { errval = 0;}; + if (errval != 0) { errval = -2111; } + if (frac_type == 1) { + vapfrac = 1.0 - frac; + } + else { + vapfrac = frac; + } + + sumx_dew = 0.0; + sumx_bubble = 0.0; + for (size_t i = 0; i < x_known.size(); i++) { + x_vap[i] = x_known[i] * K_val[i] / (1.0 - vapfrac + vapfrac * K_val[i]); + sumx_dew = sumx_dew + x_vap[i]; + x_liq[i] = x_known[i] / (1.0 - vapfrac + vapfrac * K_val[i]); + sumx_bubble = sumx_bubble + x_liq[i]; + } + + for (size_t i = 0; i < x_vap.size(); i++) + { + x_vap[i] = x_vap[i] / sumx_dew; + x_liq[i] = x_liq[i] / sumx_bubble; + } + + + } + return std::make_tuple(x_vap, x_liq, vapfrac); + } + template <typename TypeT, typename TypeP, typename TypeX> + inline auto PTX_startvals_pT(psrk& fldi, TypeT& T, TypeP p, TypeX& x_known, TypeT& vapfrac, int errval) { + + std::vector<double> K_val(x_known.size()); + for (size_t i = 0; i < x_known.size(); i++) { + K_val[i] = fldi.PC[i] / p * exp(5.3730 * (1.0 + fldi.w[i]) * (1.0 - fldi.TC[i] / T)); + } + + return rache_rich(fldi, K_val, x_known, vapfrac, errval); + + } + template <typename TypeT, typename TypeP> + inline auto PhaseDet(psrk& fldi, TypeT& T, TypeP& p, const std::vector<double>& x) { + + auto errval = 0; + auto vapfrac = 0.0; + std::vector<TypeT> x_vap, x_liq2, x_liq; + tie(x_vap, x_liq, vapfrac) = PTX_startvals_pT(fldi, T, p, x, vapfrac, errval); + return 0.0; + } + +} \ No newline at end of file diff --git a/include/teqp/models/mie/mie.hpp b/include/teqp/models/mie/mie.hpp index 5d7985b..c80a82b 100644 --- a/include/teqp/models/mie/mie.hpp +++ b/include/teqp/models/mie/mie.hpp @@ -2,71 +2,1109 @@ #include "teqp/models/multifluid.hpp" #include <Eigen/Dense> +#include <math.h> +#include "teqp/models/cubics.hpp" namespace teqp { -namespace Mie{ - - /** - Equation of state for the Mie ($\lambda_{\mathrm{r}}$,6) fluid with a repulsive exponent from 11 to 13 - Pohl, S.; Fingerhut, R., Thol, M.; Vrabec, J.; Span, R. - */ - class Mie6Pohl2023 { - - private: - using EArray6 = Eigen::Array<double, 6, 1>; - using EArray4 = Eigen::Array<double, 4, 1>; - - const EArray6 c1_pol = (EArray6() << -0.0192944,1.38,-2.2653,1.6291,-1.974,0.40412 ).finished(); - const EArray6 c1_exp = (EArray6() << 0.1845,-0.3227,1.1351,2.232,-2.344,-0.4238 ).finished(); - const EArray4 c1_gbs = (EArray4() << -4.367,0.0371,1.3895,2.835 ).finished(); - const EArray6 c2_pol = (EArray6() << 0.26021,-5.525,8.329,-19.492,25.8,-3.8133 ).finished(); - const EArray6 c2_exp = (EArray6() << -5.05,2.7842,-9.523,-30.383,17.902,2.2264 ).finished(); - const EArray4 c2_gbs = (EArray4() << 48.445,-5.506,-11.643,-24.36 ).finished(); - const EArray6 t_pol = (EArray6() << 1,0.236,0.872,0.313,0.407,0.703 ).finished(); - const EArray6 t_exp = (EArray6() << 1.78,2.99,2.866,1.2,3.06,1.073 ).finished(); - const EArray4 t_gbs = (EArray4() << 1.50,1.03,4.02,1.57 ).finished(); - const EArray6 d_pol = (EArray6() << 4,1,1,2,2,3 ).finished(); - const EArray6 d_exp = (EArray6() << 1,1,3,2,2,5 ).finished(); - const EArray4 d_gbs = (EArray4() << 2,3,2,2 ).finished(); - const EArray6 p = (EArray6() << 1,2,2,1,2,1 ).finished(); - const EArray4 eta = (EArray4() << 0.362,0.313,1.17,0.957 ).finished(); - const EArray4 beta = (EArray4() << 0.0761,0.143,0.63,1.32 ).finished(); - const EArray4 gam = (EArray4() << 1.55,-0.0826,1.505,1.07 ).finished(); - const EArray4 eps = (EArray4() << -1,-1,-0.195,-0.287 ).finished(); - - const double m_lambda_a; - const EArray6 n_pol, n_exp; - const EArray4 n_gbs; - const double Tc, rhoc; // In simulation units - public: - - Mie6Pohl2023(double lambda_a) : m_lambda_a(lambda_a), - n_pol(c1_pol + c2_pol / m_lambda_a), - n_exp(c1_exp + c2_exp / m_lambda_a), - n_gbs(c1_gbs + c2_gbs / m_lambda_a), - Tc(0.668 + 6.84 / m_lambda_a + 145 / pow(m_lambda_a, 3)), // T^* - rhoc(0.2516 + 0.049 * log10(m_lambda_a)) // rho^* - {} - - auto get_lambda_a() const { return m_lambda_a; } - - // We are in "simulation units", so R is 1.0, and T and rho that - // go into alphar are actually T^* and rho^* - template<typename MoleFracType> - double R(const MoleFracType &) const { return 1.0; } - - template<typename TTYPE, typename RHOTYPE, typename MoleFracType> - auto alphar(const TTYPE& Tstar, const RHOTYPE& rhostar, const MoleFracType& /*molefrac*/) const { - auto tau = Tc / Tstar; auto delta = rhostar / rhoc; - auto alphar_ = ( - (n_pol * pow(tau, t_pol) * pow(delta, d_pol)).sum() + - (n_exp * pow(tau, t_exp) * pow(delta, d_exp) * exp(-pow(delta, p))).sum() + - (n_gbs * pow(tau, t_gbs) * pow(delta, d_gbs) * exp(-eta * pow(delta - eps, 2) - beta * pow(tau - gam, 2))).sum() - ); - return forceeval(alphar_); + namespace Mie { + + /** + Equation of state for the Mie ($\lambda_{\mathrm{r}}$,6) fluid with a repulsive exponent from 11 to 13 + Pohl, S.; Fingerhut, R., Thol, M.; Vrabec, J.; Span, R. + */ + class Mie6Pohl2023 { + + private: + using EArray6 = Eigen::Array<double, 6, 1>; + using EArray4 = Eigen::Array<double, 4, 1>; + + const EArray6 c1_pol = (EArray6() << -0.0192944, 1.38, -2.2653, 1.6291, -1.974, 0.40412).finished(); + const EArray6 c1_exp = (EArray6() << 0.1845, -0.3227, 1.1351, 2.232, -2.344, -0.4238).finished(); + const EArray4 c1_gbs = (EArray4() << -4.367, 0.0371, 1.3895, 2.835).finished(); + const EArray6 c2_pol = (EArray6() << 0.26021, -5.525, 8.329, -19.492, 25.8, -3.8133).finished(); + const EArray6 c2_exp = (EArray6() << -5.05, 2.7842, -9.523, -30.383, 17.902, 2.2264).finished(); + const EArray4 c2_gbs = (EArray4() << 48.445, -5.506, -11.643, -24.36).finished(); + const EArray6 t_pol = (EArray6() << 1, 0.236, 0.872, 0.313, 0.407, 0.703).finished(); + const EArray6 t_exp = (EArray6() << 1.78, 2.99, 2.866, 1.2, 3.06, 1.073).finished(); + const EArray4 t_gbs = (EArray4() << 1.50, 1.03, 4.02, 1.57).finished(); + const EArray6 d_pol = (EArray6() << 4, 1, 1, 2, 2, 3).finished(); + const EArray6 d_exp = (EArray6() << 1, 1, 3, 2, 2, 5).finished(); + const EArray4 d_gbs = (EArray4() << 2, 3, 2, 2).finished(); + const EArray6 p = (EArray6() << 1, 2, 2, 1, 2, 1).finished(); + const EArray4 eta = (EArray4() << 0.362, 0.313, 1.17, 0.957).finished(); + const EArray4 beta = (EArray4() << 0.0761, 0.143, 0.63, 1.32).finished(); + const EArray4 gam = (EArray4() << 1.55, -0.0826, 1.505, 1.07).finished(); + const EArray4 eps = (EArray4() << -1, -1, -0.195, -0.287).finished(); + + const double m_lambda_a; + const EArray6 n_pol, n_exp; + const EArray4 n_gbs; + const double Tc, rhoc; // In simulation units + public: + + Mie6Pohl2023(double lambda_a) : m_lambda_a(lambda_a), + n_pol(c1_pol + c2_pol / m_lambda_a), + n_exp(c1_exp + c2_exp / m_lambda_a), + n_gbs(c1_gbs + c2_gbs / m_lambda_a), + Tc(0.668 + 6.84 / m_lambda_a + 145 / pow(m_lambda_a, 3)), // T^* + rhoc(0.2516 + 0.049 * log10(m_lambda_a)) // rho^* + {} + + auto get_lambda_a() const { return m_lambda_a; } + + // We are in "simulation units", so R is 1.0, and T and rho that + // go into alphar are actually T^* and rho^* + template<typename MoleFracType> + double R(const MoleFracType&) const { return 1.0; } + + template<typename TTYPE, typename RHOTYPE, typename MoleFracType> + auto alphar(const TTYPE& Tstar, const RHOTYPE& rhostar, const MoleFracType& /*molefrac*/) const { + auto tau = Tc / Tstar; auto delta = rhostar / rhoc; + auto alphar_ = ( + (n_pol * pow(tau, t_pol) * pow(delta, d_pol)).sum() + + (n_exp * pow(tau, t_exp) * pow(delta, d_exp) * exp(-pow(delta, p))).sum() + + (n_gbs * pow(tau, t_gbs) * pow(delta, d_gbs) * exp(-eta * pow(delta - eps, 2) - beta * pow(tau - gam, 2))).sum() + ); + return forceeval(alphar_); + } + + }; + + + // + inline auto linear_mixing(const double& x, const double& y) { + return (x + y) / 2.0; + } + + class AncTerm { + public: + Eigen::ArrayXd n, t; + double tred, dred; + std::string type; + }; + + class fluid { + public: + double lambdas, epsilons, sigmas, m, I; + AncTerm anc_dl, anc_dv; + bool is_sphere; + }; + + // Combining rules for one fluid approximation + // (1) Simple Van der Waals one fluid combininb rule + template<typename RHOTYPE, typename MoleFracType> + inline auto combining_rules_one_fluid(RHOTYPE& rhostar, MoleFracType& molefrac, std::vector<fluid> f, const bool& is_spherical) { + auto ncomp = f.size(); + using resulttype = std::common_type_t<decltype(molefrac[0]), decltype(rhostar)>; + using rhotype = std::common_type_t<decltype(rhostar)>; + std::vector<std::vector<resulttype>> sigma_ij(ncomp, std::vector<resulttype>(ncomp, 0.0)); + std::vector<std::vector<resulttype>> eps_ij(ncomp, std::vector<resulttype>(ncomp, 0.0)); + std::vector<std::vector<resulttype>> lambda_ij(ncomp, std::vector<resulttype>(ncomp, 0.0)); + std::vector<resulttype> m_mixed(ncomp, 0.0); + resulttype m_mix = 0.0; + resulttype sigma_mean = 0.0; + resulttype epsilon_mean = 0.0; + resulttype lambda_mean = 0.0; + + if (is_spherical) { + m_mix = 1.0; + } + else + { + for (auto i = 0; i < ncomp; i++) { + m_mix += f[i].m * molefrac[i]; + } + } + + resulttype kij = 0.00; + for (size_t i = 0; i < ncomp - 1; i++) + { + //if(molefrac[0]<1.0) { + kij = 0.0;//1.0 - pow(2.0, 7.0) * (sqrt(f[i].I * f[i + 1].I) / (f[i].I + f[i + 1].I)) * (pow(f[i].sigmas, 3.0) * pow(f[i + 1].sigmas, 3.0)) / pow(f[i].sigmas + f[i + 1].sigmas, 6); + /*} + else + { + kij = 0.0; + }*/ + + } + // + + // Switch between combing rules for interaction of molecular parameters + for (auto i = 0; i < ncomp; i++) { + for (auto j = 0; j < ncomp; j++) { + lambda_ij[i][j] = linear_mixing(f[i].lambdas, f[j].lambdas); //3.0 + sqrt((f[i].lambdas - 3.0) * (f[j].lambdas - 3.0)); + sigma_ij[i][j] = linear_mixing(f[i].sigmas, f[j].sigmas); + eps_ij[i][j] = (1.0 - kij) * sqrt(f[i].epsilons * f[j].epsilons);//*pow(sqrt(f[i].sigmas*f[j].sigmas)/ sigma_ij[i][j],6.0); //sqrt(pow(f[i].sigmas,3.0)*pow(f[j].sigmas,3.0))/pow(sigma_ij[i][j],3.0) + } + } + + // Calculate the mean values for the one fluid approximation + for (auto i = 0; i < ncomp; i++) { for (auto j = 0; j < ncomp; j++) { sigma_mean += molefrac[i] * molefrac[j] * f[i].m * f[j].m * pow(sigma_ij[i][j], 3.0); } } + for (auto i = 0; i < ncomp; i++) { for (auto j = 0; j < ncomp; j++) { epsilon_mean += molefrac[i] * molefrac[j] * f[i].m * f[j].m * pow(sigma_ij[i][j], 3.0) * eps_ij[i][j]; } } + for (auto i = 0; i < ncomp; i++) { for (auto j = 0; j < ncomp; j++) { lambda_mean += molefrac[i] * molefrac[j] * lambda_ij[i][j]; } } + + + epsilon_mean = epsilon_mean / sigma_mean; + sigma_mean = pow(sigma_mean / pow(m_mix, 2.0), 1.0 / 3.0); + return std::make_tuple(sigma_mean, epsilon_mean, lambda_mean, m_mix); } - }; -}; -}; + inline auto dlv_eq1(const double& T, const AncTerm& anc) { + auto tau = abs(1.0 - T / anc.tred); + auto sum = 0.0; + for (size_t i = 0; i < anc.n.size(); i++) { + sum = sum + anc.n(i) * pow(tau, anc.t(i)); + } + return anc.dred * (sum + 1.0); + } + + inline auto dlv_eq2(const double& T, const AncTerm& anc) { + auto tau = pow(abs(1.0 - T / anc.tred), 1.0 / 3.0); + auto sum = 0.0; + for (size_t i = 0; i < anc.n.size(); i++) { + sum = sum + anc.n(i) * pow(tau, anc.t(i)); + } + return anc.dred * (sum + 1.0); + } + + inline auto dlv_eq3(const double& T, const AncTerm& anc) { + auto tau = abs(1.0 - T / anc.tred); + auto sum = 0.0; + for (size_t i = 0; i < anc.n.size(); i++) { + sum = sum + anc.n(i) * pow(tau, anc.t(i)); + } + return anc.dred * exp(sum); + } + + inline auto dlv_eq4(const double& T, const AncTerm& anc) { + auto tau = pow(abs(1.0 - T / anc.tred), 1.0 / 3.0); + auto sum = 0.0; + for (size_t i = 0; i < anc.n.size(); i++) { + sum = sum + anc.n(i) * pow(tau, anc.t(i)); + } + return anc.dred * exp(sum); + } + + inline auto dlv_eq5(const double& T, const AncTerm& anc) { + auto tau = abs(1.0 - T / anc.tred); + auto sum = 0.0; + for (size_t i = 0; i < anc.n.size(); i++) { + sum = sum + anc.n(i) * pow(tau, anc.t(i)); + } + return anc.dred * exp(anc.tred / T * sum); + } + + inline auto dlv_eq6(const double& T, const AncTerm& anc) { + auto tau = pow(abs(1.0 - T / anc.tred), 1.0 / 3.0); + auto sum = 0.0; + for (size_t i = 0; i < anc.n.size(); i++) { + sum = sum + anc.n(i) * pow(tau, anc.t(i)); + } + return anc.dred * exp(anc.tred / T * sum); + } + + // Ancillaries for pure fluid components to generate starting of vle + std::tuple<double, double> get_sat_dense(double& T, fluid f) { + double dl = 0.0; + double dv = 0.0; + if (f.anc_dl.type == "DL1") { dl = dlv_eq1(T, f.anc_dl); } + else if (f.anc_dl.type == "DL2") { dl = dlv_eq2(T, f.anc_dl); } + else if (f.anc_dl.type == "DL3") { dl = dlv_eq3(T, f.anc_dl); } + else if (f.anc_dl.type == "DL4") { dl = dlv_eq4(T, f.anc_dl); } + else if (f.anc_dl.type == "DL5") { dl = dlv_eq5(T, f.anc_dl); } + else if (f.anc_dl.type == "DL6") { dl = dlv_eq6(T, f.anc_dl); } + + if (f.anc_dv.type == "DV1") { dv = dlv_eq1(T, f.anc_dv); } + else if (f.anc_dv.type == "DV2") { dv = dlv_eq2(T, f.anc_dv); } + else if (f.anc_dv.type == "DV3") { dv = dlv_eq3(T, f.anc_dv); } + else if (f.anc_dv.type == "DV4") { dv = dlv_eq4(T, f.anc_dv); } + else if (f.anc_dv.type == "DV5") { dv = dlv_eq5(T, f.anc_dv); } + else if (f.anc_dv.type == "DV6") { dv = dlv_eq6(T, f.anc_dv); } + + return { dv,dl }; + } + + template<typename Model> + auto get_vle_pure_start(const Model& model, double& T, const int idx) { + return get_sat_dense(T, model.fld[idx]); + } + + const double kBoltz = 1.380649E-23; + const double NAvo = 6.02214076E23; + + + class MieElong { + + private: + + const double kBoltz = 1.380649E-23; + const double NAvo = 6.02214076E23; + + using EArray6 = Eigen::Array<double, 6, 1>; + using EArray4 = Eigen::Array<double, 4, 1>; + + + std::valarray<double> c1_pol = { -0.0192944, 1.38, -2.2653, 1.6291, -1.974, 0.40412 }; + std::valarray<double> c1_exp = { 0.1845, -0.3227, 1.1351, 2.232, -2.344, -0.4238 }; + std::valarray<double> c1_gbs = { -4.367, 0.0371, 1.3895, 2.835 }; + std::valarray<double> c2_pol = { 0.26021, -5.525, 8.329, -19.492, 25.8, -3.8133 }; + std::valarray<double> c2_exp = { -5.05, 2.7842, -9.523, -30.383, 17.902, 2.2264 }; + std::valarray<double> c2_gbs = { 48.445, -5.506, -11.643, -24.36 }; + std::valarray<double> t_pol = { 1.0, 0.236, 0.872, 0.313, 0.407, 0.703 }; + std::valarray<double> t_exp = { 1.78, 2.99, 2.866, 1.2, 3.06, 1.073 }; + std::valarray<double> t_gbs = { 1.50, 1.03, 4.02, 1.57 }; + std::valarray<double> d_pol = { 4.0, 1.0, 1.0, 2.0, 2.0, 3.0 }; + std::valarray<double> d_exp = { 1.0, 1.0, 3.0, 2.0, 2.0, 5.0 }; + std::valarray<double> d_gbs = { 2.0, 3.0, 2.0, 2.0 }; + std::valarray<double> p = { 1.0, 2.0, 2.0, 1.0, 2.0, 1.0 }; + std::valarray<double> eta = { 0.362, 0.313, 1.17, 0.957 }; + std::valarray<double> beta = { 0.0761, 0.143, 0.63, 1.32 }; + std::valarray<double> gam = { 1.55, -0.0826, 1.505, 1.07 }; + std::valarray<double> eps = { -1.0, -1.0, -0.195, -0.287 }; + //std::valarray<double> d1_pol = { -0.0050505, 0.38601, -1.1222, -0.0039209, 0.050028, -0.0013054 }; + //std::valarray<double> d2_pol = { 0.016623, 0.53098, -1.0766, 0.26569, 0.013785, 0.0013796 }; + //std::valarray<double> d3_pol = { -0.00042696, 0.18677, 0.094444, 0.01354, 0.0051845, -0.0011878 }; + //std::valarray<double> d1_exp = { 0.12441, -0.094619, -0.16034, 0.074307, -0.011307, 0.0080606 }; + //std::valarray<double> d2_exp = { -0.24797, 0.42032, 0.46332, -0.051815, 0.039351, 0.0017919 }; + //std::valarray<double> d3_exp = { 0.17459, -0.53721, -0.12545, -1.1854, 0.98169, 0.058886 }; + //std::valarray<double> t_pol_m = { 1, 0.367124560272789, 1.2185476818727, 0.781480878568594, 1.64331019262472, 0.587674327418418 }; + //std::valarray<double> t_exp_m = { 4.47186031183114, 2.82497155574424, 3.77610727931397, 2.65845878597852, 3.80375972303267, 0.665886883638958 }; + //std::valarray<double> d_pol_m = { 4.0, 1.0, 1.0, 2.0, 2.0, 3.0 }; + //std::valarray<double> d_exp_m = { 1.0, 1.0, 3.0, 2.0, 2.0, 5.0 }; + //std::valarray<double> p_m = { 1.0 , 2.0 , 2.0 , 1.0 , 2.0 , 1.0 }; + //std::valarray<double> d1_pol = { -0.0050505, 0.38601, -1.1222, -0.0039209, 0.050028, -0.0013054 }; + //std::valarray<double> d2_pol = { 0.016623, 0.53098, -1.0766, 0.26569, 0.013785, 0.0013796 }; + //std::valarray<double> d3_pol = { -0.00042696, 0.18677, 0.094444, 0.01354, 0.0051845, -0.0011878 }; + //std::valarray<double> d1_exp = { 0.12441, -0.094619, -0.16034, 0.074307, -0.011307, 0.0080606 }; + //std::valarray<double> d2_exp = { -0.24797, 0.42032, 0.46332, -0.051815, 0.039351, 0.0017919 }; + //std::valarray<double> d3_exp = { 0.17459, -0.53721, -0.12545, -1.1854, 0.98169, 0.058886 }; + //std::valarray<double> t_pol_m = { 1, 0.367124560272789, 1.2185476818727, 0.781480878568594, 1.64331019262472, 0.587674327418418 }; + //std::valarray<double> t_exp_m = { 4.47186031183114, 2.82497155574424, 3.77610727931397, 2.65845878597852, 3.80375972303267, 0.665886883638958 }; + //std::valarray<double> d_pol_m = { 4.0, 1.0, 1.0, 2.0, 2.0, 3.0 }; + //std::valarray<double> d_exp_m = { 1.0, 1.0, 3.0, 2.0, 2.0, 5.0 }; + //std::valarray<double> p_m = { 1.0 , 2.0 , 2.0 , 1.0 , 2.0 , 1.0 }; + std::valarray<double> d1_pol = { -0.044326, -0.9164, 0.54604, 0.93411, -0.36957, -0.20822 }; + std::valarray<double> d2_pol = { 0.0010372, -0.48659, 0.36519, 2.2719, -2.1598, -0.071921 }; + std::valarray<double> d3_pol = { 0.045309, -0.080824, 0.0017117, 1.4648, -1.6084, 0.052748 }; + std::valarray<double> d1_exp = { 0.51394, 0.55711, 0.083787, -0.86286, 0.19885, 0.02952 }; + std::valarray<double> d2_exp = { 0.24318, 0.68819, 0.36777, -1.0891, 0.17631, -0.0021117 }; + std::valarray<double> d3_exp = { 0.056978, 0.60157, 0.38942, -0.70837, -0.043016, -0.027741 }; + std::valarray<double> d1_gbs = { 0.79761, -0.88304, 0.96654, -0.37683 }; + std::valarray<double> d2_gbs = { 0.60753, -0.9869, 1.5873, -0.50984 }; + std::valarray<double> d3_gbs = { 0.22228, -0.34791, 0.88312, -0.35055 }; + std::valarray<double> t_pol_m = { 1, 0.216271942617027, 0.520609388112777, 0.752133081448595, 0.775679663317215, 0.580017747329082 }; + std::valarray<double> t_exp_m = { 1.96734718455893, 2, 1.38627146150207, 1.4813773563886, 1.2103073201027, 1.01007005697967 }; + std::valarray<double> t_gbs_m = { 0.418410732352989, 0.691563451339712, 1.31680108926219, 1.99860010157709 }; + std::valarray<double> d_pol_m = { 4.0, 1.0, 1.0, 2.0, 2.0, 3.0 }; + std::valarray<double> d_exp_m = { 1.0, 3.0, 2.0, 2.0, 4.0, 7.0 }; + std::valarray<double> d_gbs_m = { 1,2,2,3 }; + std::valarray<double> p_m = { 2.0 , 2.0 , 1.0 , 2.0 , 1.0 , 1.0 }; + std::valarray<double> eta_m = { 0.240229835057927, 0.493986587611359, 1.38196451506317 , 1.18724195184123 }; + std::valarray<double> beta_m = { 0.814951788107642, 1.72306756221527 ,0.452352097018578 ,0.0656824491597725 }; + std::valarray<double> gam_m = { -0.403458611209436,0.0471338211178168 ,-0.321710057373199 , 0.494527960329717 }; + std::valarray<double> eps_m = { -0.482584264913269,-0.435554887414116 ,0.271841899896465 ,0.198724624217954 }; + + int nr_fld; + bool is_sphere; + + public: + + std::vector<fluid> fld; + std::string combining_rule; + + //MieElong(double lambda_a, double ms, double sigma, double eps) { // Repulsive exponent, segment number ms + MieElong(const std::string& path, const std::vector<std::string>& fluids, const std::string& combining_rule_in) { + std::string filepath = std::filesystem::is_regular_file(path) ? path : path; + nlohmann::json j = load_a_JSON_file(filepath); + + fld.resize(fluids.size()); + int i = 0; + for (auto f : fluids) + { + fld[i].lambdas = j.at("mie").at(f).at("lambda"); + fld[i].epsilons = j.at("mie").at(f).at("epsilon"); + fld[i].sigmas = j.at("mie").at(f).at("sigma"); + fld[i].m = j.at("mie").at(f).at("segment"); + fld[i].I = j.at("mie").at(f).at("I"); + + auto n_anc = static_cast<int>(j.at("mie").at(f).at("dl_coeff").size()); + fld[i].anc_dl.tred = j.at("mie").at(f).at("tred"); + fld[i].anc_dl.dred = j.at("mie").at(f).at("dred"); + fld[i].anc_dl.type = j.at("mie").at(f).at("dl_type"); + fld[i].anc_dl.n = toeig(j.at("mie").at(f).at("dl_coeff")).head(n_anc); + fld[i].anc_dl.t = toeig(j.at("mie").at(f).at("dl_texp")).head(n_anc); + + n_anc = static_cast<int>(j.at("mie").at(f).at("dv_coeff").size()); + fld[i].anc_dv.tred = j.at("mie").at(f).at("tred"); + fld[i].anc_dv.dred = j.at("mie").at(f).at("dred"); + fld[i].anc_dv.type = j.at("mie").at(f).at("dv_type"); + fld[i].anc_dv.n = toeig(j.at("mie").at(f).at("dv_coeff")).head(n_anc); + fld[i].anc_dv.t = toeig(j.at("mie").at(f).at("dv_texp")).head(n_anc); + + i++; + } + + nr_fld = fluids.size(); + is_sphere = true; + combining_rule = combining_rule_in; + // check if only sphericals are included + for (size_t i = 0; i < nr_fld; i++) + { + if (fld[i].m > 1.0) { + is_sphere = false; + } + } + } + + template<typename MoleFracType> + double R(const MoleFracType&) const { return NAvo * kBoltz; } + + template<typename TTYPE, typename MoleFracType, typename GammaType> + inline auto get_tred_mix(const TTYPE& tc, const MoleFracType& x, const GammaType& gama_t) const { + typename TTYPE::value_type tred_mix = 0.0; + for (size_t i = 0; i < x.size(); i++) { + tred_mix += x[i] * x[i] * tc[i]; + } + auto beta_t = 1.0; + for (size_t i = 0; i < x.size() - 1; i++) { + for (size_t j = i + 1; j < x.size(); j++) { + tred_mix += 2.0 * x[i] * x[j] * beta_t * gama_t[i][j] * (x[i] + x[j]) / (beta_t * beta_t * x[i] + x[j]) * sqrt(tc[i] * tc[j]); + } + } + return tred_mix; + } + + template<typename TTYPE, typename MoleFracType, typename GammaType> + inline auto get_vred_mix(const TTYPE& dc, const MoleFracType& x, const GammaType& gama_v) const { + typename TTYPE::value_type vred_mix1 = 0.0; + for (size_t i = 0; i < x.size(); i++) { + vred_mix1 += (x[i] * x[i]) / dc[i]; + } + auto beta_v = 1.0; + for (size_t i = 0; i < x.size() - 1; i++) { + for (size_t j = i + 1; j < x.size(); j++) { + vred_mix1 += 2.0 * x[i] * x[j] * beta_v * gama_v[i][j] * (x[i] + x[j]) / (beta_v * beta_v * x[i] + x[j]) * 0.125 * pow(1.0 / pow(dc[i], 1.0 / 3.0) + 1.0 / pow(dc[j], 1.0 / 3.0), 3.0); + } + } + + return 1.0 / vred_mix1; + } + + template<typename ETYPE, typename LTYPE, typename MTYPE> + inline auto get_tc(const ETYPE& e, const LTYPE& l, const MTYPE& m) const { + return e * (0.668 + 6.84 / l + 145.0 / pow(l, 3.0)) * + (1.0 + 0.5135 * (m - 1.0) * l - 0.0344 * (m - 1.0) * pow(l, 2.0) + 0.0037 * pow((m - 1.0), 3.0) * l) / + (1.0 + 0.3220 * (m - 1.0) * l - 0.0223 * (m - 1.0) * pow(l, 2.0)); + } + + template<typename STYPE, typename LTYPE, typename MTYPE> + inline auto get_dc(const STYPE& s, const LTYPE& l, const MTYPE& m) const { + return 1E3 * m * (0.2516 + 0.049 * log(l) / log(10.0)) * + (1.0 - 0.0681 * (m - 1.0) * l + 0.0029 * (m - 1.0) * pow(l, 2.0)) / + (1.0 + 0.0462 * (m - 1.0) * l - 0.0019 * (m - 1.0) * pow(l, 2.0)) * 1E27 / (NAvo * pow(s, 3.0)) / m; + } + + + // Approximate the mixture with the one fluid model + template<typename TTYPE, typename RHOTYPE, typename MoleFracType> + auto one_fluid(TTYPE& Tstar, RHOTYPE& rhostar, MoleFracType& molefrac) const { + + using resulttype = std::common_type_t<decltype(Tstar), decltype(molefrac[0]), decltype(rhostar)>; + resulttype l = 0.0; + resulttype s = 0.0; + resulttype e = 0.0; + resulttype m = 0.0; + + std::tie(s, e, l, m) = combining_rules_one_fluid(rhostar, molefrac, fld, is_sphere); + + resulttype tc = get_tc(e, l, m); + resulttype dc = get_dc(s, l, m); + + resulttype tau = tc / Tstar; + resulttype delta = rhostar / dc; + + // Calculate coefficients + std::vector< resulttype> n_pol; + std::vector< resulttype> n_exp; + std::vector< resulttype> n_gbs; + for (size_t i = 0; i < t_pol.size(); i++) { n_pol.push_back(c1_pol[i] + c2_pol[i] / l); } + for (size_t i = 0; i < t_exp.size(); i++) { n_exp.push_back(c1_exp[i] + c2_exp[i] / l); } + for (size_t i = 0; i < t_gbs.size(); i++) { n_gbs.push_back(c1_gbs[i] + c2_gbs[i] / l); } + + + std::vector< resulttype> n_pol_m; + std::vector< resulttype> n_exp_m; + std::vector< resulttype> n_gbs_m; + for (size_t i = 0; i < t_pol_m.size(); i++) { n_pol_m.push_back(d1_pol[i] + d2_pol[i] * (1.0 - m) + d3_pol[i] * (1.0 - m) * (1.0 - m)); } + for (size_t i = 0; i < t_exp_m.size(); i++) { n_exp_m.push_back(d1_exp[i] + d2_exp[i] * (1.0 - m) + d3_exp[i] * (1.0 - m) * (1.0 - m)); } + for (size_t i = 0; i < t_gbs_m.size(); i++) { n_gbs_m.push_back(d1_gbs[i] + d2_gbs[i] * (1.0 - m) + d3_gbs[i] * (1.0 - m) * (1.0 - m)); } + + std::vector< resulttype> pol, exp_, gbs; + for (size_t i = 0; i < t_pol.size(); i++) { pol.push_back(n_pol[i] * pow(tau, t_pol[i]) * pow(delta, d_pol[i])); } + for (size_t i = 0; i < t_exp.size(); i++) { exp_.push_back(n_exp[i] * pow(tau, t_exp[i]) * pow(delta, d_exp[i]) * exp(-pow(delta, p[i]))); } + for (size_t i = 0; i < t_gbs.size(); i++) { gbs.push_back(n_gbs[i] * pow(tau, t_gbs[i]) * pow(delta, d_gbs[i]) * exp(-eta[i] * (delta - eps[i]) * (delta - eps[i]) - beta[i] * (tau - gam[i]) * (tau - gam[i]))); } + + std::vector< resulttype> pol_m, exp_m, gbs_m; + for (size_t i = 0; i < t_pol_m.size(); i++) { pol_m.push_back(n_pol_m[i] * pow(tau, t_pol_m[i]) * pow(delta, d_pol_m[i])); } + for (size_t i = 0; i < t_exp_m.size(); i++) { exp_m.push_back(n_exp_m[i] * pow(tau, t_exp_m[i]) * pow(delta, d_exp_m[i]) * exp(-pow(delta, p_m[i]))); } + for (size_t i = 0; i < t_gbs_m.size(); i++) { gbs_m.push_back(n_gbs_m[i] * pow(tau, t_gbs_m[i]) * pow(delta, d_gbs_m[i]) * exp(-eta_m[i] * (delta - eps_m[i]) * (delta - eps_m[i]) - beta_m[i] * (tau - gam_m[i]) * (tau - gam_m[i]))); } + + auto alpha_r_sphere = std::reduce(pol.begin(), pol.end()) + std::reduce(exp_.begin(), exp_.end()) + std::reduce(gbs.begin(), gbs.end()); + auto alpha_r_el = std::reduce(pol_m.begin(), pol_m.end()) + std::reduce(exp_m.begin(), exp_m.end()) + std::reduce(gbs_m.begin(), gbs_m.end()); + resulttype alpha_r_all = 0.0; + if (is_sphere) { + alpha_r_all = alpha_r_sphere; + } + else + { + //alpha_r_all = m * alpha_r_sphere + (1.0 - m) * alpha_r_el; + alpha_r_all = alpha_r_sphere + (1.0 - m) * alpha_r_el; + } + + return alpha_r_all; + } + + template<typename TTYPE, typename RHOTYPE, typename MoleFracType> + auto corresponding_states(const TTYPE& Tstar, const RHOTYPE& rhostar, const MoleFracType& molefrac, const std::string combination_rule) const { + + using resulttype = std::common_type_t<decltype(Tstar), decltype(molefrac[0]), decltype(rhostar)>; + + auto ncomp = nr_fld; + // get pure fluid reducing + std::vector<resulttype> tc(nr_fld, 0.0); + std::vector<resulttype> dc(nr_fld, 0.0); + resulttype TC_MIX = 0.0; + resulttype DC_MIX = 0.0; + + for (size_t i = 0; i < nr_fld; i++) { + tc[i] = get_tc(fld[i].epsilons, fld[i].lambdas, fld[i].m); + dc[i] = get_dc(fld[i].sigmas, fld[i].lambdas, fld[i].m); + } + + std::vector<std::vector<resulttype>> gamma_t(ncomp, std::vector<resulttype>(ncomp, 1.0)); + std::vector<std::vector<resulttype>> gamma_v(ncomp, std::vector<resulttype>(ncomp, 1.0)); + if (combining_rule.compare("Lorentz-Berthelot") == 0) { + TC_MIX = get_tred_mix(tc, molefrac, gamma_t); + DC_MIX = get_vred_mix(dc, molefrac, gamma_v); + } + else if (combining_rule.compare("Linear") == 0) { + for (auto i = 0; i < ncomp; i++) { + for (auto j = 0; j < ncomp; j++) { + auto dom = pow(1.0 / pow(dc[i], 1.0 / 3.0) + 1.0 / pow(dc[j], 1.0 / 3.0), 3.0); + auto nom = 1.0 / dc[i] + 1.0 / dc[j]; + gamma_v[i][j] = 4.0 * nom / dom; + gamma_t[i][j] = 0.5 * (tc[i] + tc[j]) / sqrt(tc[i] * tc[j]); + }; + }; + TC_MIX = get_tred_mix(tc, molefrac, gamma_t); + DC_MIX = get_vred_mix(dc, molefrac, gamma_v); + } + + std::vector<std::vector<resulttype>> n_pol(ncomp, std::vector<resulttype>(t_pol.size(), 0.0)); + std::vector<std::vector<resulttype>> n_exp(ncomp, std::vector<resulttype>(t_exp.size(), 0.0)); + std::vector<std::vector<resulttype>> n_gbs(ncomp, std::vector<resulttype>(t_gbs.size(), 0.0)); + + // Calculate coefficients + for (size_t i = 0; i < ncomp; i++) { + for (size_t j = 0; j < t_pol.size(); j++) { + n_pol[i][j] = c1_pol[j] + c2_pol[j] / fld[i].lambdas; + } + }; + for (size_t i = 0; i < ncomp; i++) { + for (size_t j = 0; j < t_exp.size(); j++) { + n_exp[i][j] = c1_exp[j] + c2_exp[j] / fld[i].lambdas; + } + }; + for (size_t i = 0; i < ncomp; i++) { + for (size_t j = 0; j < t_gbs.size(); j++) { + n_gbs[i][j] = c1_gbs[j] + c2_gbs[j] / fld[i].lambdas; + }; + }; + + std::vector<std::vector<resulttype>> n_pol_m(ncomp, std::vector<resulttype>(t_pol_m.size(), 0.0)); + std::vector<std::vector<resulttype>> n_exp_m(ncomp, std::vector<resulttype>(t_exp_m.size(), 0.0)); + + for (size_t i = 0; i < ncomp; i++) { + for (size_t j = 0; j < t_pol_m.size(); j++) { + n_pol_m[i][j] = d1_pol[j] + d2_pol[j] * (1.0 - fld[i].m) * (1.0 - fld[i].m) + d3_pol[j] * (1.0 - fld[i].m) * (1.0 - fld[i].m) * (1.0 - fld[i].m); + }; + }; + + for (size_t i = 0; i < ncomp; i++) { + for (size_t j = 0; j < t_exp_m.size(); j++) { + n_exp_m[i][j] = d1_exp[j] + d2_exp[j] * (1.0 - fld[i].m) * (1.0 - fld[i].m) + d3_exp[j] * (1.0 - fld[i].m) * (1.0 - fld[i].m) * (1.0 - fld[i].m); + }; + }; + + std::vector<resulttype> tau; + std::vector<resulttype> delta; + + for (size_t i = 0; i < ncomp; i++) { + tau.push_back(TC_MIX / Tstar); + delta.push_back(rhostar / DC_MIX); + }; + + std::vector<resulttype> pol(ncomp, 0.0); + std::vector<resulttype> exp_(ncomp, 0.0); + std::vector<resulttype> gbs(ncomp, 0.0); + std::vector<resulttype> pol_m(ncomp, 0.0); + std::vector<resulttype> exp_m(ncomp, 0.0); + + // pure fluid contribution + for (size_t i = 0; i < ncomp; i++) { + pol[i] = 0.0; + for (size_t j = 0; j < t_pol.size(); j++) { + pol[i] += n_pol[i][j] * pow(tau[i], t_pol[j]) * pow(delta[i], d_pol[j]); + }; + }; + + for (size_t i = 0; i < ncomp; i++) { + exp_[i] = 0.0; + for (size_t j = 0; j < t_exp.size(); j++) { + exp_[i] += n_exp[i][j] * pow(tau[i], t_exp[j]) * pow(delta[i], d_exp[j]) * exp(-pow(delta[i], p[j])); + }; + }; + + for (size_t i = 0; i < ncomp; i++) { + gbs[i] = 0.0; + for (size_t j = 0; j < t_gbs.size(); j++) { + gbs[i] += n_gbs[i][j] * pow(tau[i], t_gbs[j]) * pow(delta[i], d_gbs[j]) * exp(-eta[j] * (delta[i] - eps[j]) * (delta[i] - eps[j]) - beta[j] * (tau[i] - gam[j]) * (tau[i] - gam[j])); + }; + }; + + if (!(is_sphere)) { + for (size_t i = 0; i < ncomp; i++) { + pol_m[i] = 0.0; + for (size_t j = 0; j < t_pol_m.size(); j++) { + pol_m[i] += n_pol_m[i][j] * pow(tau[i], t_pol_m[j]) * pow(delta[i], d_pol_m[j]); + }; + }; + + for (size_t i = 0; i < ncomp; i++) { + exp_m[i] = 0.0; + for (size_t j = 0; j < t_exp_m.size(); j++) { + exp_m[i] += n_exp_m[i][j] * pow(tau[i], t_exp_m[j]) * pow(delta[i], d_exp_m[j]) * exp(-pow(delta[i], p_m[j])); + }; + }; + } + + // Sum up everything + resulttype alpha_r = 0.0; + resulttype alpha_r_sphere = 0.0; + resulttype alpha_r_elongated = 0.0; + + for (size_t i = 0; i < ncomp; i++) { + if (fld[i].is_sphere) { + // Single sphere part of component i + alpha_r_sphere = pol[i] + exp_[i] + gbs[i]; + alpha_r += molefrac[i] * (fld[i].m * alpha_r_sphere); + } + else { + // Single elongation part of component i + alpha_r_sphere = pol[i] + exp_[i] + gbs[i]; + alpha_r_elongated = pol_m[i] + exp_m[i]; + alpha_r += molefrac[i] * (fld[i].m * alpha_r_sphere + (1.0 - fld[i].m) * alpha_r_elongated); + } + }; + + return alpha_r; + } + + + + // Input is temperature in K, density in mol/m^3 and molefractions + template<typename TTYPE, typename RHOTYPE, typename MoleFracType> + auto alphar(const TTYPE& Tstar, const RHOTYPE& rhostar, const MoleFracType& molefrac) const { + using resulttype = std::common_type_t<decltype(Tstar), decltype(molefrac[0]), decltype(rhostar)>; + + resulttype alpha_r_all = 0.0; + + if (combining_rule.compare("One-Fluid") == 0) { + alpha_r_all = one_fluid(Tstar, rhostar, molefrac); + } + else + { + alpha_r_all = corresponding_states(Tstar, rhostar, molefrac, combining_rule); + } + + return forceeval(alpha_r_all); + } + + }; + + + } +} + + +// class MieElongMix2 { + +// private: + +// using EArray6 = Eigen::Array<double, 6, 1>; +// using EArray4 = Eigen::Array<double, 4, 1>; + + +// const EArray6 c1_pol = (EArray6() << -0.0192944, 1.38, -2.2653, 1.6291, -1.974, 0.40412).finished(); +// const EArray6 c1_exp = (EArray6() << 0.1845, -0.3227, 1.1351, 2.232, -2.344, -0.4238).finished(); +// const EArray4 c1_gbs = (EArray4() << -4.367, 0.0371, 1.3895, 2.835).finished(); +// const EArray6 c2_pol = (EArray6() << 0.26021, -5.525, 8.329, -19.492, 25.8, -3.8133).finished(); +// const EArray6 c2_exp = (EArray6() << -5.05, 2.7842, -9.523, -30.383, 17.902, 2.2264).finished(); +// const EArray4 c2_gbs = (EArray4() << 48.445, -5.506, -11.643, -24.36).finished(); +// const EArray6 t_pol = (EArray6() << 1, 0.236, 0.872, 0.313, 0.407, 0.703).finished(); +// const EArray6 t_exp = (EArray6() << 1.78, 2.99, 2.866, 1.2, 3.06, 1.073).finished(); +// const EArray4 t_gbs = (EArray4() << 1.50, 1.03, 4.02, 1.57).finished(); +// const EArray6 d_pol = (EArray6() << 4, 1, 1, 2, 2, 3).finished(); +// const EArray6 d_exp = (EArray6() << 1, 1, 3, 2, 2, 5).finished(); +// const EArray4 d_gbs = (EArray4() << 2, 3, 2, 2).finished(); +// const EArray6 p = (EArray6() << 1, 2, 2, 1, 2, 1).finished(); +// const EArray4 eta = (EArray4() << 0.362, 0.313, 1.17, 0.957).finished(); +// const EArray4 beta = (EArray4() << 0.0761, 0.143, 0.63, 1.32).finished(); +// const EArray4 gam = (EArray4() << 1.55, -0.0826, 1.505, 1.07).finished(); +// const EArray4 eps = (EArray4() << -1, -1, -0.195, -0.287).finished(); + +// // elongation part +// const EArray6 d1_pol = (EArray6() << -0.0033717, -0.93963, 1.0403, -0.11078, -0.0025363, 0.0029514).finished(); +// const EArray6 d2_pol = (EArray6() << 0.0056524, -1.0549, 1.2465, -0.22323, 0.15836, 0.11005).finished(); +// const EArray6 d3_pol = (EArray6() << -0.0032422, -0.69384, 0.80325, 0.26957, -0.069081, -0.0026843).finished(); +// const EArray6 d1_exp = (EArray6() << -1.0436, 0.25891, -0.040776, -0.89272, -0.12408, -0.053926).finished(); +// const EArray6 d2_exp = (EArray6() << -0.27931, -0.27319, 0.1812, -1.2567, 0.71416, -0.040811).finished(); +// const EArray6 d3_exp = (EArray6() << -0.26861, -0.032778, 0.17816, -1.3529, 0.58892, 0.0034168).finished(); + +// const EArray6 t_pol_m = (EArray6() << 1, 0.684125778408247, 0.431293949327048, 0.686848959577935, 0.39116753396418, 0.755900521521362).finished(); +// const EArray6 t_exp_m = (EArray6() << 0.994097378429697, 0.81534766545966, 1.96973994708541, 1.35262796041634, 3, 1.41415130674305).finished(); +// const EArray6 d_pol_m = (EArray6() << 4, 1, 1, 2, 2, 3).finished(); +// const EArray6 d_exp_m = (EArray6() << 1, 1, 3, 2, 2, 5).finished(); +// const EArray6 p_m = (EArray6() << 1, 2, 2, 1, 2, 1).finished(); + +// double m_lambda_a, ms; +// std::vector<double> lambdas, epsilons, sigmas, ms_s; +// public: + +// //MieElong(double lambda_a, double ms, double sigma, double eps) { // Repulsive exponent, segment number ms +// MieElongMix2(const std::string& path, const std::vector<std::string>& fluids) { +// std::string filepath = std::filesystem::is_regular_file(path) ? path : path; +// nlohmann::json j = load_a_JSON_file(filepath); + +// for (auto f : fluids) +// { +// lambdas.push_back(j.at("mie").at(f).at("lambda")); +// epsilons.push_back(j.at("mie").at(f).at("epsilon")); +// sigmas.push_back(j.at("mie").at(f).at("sigma")); +// ms_s.push_back(j.at("mie").at(f).at("segment")); +// }; + +// }; + + +// // We are in "simulation units", so R is 1.0, and T and rho that +// // go into alphar are actually T^* and rho^* +// template<typename MoleFracType> +// double R(const MoleFracType&) const { return NAvo * kBoltz; } + +// // Input is temperature in K, density in mol/m^3 and molefractions +// template<typename TTYPE, typename RHOTYPE, typename MoleFracType> +// auto alphar(const TTYPE& Tstar, const RHOTYPE& rhostar, const MoleFracType& molefrac) const { + +// auto ncomp = ms_s.size(); +// using resulttype = std::common_type_t<decltype(Tstar), decltype(molefrac[0]), decltype(rhostar)>; + + +// std::vector<vector<resulttype>> sigma_ij(ncomp, vector<resulttype>(ncomp, rhostar)); +// std::vector<vector<resulttype>> epsilon_ij(ncomp, vector<resulttype>(ncomp, rhostar)); +// std::vector<vector<resulttype>> lambda_ij(ncomp, vector<resulttype>(ncomp, rhostar)); +// std::vector<vector<resulttype>> gamma_t(ncomp, vector<resulttype>(ncomp, rhostar)); +// std::vector<vector<resulttype>> gamma_v(ncomp, vector<resulttype>(ncomp, rhostar)); + + +// for (auto i = 0; i < ncomp; i++) { +// for (auto j = 0; j < ncomp; j++) { +// lambda_ij[i][j] = (lambdas[i] + lambdas[j]) / 2.0; +// sigma_ij[i][j] = (sigmas[i] + sigmas[j]) / 2.0; +// epsilon_ij[i][j] = sqrt(epsilons[i] * epsilons[j]); +// auto dom = pow(1.0 / pow(sigmas[i], 1.0 / 3.0) + 1.0 / pow(sigmas[j], 1.0 / 3.0), 3.0); +// auto nom = 1.0 / sigmas[i] + 1.0 / sigmas[j]; +// gamma_v[i][j] = 1.0;//4.0 * nom / dom; +// gamma_t[i][j] = 1.0;//0.5 * (epsilons[i] + epsilons[j]) / sqrt(epsilons[i] * epsilons[j]); +// }; +// }; + +// std::vector<resulttype> TC; +// std::vector<resulttype> DC; +// for (size_t i = 0; i < lambdas.size(); i++) { +// TC.push_back(get_tc(epsilons[i], lambdas[i], ms_s[i])); +// DC.push_back(get_dc(sigmas[i], lambdas[i], ms_s[i])); +// }; + + +// std::vector<vector<resulttype>> n_pol(ncomp, vector<resulttype>(t_pol.size(), 0.0)); +// std::vector<vector<resulttype>> n_exp(ncomp, vector<resulttype>(t_exp.size(), 0.0)); +// std::vector<vector<resulttype>> n_gbs(ncomp, vector<resulttype>(t_gbs.size(), 0.0)); +// // Calculate coefficients +// for (size_t i = 0; i < ncomp; i++) { +// for (size_t j = 0; j < t_pol.size(); j++) { +// n_pol[i][j] = c1_pol[j] + c2_pol[j] / lambdas[i]; +// } +// }; +// for (size_t i = 0; i < ncomp; i++) { +// for (size_t j = 0; j < t_exp.size(); j++) { +// n_exp[i][j] = c1_exp[j] + c2_exp[j] / lambdas[i]; +// } +// }; +// for (size_t i = 0; i < ncomp; i++) { +// for (size_t j = 0; j < t_gbs.size(); j++) { +// n_gbs[i][j] = c1_gbs[j] + c2_gbs[j] / lambdas[i]; +// }; +// }; + +// std::vector<vector<resulttype>> n_pol_m(ncomp, vector<resulttype>(t_pol_m.size(), 0.0)); +// std::vector<vector<resulttype>> n_exp_m(ncomp, vector<resulttype>(t_exp_m.size(), 0.0)); + +// for (size_t i = 0; i < ncomp; i++) { +// for (size_t j = 0; j < t_pol_m.size(); j++) { +// n_pol_m[i][j] = d1_pol[j] + d2_pol[j] * (1.0 - ms_s[i]) * (1.0 - ms_s[i]) + d3_pol[j] * (1.0 - ms_s[i]) * (1.0 - ms_s[i]) * (1.0 - ms_s[i]); +// }; +// }; + +// for (size_t i = 0; i < ncomp; i++) { +// for (size_t j = 0; j < t_exp_m.size(); j++) { +// n_exp_m[i][j] = d1_exp[j] + d2_exp[j] * (1.0 - ms_s[i]) * (1.0 - ms_s[i]) + d3_exp[j] * (1.0 - ms_s[i]) * (1.0 - ms_s[i]) * (1.0 - ms_s[i]); +// }; +// }; + +// std::vector<resulttype> tau; +// std::vector<resulttype> delta; +// for (size_t i = 0; i < ncomp; i++) { +// tau.push_back(get_tred_mix(TC, molefrac, gamma_t) / Tstar); +// delta.push_back(rhostar / get_vred_mix(DC, molefrac, gamma_v)); +// }; + +// std::vector<resulttype> pol(ncomp, 0.0); +// std::vector<resulttype> exp_(ncomp, 0.0); +// std::vector<resulttype> gbs(ncomp, 0.0); +// std::vector<resulttype> pol_m(ncomp, 0.0); +// std::vector<resulttype> exp_m(ncomp, 0.0); +// for (size_t i = 0; i < ncomp; i++) { +// pol[i] = 0.0; +// for (size_t j = 0; j < t_pol.size(); j++) { +// pol[i] += n_pol[i][j] * pow(tau[i], t_pol[j]) * pow(delta[i], d_pol[j]); +// }; +// }; +// for (size_t i = 0; i < ncomp; i++) { +// exp_[i] = 0.0; +// for (size_t j = 0; j < t_exp.size(); j++) { +// exp_[i] += n_exp[i][j] * pow(tau[i], t_exp[j]) * pow(delta[i], d_exp[j]) * exp(-pow(delta[i], p[j])); +// }; +// }; +// for (size_t i = 0; i < ncomp; i++) { +// gbs[i] = 0.0; +// for (size_t j = 0; j < t_gbs.size(); j++) { +// gbs[i] += n_gbs[i][j] * pow(tau[i], t_gbs[j]) * pow(delta[i], d_gbs[j]) * exp(-eta[j] * (delta[i] - eps[j]) * (delta[i] - eps[j]) - beta[j] * (tau[i] - gam[j]) * (tau[i] - gam[j])); +// }; +// }; + +// for (size_t i = 0; i < ncomp; i++) { +// pol_m[i] = 0.0; +// for (size_t j = 0; j < t_pol_m.size(); j++) { +// pol_m[i] += n_pol_m[i][j] * pow(tau[i], t_pol_m[j]) * pow(delta[i], d_pol_m[j]); +// }; +// }; +// for (size_t i = 0; i < ncomp; i++) { +// exp_m[i] = 0.0; +// for (size_t j = 0; j < t_exp_m.size(); j++) { +// exp_m[i] += n_exp_m[i][j] * pow(tau[i], t_exp_m[j]) * pow(delta[i], d_exp_m[j]) * exp(-pow(delta[i], p_m[j])); +// }; +// }; + + +// resulttype alpha_r_sphere = 0.0; +// resulttype alpha_r_el = 0.0; +// resulttype alpha_r_all = 0.0; +// for (size_t i = 0; i < ncomp; i++) { +// alpha_r_sphere = 0.0; +// alpha_r_el = 0.0; +// alpha_r_sphere = pol[i] + exp_[i] + gbs[i]; +// alpha_r_el = pol_m[i] + exp_m[i]; +// alpha_r_all += molefrac[i] * (ms_s[i] * alpha_r_sphere + (1.0 - ms_s[i]) * alpha_r_el); +// }; +// return forceeval(alpha_r_all); +// } + +// }; + +//}; + + +//// new mixing rules try +//class MieElongMix3 { + +//private: + +// using EArray6 = Eigen::Array<double, 6, 1>; +// using EArray4 = Eigen::Array<double, 4, 1>; + + +// const EArray6 c1_pol = (EArray6() << -0.0192944, 1.38, -2.2653, 1.6291, -1.974, 0.40412).finished(); +// const EArray6 c1_exp = (EArray6() << 0.1845, -0.3227, 1.1351, 2.232, -2.344, -0.4238).finished(); +// const EArray4 c1_gbs = (EArray4() << -4.367, 0.0371, 1.3895, 2.835).finished(); +// const EArray6 c2_pol = (EArray6() << 0.26021, -5.525, 8.329, -19.492, 25.8, -3.8133).finished(); +// const EArray6 c2_exp = (EArray6() << -5.05, 2.7842, -9.523, -30.383, 17.902, 2.2264).finished(); +// const EArray4 c2_gbs = (EArray4() << 48.445, -5.506, -11.643, -24.36).finished(); +// const EArray6 t_pol = (EArray6() << 1, 0.236, 0.872, 0.313, 0.407, 0.703).finished(); +// const EArray6 t_exp = (EArray6() << 1.78, 2.99, 2.866, 1.2, 3.06, 1.073).finished(); +// const EArray4 t_gbs = (EArray4() << 1.50, 1.03, 4.02, 1.57).finished(); +// const EArray6 d_pol = (EArray6() << 4, 1, 1, 2, 2, 3).finished(); +// const EArray6 d_exp = (EArray6() << 1, 1, 3, 2, 2, 5).finished(); +// const EArray4 d_gbs = (EArray4() << 2, 3, 2, 2).finished(); +// const EArray6 p = (EArray6() << 1, 2, 2, 1, 2, 1).finished(); +// const EArray4 eta = (EArray4() << 0.362, 0.313, 1.17, 0.957).finished(); +// const EArray4 beta = (EArray4() << 0.0761, 0.143, 0.63, 1.32).finished(); +// const EArray4 gam = (EArray4() << 1.55, -0.0826, 1.505, 1.07).finished(); +// const EArray4 eps = (EArray4() << -1, -1, -0.195, -0.287).finished(); + +// // elongation part +// const EArray6 d1_pol = (EArray6() << -0.0033717, -0.93963, 1.0403, -0.11078, -0.0025363, 0.0029514).finished(); +// const EArray6 d2_pol = (EArray6() << 0.0056524, -1.0549, 1.2465, -0.22323, 0.15836, 0.11005).finished(); +// const EArray6 d3_pol = (EArray6() << -0.0032422, -0.69384, 0.80325, 0.26957, -0.069081, -0.0026843).finished(); +// const EArray6 d1_exp = (EArray6() << -1.0436, 0.25891, -0.040776, -0.89272, -0.12408, -0.053926).finished(); +// const EArray6 d2_exp = (EArray6() << -0.27931, -0.27319, 0.1812, -1.2567, 0.71416, -0.040811).finished(); +// const EArray6 d3_exp = (EArray6() << -0.26861, -0.032778, 0.17816, -1.3529, 0.58892, 0.0034168).finished(); + +// const EArray6 t_pol_m = (EArray6() << 1, 0.684125778408247, 0.431293949327048, 0.686848959577935, 0.39116753396418, 0.755900521521362).finished(); +// const EArray6 t_exp_m = (EArray6() << 0.994097378429697, 0.81534766545966, 1.96973994708541, 1.35262796041634, 3, 1.41415130674305).finished(); +// const EArray6 d_pol_m = (EArray6() << 4, 1, 1, 2, 2, 3).finished(); +// const EArray6 d_exp_m = (EArray6() << 1, 1, 3, 2, 2, 5).finished(); +// const EArray6 p_m = (EArray6() << 1, 2, 2, 1, 2, 1).finished(); + +// double m_lambda_a, ms; +// std::vector<double> lambdas, epsilons, sigmas, ms_s; +//public: + +// //MieElong(double lambda_a, double ms, double sigma, double eps) { // Repulsive exponent, segment number ms +// MieElongMix3(const std::string& path, const std::vector<std::string>& fluids) { +// std::string filepath = std::filesystem::is_regular_file(path) ? path : path; +// nlohmann::json j = load_a_JSON_file(filepath); + +// for (auto f : fluids) +// { +// lambdas.push_back(j.at("mie").at(f).at("lambda")); +// epsilons.push_back(j.at("mie").at(f).at("epsilon")); +// sigmas.push_back(j.at("mie").at(f).at("sigma")); +// ms_s.push_back(j.at("mie").at(f).at("segment")); +// }; + +// }; + + +// // We are in "simulation units", so R is 1.0, and T and rho that +// // go into alphar are actually T^* and rho^* +// template<typename MoleFracType> +// double R(const MoleFracType&) const { return NAvo * kBoltz; } + +// // Input is temperature in K, density in mol/m^3 and molefractions +// template<typename TTYPE, typename RHOTYPE, typename MoleFracType> +// auto alphar(const TTYPE& Tstar, const RHOTYPE& rhostar, const MoleFracType& molefrac) const { + +// auto ncomp = ms_s.size(); +// using resulttype = std::common_type_t<decltype(Tstar), decltype(molefrac[0]), decltype(rhostar)>; + + +// std::vector<vector<resulttype>> sigma_ij(ncomp, vector<resulttype>(ncomp, rhostar)); +// std::vector<vector<resulttype>> epsilon_ij(ncomp, vector<resulttype>(ncomp, rhostar)); +// std::vector<vector<resulttype>> lambda_ij(ncomp, vector<resulttype>(ncomp, rhostar)); +// std::vector<vector<resulttype>> gamma_t(ncomp, vector<resulttype>(ncomp, rhostar)); +// std::vector<vector<resulttype>> gamma_v(ncomp, vector<resulttype>(ncomp, rhostar)); + + +// for (auto i = 0; i < ncomp; i++) { +// for (auto j = 0; j < ncomp; j++) { +// lambda_ij[i][j] = (lambdas[i] + lambdas[j]) / 2.0; +// sigma_ij[i][j] = (sigmas[i] + sigmas[j]) / 2.0; +// epsilon_ij[i][j] = sqrt(epsilons[i] * epsilons[j]); +// auto dom = pow(1.0 / pow(sigmas[i], 1.0 / 3.0) + 1.0 / pow(sigmas[j], 1.0 / 3.0), 3.0); +// auto nom = 1.0 / sigmas[i] + 1.0 / sigmas[j]; +// gamma_v[i][j] = 1.0;//4.0 * nom / dom; +// gamma_t[i][j] = 1.0;//0.5 * (epsilons[i] + epsilons[j]) / sqrt(epsilons[i] * epsilons[j]); +// }; +// }; + +// std::vector<resulttype> TC; +// std::vector<resulttype> DC; +// for (size_t i = 0; i < lambdas.size(); i++) { +// TC.push_back(get_tc(epsilons[i], lambdas[i], ms_s[i])); +// DC.push_back(get_dc(sigmas[i], lambdas[i], ms_s[i])); +// }; + + +// std::vector<vector<resulttype>> n_pol(ncomp, vector<resulttype>(t_pol.size(), 0.0)); +// std::vector<vector<resulttype>> n_exp(ncomp, vector<resulttype>(t_exp.size(), 0.0)); +// std::vector<vector<resulttype>> n_gbs(ncomp, vector<resulttype>(t_gbs.size(), 0.0)); +// // Calculate coefficients +// for (size_t i = 0; i < ncomp; i++) { +// for (size_t j = 0; j < t_pol.size(); j++) { +// n_pol[i][j] = c1_pol[j] + c2_pol[j] / lambdas[i]; +// } +// }; +// for (size_t i = 0; i < ncomp; i++) { +// for (size_t j = 0; j < t_exp.size(); j++) { +// n_exp[i][j] = c1_exp[j] + c2_exp[j] / lambdas[i]; +// } +// }; +// for (size_t i = 0; i < ncomp; i++) { +// for (size_t j = 0; j < t_gbs.size(); j++) { +// n_gbs[i][j] = c1_gbs[j] + c2_gbs[j] / lambdas[i]; +// }; +// }; + +// std::vector<vector<resulttype>> n_pol_m(ncomp, vector<resulttype>(t_pol_m.size(), 0.0)); +// std::vector<vector<resulttype>> n_exp_m(ncomp, vector<resulttype>(t_exp_m.size(), 0.0)); + +// for (size_t i = 0; i < ncomp; i++) { +// for (size_t j = 0; j < t_pol_m.size(); j++) { +// n_pol_m[i][j] = d1_pol[j] + d2_pol[j] * (1.0 - ms_s[i]) * (1.0 - ms_s[i]) + d3_pol[j] * (1.0 - ms_s[i]) * (1.0 - ms_s[i]) * (1.0 - ms_s[i]); +// }; +// }; + +// for (size_t i = 0; i < ncomp; i++) { +// for (size_t j = 0; j < t_exp_m.size(); j++) { +// n_exp_m[i][j] = d1_exp[j] + d2_exp[j] * (1.0 - ms_s[i]) * (1.0 - ms_s[i]) + d3_exp[j] * (1.0 - ms_s[i]) * (1.0 - ms_s[i]) * (1.0 - ms_s[i]); +// }; +// }; + +// std::vector<resulttype> tau; +// std::vector<resulttype> delta; +// /*for (size_t i = 0; i < ncomp; i++) { +// tau.push_back(TC[i] / Tstar); +// delta.push_back(rhostar / DC[i]); +// };*/ +// for (size_t i = 0; i < ncomp; i++) { +// tau.push_back(TC[i] / Tstar); +// delta.push_back(rhostar / DC[i]); +// }; + +// std::vector<resulttype> pol(ncomp, 0.0); +// std::vector<resulttype> exp_(ncomp, 0.0); +// std::vector<resulttype> gbs(ncomp, 0.0); +// std::vector<resulttype> pol_m(ncomp, 0.0); +// std::vector<resulttype> exp_m(ncomp, 0.0); + +// std::vector<resulttype> pol_dep(ncomp - 1, 0.0); +// std::vector<resulttype> exp_dep(ncomp - 1, 0.0); +// std::vector<resulttype> gbs_dep(ncomp - 1, 0.0); +// std::vector<resulttype> pol_m_dep(ncomp - 1, 0.0); +// std::vector<resulttype> exp_m_dep(ncomp - 1, 0.0); + + +// // pure fluid contribution +// for (size_t i = 0; i < ncomp; i++) { +// pol[i] = 0.0; +// for (size_t j = 0; j < t_pol.size(); j++) { +// pol[i] += n_pol[i][j] * pow(tau[i], t_pol[j]) * pow(delta[i], d_pol[j]); +// }; +// }; + +// for (size_t i = 0; i < ncomp; i++) { +// exp_[i] = 0.0; +// for (size_t j = 0; j < t_exp.size(); j++) { +// exp_[i] += n_exp[i][j] * pow(tau[i], t_exp[j]) * pow(delta[i], d_exp[j]) * exp(-pow(delta[i], p[j])); +// }; +// }; + +// for (size_t i = 0; i < ncomp; i++) { +// gbs[i] = 0.0; +// for (size_t j = 0; j < t_gbs.size(); j++) { +// gbs[i] += n_gbs[i][j] * pow(tau[i], t_gbs[j]) * pow(delta[i], d_gbs[j]) * exp(-eta[j] * (delta[i] - eps[j]) * (delta[i] - eps[j]) - beta[j] * (tau[i] - gam[j]) * (tau[i] - gam[j])); +// }; +// }; + +// for (size_t i = 0; i < ncomp; i++) { +// pol_m[i] = 0.0; +// for (size_t j = 0; j < t_pol_m.size(); j++) { +// pol_m[i] += n_pol_m[i][j] * pow(tau[i], t_pol_m[j]) * pow(delta[i], d_pol_m[j]); +// }; +// }; + +// for (size_t i = 0; i < ncomp; i++) { +// exp_m[i] = 0.0; +// for (size_t j = 0; j < t_exp_m.size(); j++) { +// exp_m[i] += n_exp_m[i][j] * pow(tau[i], t_exp_m[j]) * pow(delta[i], d_exp_m[j]) * exp(-pow(delta[i], p_m[j])); +// }; +// }; + +// // depature contribution +// /*for (size_t i = 0; i < ncomp - 1; i++) { +// pol_dep[i] = 0.0; +// for (size_t j = 0; j < t_pol.size(); j++) { +// pol_dep[i] += (n_pol[i][j] + n_pol[i + 1][j]) / 2.0 * pow(tau[i], t_pol[j]) * pow(delta[i], d_pol[j]); +// }; +// }; + +// for (size_t i = 0; i < ncomp - 1; i++) { +// exp_dep[i] = 0.0; +// for (size_t j = 0; j < t_exp.size(); j++) { +// exp_dep[i] += (n_exp[i][j] + n_exp[i + 1][j]) / 2.0 * pow(tau[i], t_exp[j]) * pow(delta[i], d_exp[j]) * exp(-pow(delta[i], p[j])); +// }; +// }; + +// for (size_t i = 0; i < ncomp - 1; i++) { +// gbs_dep[i] = 0.0; +// for (size_t j = 0; j < t_gbs.size(); j++) { +// gbs_dep[i] += (n_gbs[i][j] + n_gbs[i + 1][j]) / 2.0 * pow(tau[i], t_gbs[j]) * pow(delta[i], d_gbs[j]) * exp(-eta[j] * (delta[i] - eps[j]) * (delta[i] - eps[j]) - beta[j] * (tau[i] - gam[j]) * (tau[i] - gam[j])); +// }; +// }; + +// for (size_t i = 0; i < ncomp - 1; i++) { +// pol_m_dep[i] = 0.0; +// for (size_t j = 0; j < t_pol_m.size(); j++) { +// pol_m_dep[i] += (n_pol_m[i][j] + n_pol_m[i + 1][j]) / 2.0 * pow(tau[i], t_pol_m[j]) * pow(delta[i], d_pol_m[j]); +// }; +// }; +// for (size_t i = 0; i < ncomp - 1; i++) { +// exp_m_dep[i] = 0.0; +// for (size_t j = 0; j < t_exp_m.size(); j++) { +// exp_m_dep[i] += (n_exp_m[i][j] + n_exp_m[i + 1][j]) / 2.0 * pow(tau[i], t_exp_m[j]) * pow(delta[i], d_exp_m[j]) * exp(-pow(delta[i], p_m[j])); +// }; +// };*/ +// resulttype m_mix = 0.0; +// for (size_t i = 0; i < ncomp; i++) { +// m_mix += ms_s[i] * molefrac[i]; +// } + +// resulttype alpha_r_sphere = 0.0; +// resulttype alpha_r_el = 0.0; +// resulttype alpha_r_all = 0.0; + +// std::vector<resulttype> sphere_part(ncomp, 0.0); +// std::vector<resulttype> elongation_part(ncomp, 0.0); +// resulttype sphere_mix = 0.0; +// resulttype elongation_mix = 0.0; +// resulttype cross_mix = 0.0; + +// for (size_t i = 0; i < ncomp; i++) { +// // Single sphere part of component i +// sphere_part[i] = pol[i] + exp_[i] + gbs[i]; + +// // Single elongation part of component j +// elongation_part[i] = pol_m[i] + exp_m[i]; + +// sphere_mix += ms_s[i] * molefrac[i] * sphere_part[i]; + +// }; + +// for (size_t i = 0; i < ncomp - 1; i++) { +// for (size_t j = i + 1; j < ncomp; j++) { +// /*cross_mix += (molefrac[i] * molefrac[j] * ms_s[i]*(1.0 - ms_s[j])*sphere_part[i]*elongation_part[j] + +// ms_s[j]*(1.0 - ms_s[i])*sphere_part[j]*elongation_part[i] * molefrac[i] * molefrac[j]);*/ +// elongation_mix = (1.0 - ms_s[i]) * molefrac[i] * elongation_part[i] + (1.0 - ms_s[j]) * molefrac[j] * elongation_part[j]; +// } +// } + +// alpha_r_all = sphere_mix + elongation_mix + cross_mix; +// return forceeval(alpha_r_all); +// } + +//}; + + diff --git a/include/teqp/types.hpp b/include/teqp/types.hpp index bcebf21..1c296e0 100644 --- a/include/teqp/types.hpp +++ b/include/teqp/types.hpp @@ -213,6 +213,12 @@ namespace teqp { } #endif + template<typename T,typename B> + auto pow(const B& BASE, const T& POWER) { + return exp(POWER*log(BASE)); + } + + }; // namespace teqp #endif diff --git a/interface/CPP/teqp_impl_factory.cpp b/interface/CPP/teqp_impl_factory.cpp index 8c0c3f2..ee0909a 100644 --- a/interface/CPP/teqp_impl_factory.cpp +++ b/interface/CPP/teqp_impl_factory.cpp @@ -31,7 +31,7 @@ namespace teqp { {"2CLJF-Dipole", [](const nlohmann::json& spec){ return make_owned(twocenterljf::build_two_center_model_dipole(spec.at("author"), spec.at("L^*"), spec.at("(mu^*)^2")));}}, {"2CLJF-Quadrupole", [](const nlohmann::json& spec){ return make_owned(twocenterljf::build_two_center_model_quadrupole(spec.at("author"), spec.at("L^*"), spec.at("(mu^*)^2")));}}, {"IdealHelmholtz", [](const nlohmann::json& spec){ return make_owned(IdealHelmholtz(spec));}}, - + {"MieElong", [](const nlohmann::json& spec) { return make_owned(Mie::MieElong(spec.at("model_path"),spec.at("components"),spec.at("combination"))); }}, // Implemented in its own compilation unit to help with compilation time {"SAFT-VR-Mie", [](const nlohmann::json& spec){ return make_SAFTVRMie(spec); }} }; -- GitLab