<commit_before>
/*!
From REFPROP:
temperature                     K
pressure, fugacity              kPa
density                         mol/L
composition                     mole fraction
quality                         mole basis (moles vapor/total moles)
enthalpy, internal energy       J/mol
Gibbs, Helmholtz free energy    J/mol
entropy, heat capacity          J/(mol.K)
speed of sound                  m/s
Joule-Thomson coefficient       K/kPa
d(p)/d(rho)                     kPa.L/mol
d2(p)/d(rho)2                   kPa.(L/mol)^2
viscosity                       microPa.s (10^-6 Pa.s)
thermal conductivity            W/(m.K)
dipole moment                   debye
surface tension                 N/m
*/

#define REFPROP_IMPLEMENTATION
#define REFPROP_CSTYLE_REFERENCES
#include "externals/REFPROP-headers/REFPROP_lib.h"
#undef REFPROP_IMPLEMENTATION
#undef REFPROP_CSTYLE_REFERENCES

#include "CoolPropTools.h"
#include "REFPROPMixtureBackend.h"
#include "REFPROPBackend.h"
#include "Exceptions.h"
#include "Configuration.h"
#include "CPfilepaths.h"
#include "CoolProp.h"
#include "Solvers.h"
#include "IdealCurves.h"
#include "DataStructures.h"
#include "AbstractState.h"

#include <stdlib.h>
#include <string>
#include <stdio.h>
#include <iostream>
#include <cassert>
#include "crossplatform_shared_ptr.h"
#include <stdlib.h>

#if defined(_MSC_VER)
#define _CRTDBG_MAP_ALLOC
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <crtdbg.h>
#include <sys/stat.h>
#else
#include <sys/stat.h>
#endif

std::string LoadedREFPROPRef;

static bool dbg_refprop = false;
static const unsigned int number_of_endings = 5;
std::string endings[number_of_endings] = {"", ".FLD", ".fld", ".PPF", ".ppf"};

static char default_reference_state[] = "DEF";

// Default location, can be over-ridden by configuration variable
#if defined(__powerpc__) || defined(__ISLINUX__) || defined(__ISAPPLE__)
    char refpropPath[] = "/opt/refprop";
#elif defined(__ISWINDOWS__)
    char refpropPath[] = "";
#else
    #pragma error
#endif

std::string get_REFPROP_fluid_path_prefix()
{
    std::string rpPath = refpropPath;
    // Allow the user to specify an alternative REFPROP path by configuration value
    std::string alt_refprop_path = CoolProp::get_config_string(ALTERNATIVE_REFPROP_PATH);
    std::string separator = get_separator();
    if (!alt_refprop_path.empty()){
        //if (!endswith(alt_refprop_path, separator)){
        //    throw CoolProp::ValueError(format("ALTERNATIVE_REFPROP_PATH [%s] must end with a path sparator, typically a slash character", alt_refprop_path.c_str()));
        //}
        if (!path_exists(alt_refprop_path)) {
            throw CoolProp::ValueError(format("ALTERNATIVE_REFPROP_PATH [%s] could not be found", alt_refprop_path.c_str()));
        }
        // The alternative path has been set, so we give all fluid paths as relative to this directory
        return join_path(alt_refprop_path,"fluids");
    }
    #if defined(__ISWINDOWS__)
        return rpPath;
    #elif defined(__ISLINUX__) || defined(__ISAPPLE__)
        return join_path(rpPath,"fluids");
    #else
        throw CoolProp::NotImplementedError("This function should not be called.");
        return rpPath;
    #endif
}
std::string get_REFPROP_mixtures_path_prefix()
{
    std::string rpPath = refpropPath;
    // Allow the user to specify an alternative REFPROP path by configuration value
    std::string alt_refprop_path = CoolProp::get_config_string(ALTERNATIVE_REFPROP_PATH);
    std::string separator = get_separator();
    if (!alt_refprop_path.empty()){
        //if (!endswith(alt_refprop_path, separator)) {
        //    throw CoolProp::ValueError(format("ALTERNATIVE_REFPROP_PATH [%s] must end with a path sparator, typically a slash character", alt_refprop_path.c_str()));
        //}
        if (!path_exists(alt_refprop_path)) {
            throw CoolProp::ValueError(format("ALTERNATIVE_REFPROP_PATH [%s] could not be found", alt_refprop_path.c_str()));
        }
        // The alternative path has been set
        return join_path(alt_refprop_path, "mixtures");
    }
    #if defined(__ISWINDOWS__)
        return rpPath;
    #elif defined(__ISLINUX__) || defined(__ISAPPLE__)
        return join_path(rpPath,"mixtures");
    #else
        throw CoolProp::NotImplementedError("This function should not be called.");
        return rpPath;
    #endif
}

/// Construct the path to the HMX.BNC file
std::string get_REFPROP_HMX_BNC_path()
{
    std::string alt_hmx_bnc_path = CoolProp::get_config_string(ALTERNATIVE_REFPROP_HMX_BNC_PATH);
    // Use the alternative HMX.BNC path if provided - replace all the path to HMX.BNC with provided path
    if (!alt_hmx_bnc_path.empty()){
        return alt_hmx_bnc_path;
    }
    else{
        // Otherwise fall back to default paths; get_REFPROP_fluid_path_prefix will query ALTERNATIVE_REFPROP_PATH
        return join_path(get_REFPROP_fluid_path_prefix(), "HMX.BNC");
    }
}


namespace CoolProp {
    
class REFPROPGenerator : public AbstractStateGenerator{
public:
    AbstractState * get_AbstractState(const std::vector<std::string> &fluid_names){
        bool REFPROP_is_supported = REFPROPMixtureBackend::REFPROP_supported ();
        if (fluid_names.size() == 1){
            return new REFPROPBackend(fluid_names[0]);
        }
        else{
            return new REFPROPMixtureBackend(fluid_names);
        }
    };
};
// This static initialization will cause the generator to register
static GeneratorInitializer<REFPROPGenerator> refprop_gen(REFPROP_BACKEND_FAMILY);
    
void REFPROPMixtureBackend::construct(const std::vector<std::string>& fluid_names) {
    // Do the REFPROP instantiation for this fluid
    _mole_fractions_set = false;
    
    // Force loading of REFPROP
    REFPROP_supported();
    
    // Try to add this fluid to REFPROP - might want to think about making array of
    // components and setting mole fractions if they change a lot.
    this->set_REFPROP_fluids(fluid_names);
    
    // Bump the number of REFPROP backends that are in existence;
    REFPROPMixtureBackend::instance_counter++;
    
    // Set the imposed phase index to default value
    imposed_phase_index = iphase_not_imposed;
}

REFPROPMixtureBackend::~REFPROPMixtureBackend() {
    // Decrement the counter for the number of instances
    REFPROPMixtureBackend::instance_counter--;
    // Unload the shared library when the last instance is about to be destroyed
    if (REFPROPMixtureBackend::instance_counter == 0){
        if (RefpropdllInstance!=NULL) {

            // Unload it
            #if defined(__ISWINDOWS__)
                FreeLibrary(RefpropdllInstance);
                //delete RefpropdllInstance;
                RefpropdllInstance = NULL;
            #elif defined(__ISLINUX__)
                dlclose (RefpropdllInstance);
                //delete RefpropdllInstance;
                RefpropdllInstance = NULL;
            #elif defined(__ISAPPLE__)
                dlclose (RefpropdllInstance);
                //delete RefpropdllInstance;
                RefpropdllInstance = NULL;
            #else
                throw CoolProp::NotImplementedError("We should not reach this point.");
                //delete RefpropdllInstance;
                RefpropdllInstance = NULL;
            #endif
            LoadedREFPROPRef = "";
        }
    }
}
void REFPROPMixtureBackend::check_loaded_fluid()
{
    this->set_REFPROP_fluids(this->fluid_names);
}

std::size_t REFPROPMixtureBackend::instance_counter = 0; // initialise with 0
bool REFPROPMixtureBackend::_REFPROP_supported = true; // initialise with true
bool REFPROPMixtureBackend::REFPROP_supported () {
    /*
     * Here we build the bridge from macro definitions
     * into the actual code. This is also going to be
     * the central place to handle error messages on
     * unsupported platforms.
     */

    // Abort check if Refprop has been loaded.
    if (RefpropdllInstance!=NULL) return true;


    // Store result of previous check.
    if (_REFPROP_supported) {
        // Either Refprop is supported or it is the first check.
        std::string rpv(STRINGIFY(RPVersion));
        if (rpv.compare("NOTAVAILABLE")!=0) {
            // Function names were defined in "REFPROP_lib.h",
            // This platform theoretically supports Refprop.
            std::string err;
            const std::string alt_rp_path = get_config_string(ALTERNATIVE_REFPROP_PATH);
            const std::string alt_rp_name = get_config_string(ALTERNATIVE_REFPROP_LIBRARY_PATH);
            bool loaded_REFPROP = false;
            if (!alt_rp_name.empty()) {
                loaded_REFPROP = ::load_REFPROP(err, "", alt_rp_name);
            } else {
                loaded_REFPROP = ::load_REFPROP(err, alt_rp_path, "");
            }
                
            if (loaded_REFPROP) {
                return true;
            }
            else {
                printf("Good news: It is possible to use REFPROP on your system! However, the library \n");
                printf("could not be loaded. Please make sure that REFPROP is available on your system.\n\n");
                printf("Neither found in current location nor found in system PATH.\n");
                printf("If you already obtained a copy of REFPROP from http://www.nist.gov/srd/, \n");
                printf("add location of REFPROP to the PATH environment variable or your library path.\n\n");
                printf("In case you do not use Windows, have a look at https://github.com/jowr/librefprop.so \n");
                printf("to find instructions on how to compile your own version of the REFPROP library.\n\n");
                printf("ALTERNATIVE_REFPROP_PATH: %s\n", alt_rp_path.c_str());
                _REFPROP_supported = false;
                return false;
            }
        } else {
            // No definition of function names, we do not expect
            // the Refprop library to be available.
            _REFPROP_supported = false;
            return false;
        }
    } else {
        return false;
    }
    return false;
}
std::string REFPROPMixtureBackend::version(){
    long N = -1;
    long ierr = 0;
    char fluids[10000] = "", hmx[] = "HMX.BNC", default_reference_state[] = "DEF", herr[255] = "";
    if (!REFPROP_supported()) {
      return "n/a";
    };
    // Pad the version string with NULL characters
    for (int i = 0; i < 255; ++i){
        herr[i] = '\0';
    }
    SETUPdll(&N, fluids, hmx, default_reference_state,
                &ierr, herr,
                10000, // Length of component_string (see PASS_FTN.for from REFPROP)
                refpropcharlength, // Length of path_HMX_BNC
                lengthofreference, // Length of reference
                errormessagelength // Length of error message
                );
    if (strlen(herr) == 0){
        return format("%g", ((double)ierr)/10000.0);
    }
    else{
        std::string s(herr, herr+254);
        return strstrip(s);
    }
}

void REFPROPMixtureBackend::set_REFPROP_fluids(const std::vector<std::string> &fluid_names)
{
    // If the name of the refrigerant doesn't match
    // that of the currently loaded refrigerant, fluids must be loaded
    if (!cached_component_string.empty() && LoadedREFPROPRef == cached_component_string)
    {
        if (CoolProp::get_debug_level() > 5){ std::cout << format("%s:%d: The current fluid can be reused; %s and %s match \n",__FILE__,__LINE__,cached_component_string.c_str(),LoadedREFPROPRef.c_str()); }
        if (dbg_refprop) std::cout << format("%s:%d: The current fluid can be reused; %s and %s match \n",__FILE__,__LINE__,cached_component_string.c_str(),LoadedREFPROPRef.c_str());
        long N = static_cast<long>(this->fluid_names.size());
        if (N > ncmax){
            throw ValueError(format("Size of fluid vector [%d] is larger than the maximum defined by REFPROP [%d]", fluid_names.size(), ncmax));
        }
        // this->Ncomp = N; ( this should not get set because it is already set and is always 1 for predefined mixtures )
        mole_fractions.resize(ncmax);
        mole_fractions_liq.resize(ncmax);
        mole_fractions_vap.resize(ncmax);
        return;
    }
    else
    {
        long ierr=0;
        this->fluid_names = fluid_names;
        char component_string[10000], herr[errormessagelength];
        std::string components_joined = strjoin(fluid_names,"|");
        std::string components_joined_raw = strjoin(fluid_names,"|");
        std::string fdPath = get_REFPROP_fluid_path_prefix();
        long N = static_cast<long>(fluid_names.size());
        
        // Get path to HMX.BNC file
        char hmx_bnc[255];
        const std::string HMX_path = get_REFPROP_HMX_BNC_path();
        const char * _HMX_path = HMX_path.c_str();
        if (strlen(_HMX_path) > refpropcharlength){
            throw ValueError(format("Full HMX path (%s) is too long; max length is 255 characters", _HMX_path));
        }
        strcpy(hmx_bnc, _HMX_path);

        if (get_config_bool(REFPROP_USE_GERG)) {
            long iflag = 1, // Tell REFPROP to use GERG04; 0 unsets GERG usage
                 ierr = 0;
            char herr[255];
            GERG04dll(&N, &iflag, &ierr, herr, 255);
        }

        // Check platform support
        if(!REFPROP_supported()){ throw NotImplementedError("You cannot use the REFPROPMixtureBackend."); }

        if (N == 1 && upper(components_joined_raw).find(".MIX") != std::string::npos){
            // It's a predefined mixture
            ierr = 0;
            std::vector<double> x(ncmax);
            char mix[255], reference_state[4] = "DEF";
            
            std::string path_to_MIX_file = join_path(get_REFPROP_mixtures_path_prefix(), components_joined_raw);
            const char * _components_joined_raw  = path_to_MIX_file.c_str();
            if (strlen(_components_joined_raw) > 255){ throw ValueError(format("components (%s) is too long", components_joined_raw.c_str())); }
            strcpy(mix, _components_joined_raw);
            
            SETMIXdll(mix,
                      hmx_bnc,
                      reference_state,
                      &N,
                      component_string,
                      &(x[0]),
                      &ierr,
                      herr,
                      255,
                      255,
                      3,
                      10000,
                      255);
            if (static_cast<int>(ierr) <= 0){
                this->Ncomp = N;
                mole_fractions.resize(ncmax);
                mole_fractions_liq.resize(ncmax);
                mole_fractions_vap.resize(ncmax);
                LoadedREFPROPRef = mix;
                cached_component_string = mix;
                this->fluid_names.clear();
                this->fluid_names.push_back(components_joined_raw);
                if (CoolProp::get_debug_level() > 5){ std::cout << format("%s:%d: Successfully loaded REFPROP fluid: %s\n",__FILE__,__LINE__, components_joined.c_str()); }
                if (dbg_refprop) std::cout << format("%s:%d: Successfully loaded REFPROP fluid: %s\n",__FILE__,__LINE__, components_joined.c_str());
                if (get_config_bool(REFPROP_DONT_ESTIMATE_INTERACTION_PARAMETERS) && ierr == -117){
                    throw ValueError(format("Interaction parameter estimation has been disabled: %s", herr));
                }
                set_mole_fractions(std::vector<CoolPropDbl>(x.begin(), x.begin()+N));
                if (get_config_bool(REFPROP_USE_PENGROBINSON)) {
                    long iflag = 2; // Tell REFPROP to use Peng-Robinson;
                    PREOSdll(&iflag);
                }
                else{
                    long iflag = 0; // Tell REFPROP to use normal Helmholtz models
                    PREOSdll(&iflag);
                }
                return;
            }
            else{
                throw ValueError(format("Unable to load mixture: %s",components_joined_raw.c_str()));
            }
        }

        // Loop over the file names - first we try with nothing, then .fld, then .FLD, then .ppf - means you can't mix and match
        for (unsigned int k = 0; k < number_of_endings; k++)
        {
            // Build the mixture string
            for (unsigned int j = 0; j < (unsigned int)N; j++)
            {
                if (j == 0){
                    components_joined = join_path(fdPath, upper(fluid_names[j]) + endings[k]);
                }
                else{
                    components_joined += "|" + join_path(fdPath, upper(fluid_names[j]) + endings[k]);
                }
            }

            if (dbg_refprop) std::cout << format("%s:%d: The fluid %s has not been loaded before, current value is %s \n",__FILE__,__LINE__,components_joined_raw.c_str(),LoadedREFPROPRef.c_str());

            // Copy over the list of components
            const char * _components_joined = components_joined.c_str();
            if (strlen(_components_joined) > 10000) { throw ValueError(format("components_joined (%s) is too long", _components_joined)); }
            strcpy(component_string, _components_joined);
            // Pad the fluid string all the way to 10k characters with spaces to deal with string parsing bug in REFPROP in SETUPdll
            for (int i = static_cast<int>(components_joined.size()); i < 10000; ++i){
                component_string[i] = ' ';
            }

            ierr = 0;
            //...Call SETUP to initialize the program
            SETUPdll(&N, component_string, hmx_bnc, default_reference_state,
                     &ierr, herr,
                     10000, // Length of component_string (see PASS_FTN.for from REFPROP)
                     refpropcharlength, // Length of path_HMX_BNC
                     lengthofreference, // Length of reference
                     errormessagelength // Length of error message
                     );
            if (get_config_bool(REFPROP_DONT_ESTIMATE_INTERACTION_PARAMETERS) && ierr == -117){
                throw ValueError(format("Interaction parameter estimation has been disabled: %s", herr));
            }
            if (get_config_bool(REFPROP_IGNORE_ERROR_ESTIMATED_INTERACTION_PARAMETERS) && ierr == 117) {
                ierr = 0;
            }
            if (static_cast<int>(ierr) <= 0) // Success (or a warning, which is silently squelched for now)
            {
                this->Ncomp = N;
                mole_fractions.resize(ncmax);
                mole_fractions_liq.resize(ncmax);
                mole_fractions_vap.resize(ncmax);
                LoadedREFPROPRef = _components_joined;
                cached_component_string = _components_joined;
                if (CoolProp::get_debug_level() > 5){ std::cout << format("%s:%d: Successfully loaded REFPROP fluid: %s\n",__FILE__,__LINE__, components_joined.c_str()); }
                if (dbg_refprop) std::cout << format("%s:%d: Successfully loaded REFPROP fluid: %s\n",__FILE__,__LINE__, components_joined.c_str());
                
                if (get_config_bool(REFPROP_USE_PENGROBINSON)) {
                    long iflag = 2; // Tell REFPROP to use Peng-Robinson;
                    PREOSdll(&iflag);
                }
                else{
                    long iflag = 0; // Tell REFPROP to use normal Helmholtz models
                    PREOSdll(&iflag);
                }
                return;
            }
            else if (k < number_of_endings-1){ // Keep going
                if (CoolProp::get_debug_level() > 5){std::cout << format("REFPROP error/warning [ierr: %d]: %s",ierr, herr) << std::endl;}
                continue;
            }
            else
            {
                if (CoolProp::get_debug_level() > 5){std::cout << format("k: %d #endings: %d", k, number_of_endings) << std::endl;}
                throw ValueError(format("Could not load these fluids: %s", components_joined_raw.c_str()));
            }
        }
    }
}
std::string REFPROPMixtureBackend::fluid_param_string(const std::string &ParamName){
    if (ParamName == "CAS"){
//        subroutine NAME (icomp,hnam,hn80,hcasn)
//        c
//        c  provides name information for specified component
//            c
//            c  input:
//            c    icomp--component number in mixture; 1 for pure fluid
//                c  outputs:
//                c     hnam--component name [character*12]
//                c     hn80--component name--long form [character*80]
//                c    hcasn--CAS (Chemical Abstracts Service) number [character*12]
        std::vector<std::string> CASvec;
        for (long icomp = 1L; icomp <= static_cast<long>(fluid_names.size()); ++icomp){
            char hnam[13], hn80[81], hcasn[13];
            NAMEdll(&icomp, hnam, hn80, hcasn, 12, 80, 12);
            hcasn[12]='\0';
            std::string casn = hcasn;
            strstrip(casn);
            CASvec.push_back(casn);
        }
        return strjoin(CASvec, "&");
    }
    else if (ParamName == "name"){
        long icomp = 1L;
        char hnam[13], hn80[81], hcasn[13];
        NAMEdll(&icomp, hnam, hn80, hcasn, 12, 80, 12);
        hnam[12] = '\0';
        std::string name = hnam;
        strstrip(name);
        return name;
    }
    else if (ParamName == "long_name"){
        long icomp = 1L;
        char hnam[13], hn80[81], hcasn[13];
        NAMEdll(&icomp, hnam, hn80, hcasn, 12, 80, 12);
        hn80[80] = '\0';
        std::string n80 = hn80;
        strstrip(n80);
        return n80;
    }
    else{
        throw ValueError(format("parameter to fluid_param_string is invalid: %s", ParamName.c_str()));
    }
};
long REFPROPMixtureBackend::match_CAS(const std::string &CAS){
    for (long icomp = 1L; icomp <= static_cast<long>(fluid_names.size()); ++icomp){
        char hnam[13], hn80[81], hcasn[13];
        NAMEdll(&icomp, hnam, hn80, hcasn, 12, 80, 12);
        hcasn[12] = '\0';
        std::string casn = hcasn;
        strstrip(casn);
        if (casn == CAS){
            return icomp;
        }
    }
    throw ValueError(format("Unable to match CAS number [%s]", CAS.c_str()));
}
/// Set binary mixture floating point parameter
void REFPROPMixtureBackend::set_binary_interaction_double(const std::string &CAS1, const std::string &CAS2, const std::string &parameter, const double value){
    std::size_t i = match_CAS(CAS1)-1, j = match_CAS(CAS2)-1;
    return set_binary_interaction_double(i, j, parameter, value);
};
/// Get binary mixture double value
double REFPROPMixtureBackend::get_binary_interaction_double(const std::string &CAS1, const std::string &CAS2, const std::string &parameter){
    std::size_t i = match_CAS(CAS1)-1, j = match_CAS(CAS2)-1;
    return get_binary_interaction_double(i, j, parameter);
}
/// Get binary mixture string value
std::string REFPROPMixtureBackend::get_binary_interaction_string(const std::string &CAS1, const std::string &CAS2, const std::string &parameter){
        
    long icomp, jcomp;
    char hmodij[4], hfmix[255], hbinp[255], hfij[255], hmxrul[255];
    double fij[6];
    
    icomp = match_CAS(CAS1);
    jcomp = match_CAS(CAS2);
    
    // Get the current state
    GETKTVdll(&icomp, &jcomp, hmodij, fij, hfmix, hfij, hbinp, hmxrul, 3, 255, 255, 255, 255);
    
    std::string shmodij(hmodij);
    if (shmodij.find("KW")==0 || shmodij.find("GE")==0)// Starts with KW or GE
    {
        if (parameter == "model"){ 
            return shmodij;
        }
        else {
            throw ValueError(format(" I don't know what to do with your parameter [%s]", parameter.c_str()));
            return "";
        }
    }
    else {
        //throw ValueError(format("For now, model [%s] must start with KW or GE", hmodij));
        return "";
    }
}
/// Set binary mixture string value
void REFPROPMixtureBackend::set_binary_interaction_string(const std::size_t i, const std::size_t j, const std::string &parameter, const std::string &value) {

    long icomp = static_cast<long>(i) + 1, jcomp = static_cast<long>(j) + 1, ierr = 0L;
    char hmodij[4], hfmix[255], hbinp[255], hfij[255], hmxrul[255];
    double fij[6];
    char herr[255];

    // Get the current state
    GETKTVdll(&icomp, &jcomp, hmodij, fij, hfmix, hfij, hbinp, hmxrul, 3, 255, 255, 255, 255);

    if (parameter == "model") {
        if (value.length()>4) { 
            throw ValueError(format("Model parameter (%s) is longer than 4 characters.", value)); 
        } else {
            strcpy(hmodij, value.c_str());
        }
    }
    else {
        throw ValueError(format("I don't know what to do with your parameter [%s]", parameter.c_str()));
    }
    SETKTVdll(&icomp, &jcomp, hmodij, fij, hfmix, &ierr, herr, 3, 255, 255);
    if (ierr > 0L){
        throw ValueError(format("Unable to set parameter[%s] to value[%s]: %s",parameter.c_str(),value.c_str(),herr));
    }
}
/// Set binary mixture string parameter (EXPERT USE ONLY!!!)
void REFPROPMixtureBackend::set_binary_interaction_double(const std::size_t i, const std::size_t j, const std::string &parameter, const double value){
    long icomp = static_cast<long>(i)+1, jcomp = static_cast<long>(j)+1, ierr = 0L;
    char hmodij[4], hfmix[255], hbinp[255], hfij[255], hmxrul[255];
    double fij[6];
    char herr[255];
    
    // Get the prior state
    GETKTVdll(&icomp, &jcomp, hmodij, fij, hfmix, hfij, hbinp, hmxrul, 3, 255, 255, 255, 255);
    
    std::string shmodij(hmodij);
    if (shmodij.find("KW")==0 || shmodij.find("GE")==0)// Starts with KW or GE
    {
        if (parameter == "betaT"){ fij[0] = value;}
        else if (parameter == "gammaT"){ fij[1] = value; }
        else if (parameter == "betaV"){ fij[2] = value; }
        else if (parameter == "gammaV"){ fij[3] = value; }
        else if (parameter == "Fij"){ fij[4] = value; }
        else{
            throw ValueError(format("I don't know what to do with your parameter [%s]", parameter.c_str()));
        }
        SETKTVdll(&icomp, &jcomp, hmodij, fij, hfmix, &ierr, herr, 3, 255, 255);
        if (ierr > 0L){
            throw ValueError(format("Unable to set parameter[%s] to value[%g]: %s",parameter.c_str(),value,herr));
        }
    }
    else{
        throw ValueError(format("For now, model [%s] must start with KW or GE", hmodij));
    }
}

/// Get binary mixture double value (EXPERT USE ONLY!!!)
double REFPROPMixtureBackend::get_binary_interaction_double(const std::size_t i, const std::size_t j, const std::string &parameter){
    long icomp = static_cast<long>(i)+1, jcomp = static_cast<long>(j)+1;
    char hmodij[4], hfmix[255], hbinp[255], hfij[255], hmxrul[255];
    double fij[6];
    
    // Get the current state
    GETKTVdll(&icomp, &jcomp, hmodij, fij, hfmix, hfij, hbinp, hmxrul, 3, 255, 255, 255, 255);
    
    std::string shmodij(hmodij);
    if (shmodij.find("KW")==0 || shmodij.find("GE")==0)// Starts with KW or GE
    {
        double val;
        if (parameter == "betaT"){ val = fij[0];}
        else if (parameter == "gammaT"){ val = fij[1]; }
        else if (parameter == "betaV"){ val = fij[2]; }
        else if (parameter == "gammaV"){ val = fij[3]; }
        else if (parameter == "Fij"){ val = fij[4]; }
        else{
            throw ValueError(format(" I don't know what to do with your parameter [%s]", parameter.c_str()));
            return _HUGE;
        }
        return val;
    }
    else{
        //throw ValueError(format("For now, model [%s] must start with KW or GE", hmodij));
        return _HUGE;
    }
}
void REFPROPMixtureBackend::set_mole_fractions(const std::vector<CoolPropDbl> &mole_fractions)
{
    if (mole_fractions.size() != this->Ncomp)
    {
        throw ValueError(format("Size of mole fraction vector [%d] does not equal that of component vector [%d]", mole_fractions.size(), this->Ncomp));
    }
    this->mole_fractions = std::vector<CoolPropDbl>(ncmax, 0.0);
    for (std::size_t i = 0; i < mole_fractions.size(); ++i){
        this->mole_fractions[i] = static_cast<double>(mole_fractions[i]);
    }
    this->mole_fractions_long_double = mole_fractions; // same size as Ncomp
    _mole_fractions_set = true;
}
void REFPROPMixtureBackend::set_mass_fractions(const std::vector<CoolPropDbl> &mass_fractions)
{<commit_msg>Clear reducing state for REFPROP backend when fractions set; closes #1659<commit_after>
/*!
From REFPROP:
temperature                     K
pressure, fugacity              kPa
density                         mol/L
composition                     mole fraction
quality                         mole basis (moles vapor/total moles)
enthalpy, internal energy       J/mol
Gibbs, Helmholtz free energy    J/mol
entropy, heat capacity          J/(mol.K)
speed of sound                  m/s
Joule-Thomson coefficient       K/kPa
d(p)/d(rho)                     kPa.L/mol
d2(p)/d(rho)2                   kPa.(L/mol)^2
viscosity                       microPa.s (10^-6 Pa.s)
thermal conductivity            W/(m.K)
dipole moment                   debye
surface tension                 N/m
*/

#define REFPROP_IMPLEMENTATION
#define REFPROP_CSTYLE_REFERENCES
#include "externals/REFPROP-headers/REFPROP_lib.h"
#undef REFPROP_IMPLEMENTATION
#undef REFPROP_CSTYLE_REFERENCES

#include "CoolPropTools.h"
#include "REFPROPMixtureBackend.h"
#include "REFPROPBackend.h"
#include "Exceptions.h"
#include "Configuration.h"
#include "CPfilepaths.h"
#include "CoolProp.h"
#include "Solvers.h"
#include "IdealCurves.h"
#include "DataStructures.h"
#include "AbstractState.h"

#include <stdlib.h>
#include <string>
#include <stdio.h>
#include <iostream>
#include <cassert>
#include "crossplatform_shared_ptr.h"
#include <stdlib.h>

#if defined(_MSC_VER)
#define _CRTDBG_MAP_ALLOC
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <crtdbg.h>
#include <sys/stat.h>
#else
#include <sys/stat.h>
#endif

std::string LoadedREFPROPRef;

static bool dbg_refprop = false;
static const unsigned int number_of_endings = 5;
std::string endings[number_of_endings] = {"", ".FLD", ".fld", ".PPF", ".ppf"};

static char default_reference_state[] = "DEF";

// Default location, can be over-ridden by configuration variable
#if defined(__powerpc__) || defined(__ISLINUX__) || defined(__ISAPPLE__)
    char refpropPath[] = "/opt/refprop";
#elif defined(__ISWINDOWS__)
    char refpropPath[] = "";
#else
    #pragma error
#endif

std::string get_REFPROP_fluid_path_prefix()
{
    std::string rpPath = refpropPath;
    // Allow the user to specify an alternative REFPROP path by configuration value
    std::string alt_refprop_path = CoolProp::get_config_string(ALTERNATIVE_REFPROP_PATH);
    std::string separator = get_separator();
    if (!alt_refprop_path.empty()){
        //if (!endswith(alt_refprop_path, separator)){
        //    throw CoolProp::ValueError(format("ALTERNATIVE_REFPROP_PATH [%s] must end with a path sparator, typically a slash character", alt_refprop_path.c_str()));
        //}
        if (!path_exists(alt_refprop_path)) {
            throw CoolProp::ValueError(format("ALTERNATIVE_REFPROP_PATH [%s] could not be found", alt_refprop_path.c_str()));
        }
        // The alternative path has been set, so we give all fluid paths as relative to this directory
        return join_path(alt_refprop_path,"fluids");
    }
    #if defined(__ISWINDOWS__)
        return rpPath;
    #elif defined(__ISLINUX__) || defined(__ISAPPLE__)
        return join_path(rpPath,"fluids");
    #else
        throw CoolProp::NotImplementedError("This function should not be called.");
        return rpPath;
    #endif
}
std::string get_REFPROP_mixtures_path_prefix()
{
    std::string rpPath = refpropPath;
    // Allow the user to specify an alternative REFPROP path by configuration value
    std::string alt_refprop_path = CoolProp::get_config_string(ALTERNATIVE_REFPROP_PATH);
    std::string separator = get_separator();
    if (!alt_refprop_path.empty()){
        //if (!endswith(alt_refprop_path, separator)) {
        //    throw CoolProp::ValueError(format("ALTERNATIVE_REFPROP_PATH [%s] must end with a path sparator, typically a slash character", alt_refprop_path.c_str()));
        //}
        if (!path_exists(alt_refprop_path)) {
            throw CoolProp::ValueError(format("ALTERNATIVE_REFPROP_PATH [%s] could not be found", alt_refprop_path.c_str()));
        }
        // The alternative path has been set
        return join_path(alt_refprop_path, "mixtures");
    }
    #if defined(__ISWINDOWS__)
        return rpPath;
    #elif defined(__ISLINUX__) || defined(__ISAPPLE__)
        return join_path(rpPath,"mixtures");
    #else
        throw CoolProp::NotImplementedError("This function should not be called.");
        return rpPath;
    #endif
}

/// Construct the path to the HMX.BNC file
std::string get_REFPROP_HMX_BNC_path()
{
    std::string alt_hmx_bnc_path = CoolProp::get_config_string(ALTERNATIVE_REFPROP_HMX_BNC_PATH);
    // Use the alternative HMX.BNC path if provided - replace all the path to HMX.BNC with provided path
    if (!alt_hmx_bnc_path.empty()){
        return alt_hmx_bnc_path;
    }
    else{
        // Otherwise fall back to default paths; get_REFPROP_fluid_path_prefix will query ALTERNATIVE_REFPROP_PATH
        return join_path(get_REFPROP_fluid_path_prefix(), "HMX.BNC");
    }
}


namespace CoolProp {
    
class REFPROPGenerator : public AbstractStateGenerator{
public:
    AbstractState * get_AbstractState(const std::vector<std::string> &fluid_names){
        bool REFPROP_is_supported = REFPROPMixtureBackend::REFPROP_supported ();
        if (fluid_names.size() == 1){
            return new REFPROPBackend(fluid_names[0]);
        }
        else{
            return new REFPROPMixtureBackend(fluid_names);
        }
    };
};
// This static initialization will cause the generator to register
static GeneratorInitializer<REFPROPGenerator> refprop_gen(REFPROP_BACKEND_FAMILY);
    
void REFPROPMixtureBackend::construct(const std::vector<std::string>& fluid_names) {
    // Do the REFPROP instantiation for this fluid
    _mole_fractions_set = false;
    
    // Force loading of REFPROP
    REFPROP_supported();
    
    // Try to add this fluid to REFPROP - might want to think about making array of
    // components and setting mole fractions if they change a lot.
    this->set_REFPROP_fluids(fluid_names);
    
    // Bump the number of REFPROP backends that are in existence;
    REFPROPMixtureBackend::instance_counter++;
    
    // Set the imposed phase index to default value
    imposed_phase_index = iphase_not_imposed;
}

REFPROPMixtureBackend::~REFPROPMixtureBackend() {
    // Decrement the counter for the number of instances
    REFPROPMixtureBackend::instance_counter--;
    // Unload the shared library when the last instance is about to be destroyed
    if (REFPROPMixtureBackend::instance_counter == 0){
        if (RefpropdllInstance!=NULL) {

            // Unload it
            #if defined(__ISWINDOWS__)
                FreeLibrary(RefpropdllInstance);
                //delete RefpropdllInstance;
                RefpropdllInstance = NULL;
            #elif defined(__ISLINUX__)
                dlclose (RefpropdllInstance);
                //delete RefpropdllInstance;
                RefpropdllInstance = NULL;
            #elif defined(__ISAPPLE__)
                dlclose (RefpropdllInstance);
                //delete RefpropdllInstance;
                RefpropdllInstance = NULL;
            #else
                throw CoolProp::NotImplementedError("We should not reach this point.");
                //delete RefpropdllInstance;
                RefpropdllInstance = NULL;
            #endif
            LoadedREFPROPRef = "";
        }
    }
}
void REFPROPMixtureBackend::check_loaded_fluid()
{
    this->set_REFPROP_fluids(this->fluid_names);
}

std::size_t REFPROPMixtureBackend::instance_counter = 0; // initialise with 0
bool REFPROPMixtureBackend::_REFPROP_supported = true; // initialise with true
bool REFPROPMixtureBackend::REFPROP_supported () {
    /*
     * Here we build the bridge from macro definitions
     * into the actual code. This is also going to be
     * the central place to handle error messages on
     * unsupported platforms.
     */

    // Abort check if Refprop has been loaded.
    if (RefpropdllInstance!=NULL) return true;


    // Store result of previous check.
    if (_REFPROP_supported) {
        // Either Refprop is supported or it is the first check.
        std::string rpv(STRINGIFY(RPVersion));
        if (rpv.compare("NOTAVAILABLE")!=0) {
            // Function names were defined in "REFPROP_lib.h",
            // This platform theoretically supports Refprop.
            std::string err;
            const std::string alt_rp_path = get_config_string(ALTERNATIVE_REFPROP_PATH);
            const std::string alt_rp_name = get_config_string(ALTERNATIVE_REFPROP_LIBRARY_PATH);
            bool loaded_REFPROP = false;
            if (!alt_rp_name.empty()) {
                loaded_REFPROP = ::load_REFPROP(err, "", alt_rp_name);
            } else {
                loaded_REFPROP = ::load_REFPROP(err, alt_rp_path, "");
            }
                
            if (loaded_REFPROP) {
                return true;
            }
            else {
                printf("Good news: It is possible to use REFPROP on your system! However, the library \n");
                printf("could not be loaded. Please make sure that REFPROP is available on your system.\n\n");
                printf("Neither found in current location nor found in system PATH.\n");
                printf("If you already obtained a copy of REFPROP from http://www.nist.gov/srd/, \n");
                printf("add location of REFPROP to the PATH environment variable or your library path.\n\n");
                printf("In case you do not use Windows, have a look at https://github.com/jowr/librefprop.so \n");
                printf("to find instructions on how to compile your own version of the REFPROP library.\n\n");
                printf("ALTERNATIVE_REFPROP_PATH: %s\n", alt_rp_path.c_str());
                _REFPROP_supported = false;
                return false;
            }
        } else {
            // No definition of function names, we do not expect
            // the Refprop library to be available.
            _REFPROP_supported = false;
            return false;
        }
    } else {
        return false;
    }
    return false;
}
std::string REFPROPMixtureBackend::version(){
    long N = -1;
    long ierr = 0;
    char fluids[10000] = "", hmx[] = "HMX.BNC", default_reference_state[] = "DEF", herr[255] = "";
    if (!REFPROP_supported()) {
      return "n/a";
    };
    // Pad the version string with NULL characters
    for (int i = 0; i < 255; ++i){
        herr[i] = '\0';
    }
    SETUPdll(&N, fluids, hmx, default_reference_state,
                &ierr, herr,
                10000, // Length of component_string (see PASS_FTN.for from REFPROP)
                refpropcharlength, // Length of path_HMX_BNC
                lengthofreference, // Length of reference
                errormessagelength // Length of error message
                );
    if (strlen(herr) == 0){
        return format("%g", ((double)ierr)/10000.0);
    }
    else{
        std::string s(herr, herr+254);
        return strstrip(s);
    }
}

void REFPROPMixtureBackend::set_REFPROP_fluids(const std::vector<std::string> &fluid_names)
{
    // If the name of the refrigerant doesn't match
    // that of the currently loaded refrigerant, fluids must be loaded
    if (!cached_component_string.empty() && LoadedREFPROPRef == cached_component_string)
    {
        if (CoolProp::get_debug_level() > 5){ std::cout << format("%s:%d: The current fluid can be reused; %s and %s match \n",__FILE__,__LINE__,cached_component_string.c_str(),LoadedREFPROPRef.c_str()); }
        if (dbg_refprop) std::cout << format("%s:%d: The current fluid can be reused; %s and %s match \n",__FILE__,__LINE__,cached_component_string.c_str(),LoadedREFPROPRef.c_str());
        long N = static_cast<long>(this->fluid_names.size());
        if (N > ncmax){
            throw ValueError(format("Size of fluid vector [%d] is larger than the maximum defined by REFPROP [%d]", fluid_names.size(), ncmax));
        }
        // this->Ncomp = N; ( this should not get set because it is already set and is always 1 for predefined mixtures )
        mole_fractions.resize(ncmax);
        mole_fractions_liq.resize(ncmax);
        mole_fractions_vap.resize(ncmax);
        return;
    }
    else
    {
        long ierr=0;
        this->fluid_names = fluid_names;
        char component_string[10000], herr[errormessagelength];
        std::string components_joined = strjoin(fluid_names,"|");
        std::string components_joined_raw = strjoin(fluid_names,"|");
        std::string fdPath = get_REFPROP_fluid_path_prefix();
        long N = static_cast<long>(fluid_names.size());
        
        // Get path to HMX.BNC file
        char hmx_bnc[255];
        const std::string HMX_path = get_REFPROP_HMX_BNC_path();
        const char * _HMX_path = HMX_path.c_str();
        if (strlen(_HMX_path) > refpropcharlength){
            throw ValueError(format("Full HMX path (%s) is too long; max length is 255 characters", _HMX_path));
        }
        strcpy(hmx_bnc, _HMX_path);

        if (get_config_bool(REFPROP_USE_GERG)) {
            long iflag = 1, // Tell REFPROP to use GERG04; 0 unsets GERG usage
                 ierr = 0;
            char herr[255];
            GERG04dll(&N, &iflag, &ierr, herr, 255);
        }

        // Check platform support
        if(!REFPROP_supported()){ throw NotImplementedError("You cannot use the REFPROPMixtureBackend."); }

        if (N == 1 && upper(components_joined_raw).find(".MIX") != std::string::npos){
            // It's a predefined mixture
            ierr = 0;
            std::vector<double> x(ncmax);
            char mix[255], reference_state[4] = "DEF";
            
            std::string path_to_MIX_file = join_path(get_REFPROP_mixtures_path_prefix(), components_joined_raw);
            const char * _components_joined_raw  = path_to_MIX_file.c_str();
            if (strlen(_components_joined_raw) > 255){ throw ValueError(format("components (%s) is too long", components_joined_raw.c_str())); }
            strcpy(mix, _components_joined_raw);
            
            SETMIXdll(mix,
                      hmx_bnc,
                      reference_state,
                      &N,
                      component_string,
                      &(x[0]),
                      &ierr,
                      herr,
                      255,
                      255,
                      3,
                      10000,
                      255);
            if (static_cast<int>(ierr) <= 0){
                this->Ncomp = N;
                mole_fractions.resize(ncmax);
                mole_fractions_liq.resize(ncmax);
                mole_fractions_vap.resize(ncmax);
                LoadedREFPROPRef = mix;
                cached_component_string = mix;
                this->fluid_names.clear();
                this->fluid_names.push_back(components_joined_raw);
                if (CoolProp::get_debug_level() > 5){ std::cout << format("%s:%d: Successfully loaded REFPROP fluid: %s\n",__FILE__,__LINE__, components_joined.c_str()); }
                if (dbg_refprop) std::cout << format("%s:%d: Successfully loaded REFPROP fluid: %s\n",__FILE__,__LINE__, components_joined.c_str());
                if (get_config_bool(REFPROP_DONT_ESTIMATE_INTERACTION_PARAMETERS) && ierr == -117){
                    throw ValueError(format("Interaction parameter estimation has been disabled: %s", herr));
                }
                set_mole_fractions(std::vector<CoolPropDbl>(x.begin(), x.begin()+N));
                if (get_config_bool(REFPROP_USE_PENGROBINSON)) {
                    long iflag = 2; // Tell REFPROP to use Peng-Robinson;
                    PREOSdll(&iflag);
                }
                else{
                    long iflag = 0; // Tell REFPROP to use normal Helmholtz models
                    PREOSdll(&iflag);
                }
                return;
            }
            else{
                throw ValueError(format("Unable to load mixture: %s",components_joined_raw.c_str()));
            }
        }

        // Loop over the file names - first we try with nothing, then .fld, then .FLD, then .ppf - means you can't mix and match
        for (unsigned int k = 0; k < number_of_endings; k++)
        {
            // Build the mixture string
            for (unsigned int j = 0; j < (unsigned int)N; j++)
            {
                if (j == 0){
                    components_joined = join_path(fdPath, upper(fluid_names[j]) + endings[k]);
                }
                else{
                    components_joined += "|" + join_path(fdPath, upper(fluid_names[j]) + endings[k]);
                }
            }

            if (dbg_refprop) std::cout << format("%s:%d: The fluid %s has not been loaded before, current value is %s \n",__FILE__,__LINE__,components_joined_raw.c_str(),LoadedREFPROPRef.c_str());

            // Copy over the list of components
            const char * _components_joined = components_joined.c_str();
            if (strlen(_components_joined) > 10000) { throw ValueError(format("components_joined (%s) is too long", _components_joined)); }
            strcpy(component_string, _components_joined);
            // Pad the fluid string all the way to 10k characters with spaces to deal with string parsing bug in REFPROP in SETUPdll
            for (int i = static_cast<int>(components_joined.size()); i < 10000; ++i){
                component_string[i] = ' ';
            }

            ierr = 0;
            //...Call SETUP to initialize the program
            SETUPdll(&N, component_string, hmx_bnc, default_reference_state,
                     &ierr, herr,
                     10000, // Length of component_string (see PASS_FTN.for from REFPROP)
                     refpropcharlength, // Length of path_HMX_BNC
                     lengthofreference, // Length of reference
                     errormessagelength // Length of error message
                     );
            if (get_config_bool(REFPROP_DONT_ESTIMATE_INTERACTION_PARAMETERS) && ierr == -117){
                throw ValueError(format("Interaction parameter estimation has been disabled: %s", herr));
            }
            if (get_config_bool(REFPROP_IGNORE_ERROR_ESTIMATED_INTERACTION_PARAMETERS) && ierr == 117) {
                ierr = 0;
            }
            if (static_cast<int>(ierr) <= 0) // Success (or a warning, which is silently squelched for now)
            {
                this->Ncomp = N;
                mole_fractions.resize(ncmax);
                mole_fractions_liq.resize(ncmax);
                mole_fractions_vap.resize(ncmax);
                LoadedREFPROPRef = _components_joined;
                cached_component_string = _components_joined;
                if (CoolProp::get_debug_level() > 5){ std::cout << format("%s:%d: Successfully loaded REFPROP fluid: %s\n",__FILE__,__LINE__, components_joined.c_str()); }
                if (dbg_refprop) std::cout << format("%s:%d: Successfully loaded REFPROP fluid: %s\n",__FILE__,__LINE__, components_joined.c_str());
                
                if (get_config_bool(REFPROP_USE_PENGROBINSON)) {
                    long iflag = 2; // Tell REFPROP to use Peng-Robinson;
                    PREOSdll(&iflag);
                }
                else{
                    long iflag = 0; // Tell REFPROP to use normal Helmholtz models
                    PREOSdll(&iflag);
                }
                return;
            }
            else if (k < number_of_endings-1){ // Keep going
                if (CoolProp::get_debug_level() > 5){std::cout << format("REFPROP error/warning [ierr: %d]: %s",ierr, herr) << std::endl;}
                continue;
            }
            else
            {
                if (CoolProp::get_debug_level() > 5){std::cout << format("k: %d #endings: %d", k, number_of_endings) << std::endl;}
                throw ValueError(format("Could not load these fluids: %s", components_joined_raw.c_str()));
            }
        }
    }
}
std::string REFPROPMixtureBackend::fluid_param_string(const std::string &ParamName){
    if (ParamName == "CAS"){
//        subroutine NAME (icomp,hnam,hn80,hcasn)
//        c
//        c  provides name information for specified component
//            c
//            c  input:
//            c    icomp--component number in mixture; 1 for pure fluid
//                c  outputs:
//                c     hnam--component name [character*12]
//                c     hn80--component name--long form [character*80]
//                c    hcasn--CAS (Chemical Abstracts Service) number [character*12]
        std::vector<std::string> CASvec;
        for (long icomp = 1L; icomp <= static_cast<long>(fluid_names.size()); ++icomp){
            char hnam[13], hn80[81], hcasn[13];
            NAMEdll(&icomp, hnam, hn80, hcasn, 12, 80, 12);
            hcasn[12]='\0';
            std::string casn = hcasn;
            strstrip(casn);
            CASvec.push_back(casn);
        }
        return strjoin(CASvec, "&");
    }
    else if (ParamName == "name"){
        long icomp = 1L;
        char hnam[13], hn80[81], hcasn[13];
        NAMEdll(&icomp, hnam, hn80, hcasn, 12, 80, 12);
        hnam[12] = '\0';
        std::string name = hnam;
        strstrip(name);
        return name;
    }
    else if (ParamName == "long_name"){
        long icomp = 1L;
        char hnam[13], hn80[81], hcasn[13];
        NAMEdll(&icomp, hnam, hn80, hcasn, 12, 80, 12);
        hn80[80] = '\0';
        std::string n80 = hn80;
        strstrip(n80);
        return n80;
    }
    else{
        throw ValueError(format("parameter to fluid_param_string is invalid: %s", ParamName.c_str()));
    }
};
long REFPROPMixtureBackend::match_CAS(const std::string &CAS){
    for (long icomp = 1L; icomp <= static_cast<long>(fluid_names.size()); ++icomp){
        char hnam[13], hn80[81], hcasn[13];
        NAMEdll(&icomp, hnam, hn80, hcasn, 12, 80, 12);
        hcasn[12] = '\0';
        std::string casn = hcasn;
        strstrip(casn);
        if (casn == CAS){
            return icomp;
        }
    }
    throw ValueError(format("Unable to match CAS number [%s]", CAS.c_str()));
}
/// Set binary mixture floating point parameter
void REFPROPMixtureBackend::set_binary_interaction_double(const std::string &CAS1, const std::string &CAS2, const std::string &parameter, const double value){
    std::size_t i = match_CAS(CAS1)-1, j = match_CAS(CAS2)-1;
    return set_binary_interaction_double(i, j, parameter, value);
};
/// Get binary mixture double value
double REFPROPMixtureBackend::get_binary_interaction_double(const std::string &CAS1, const std::string &CAS2, const std::string &parameter){
    std::size_t i = match_CAS(CAS1)-1, j = match_CAS(CAS2)-1;
    return get_binary_interaction_double(i, j, parameter);
}
/// Get binary mixture string value
std::string REFPROPMixtureBackend::get_binary_interaction_string(const std::string &CAS1, const std::string &CAS2, const std::string &parameter){
        
    long icomp, jcomp;
    char hmodij[4], hfmix[255], hbinp[255], hfij[255], hmxrul[255];
    double fij[6];
    
    icomp = match_CAS(CAS1);
    jcomp = match_CAS(CAS2);
    
    // Get the current state
    GETKTVdll(&icomp, &jcomp, hmodij, fij, hfmix, hfij, hbinp, hmxrul, 3, 255, 255, 255, 255);
    
    std::string shmodij(hmodij);
    if (shmodij.find("KW")==0 || shmodij.find("GE")==0)// Starts with KW or GE
    {
        if (parameter == "model"){ 
            return shmodij;
        }
        else {
            throw ValueError(format(" I don't know what to do with your parameter [%s]", parameter.c_str()));
            return "";
        }
    }
    else {
        //throw ValueError(format("For now, model [%s] must start with KW or GE", hmodij));
        return "";
    }
}
/// Set binary mixture string value
void REFPROPMixtureBackend::set_binary_interaction_string(const std::size_t i, const std::size_t j, const std::string &parameter, const std::string &value) {

    long icomp = static_cast<long>(i) + 1, jcomp = static_cast<long>(j) + 1, ierr = 0L;
    char hmodij[4], hfmix[255], hbinp[255], hfij[255], hmxrul[255];
    double fij[6];
    char herr[255];

    // Get the current state
    GETKTVdll(&icomp, &jcomp, hmodij, fij, hfmix, hfij, hbinp, hmxrul, 3, 255, 255, 255, 255);

    if (parameter == "model") {
        if (value.length()>4) { 
            throw ValueError(format("Model parameter (%s) is longer than 4 characters.", value)); 
        } else {
            strcpy(hmodij, value.c_str());
        }
    }
    else {
        throw ValueError(format("I don't know what to do with your parameter [%s]", parameter.c_str()));
    }
    SETKTVdll(&icomp, &jcomp, hmodij, fij, hfmix, &ierr, herr, 3, 255, 255);
    if (ierr > 0L){
        throw ValueError(format("Unable to set parameter[%s] to value[%s]: %s",parameter.c_str(),value.c_str(),herr));
    }
}
/// Set binary mixture string parameter (EXPERT USE ONLY!!!)
void REFPROPMixtureBackend::set_binary_interaction_double(const std::size_t i, const std::size_t j, const std::string &parameter, const double value){
    long icomp = static_cast<long>(i)+1, jcomp = static_cast<long>(j)+1, ierr = 0L;
    char hmodij[4], hfmix[255], hbinp[255], hfij[255], hmxrul[255];
    double fij[6];
    char herr[255];
    
    // Get the prior state
    GETKTVdll(&icomp, &jcomp, hmodij, fij, hfmix, hfij, hbinp, hmxrul, 3, 255, 255, 255, 255);
    
    std::string shmodij(hmodij);
    if (shmodij.find("KW")==0 || shmodij.find("GE")==0)// Starts with KW or GE
    {
        if (parameter == "betaT"){ fij[0] = value;}
        else if (parameter == "gammaT"){ fij[1] = value; }
        else if (parameter == "betaV"){ fij[2] = value; }
        else if (parameter == "gammaV"){ fij[3] = value; }
        else if (parameter == "Fij"){ fij[4] = value; }
        else{
            throw ValueError(format("I don't know what to do with your parameter [%s]", parameter.c_str()));
        }
        SETKTVdll(&icomp, &jcomp, hmodij, fij, hfmix, &ierr, herr, 3, 255, 255);
        if (ierr > 0L){
            throw ValueError(format("Unable to set parameter[%s] to value[%g]: %s",parameter.c_str(),value,herr));
        }
    }
    else{
        throw ValueError(format("For now, model [%s] must start with KW or GE", hmodij));
    }
}

/// Get binary mixture double value (EXPERT USE ONLY!!!)
double REFPROPMixtureBackend::get_binary_interaction_double(const std::size_t i, const std::size_t j, const std::string &parameter){
    long icomp = static_cast<long>(i)+1, jcomp = static_cast<long>(j)+1;
    char hmodij[4], hfmix[255], hbinp[255], hfij[255], hmxrul[255];
    double fij[6];
    
    // Get the current state
    GETKTVdll(&icomp, &jcomp, hmodij, fij, hfmix, hfij, hbinp, hmxrul, 3, 255, 255, 255, 255);
    
    std::string shmodij(hmodij);
    if (shmodij.find("KW")==0 || shmodij.find("GE")==0)// Starts with KW or GE
    {
        double val;
        if (parameter == "betaT"){ val = fij[0];}
        else if (parameter == "gammaT"){ val = fij[1]; }
        else if (parameter == "betaV"){ val = fij[2]; }
        else if (parameter == "gammaV"){ val = fij[3]; }
        else if (parameter == "Fij"){ val = fij[4]; }
        else{
            throw ValueError(format(" I don't know what to do with your parameter [%s]", parameter.c_str()));
            return _HUGE;
        }
        return val;
    }
    else{
        //throw ValueError(format("For now, model [%s] must start with KW or GE", hmodij));
        return _HUGE;
    }
}
void REFPROPMixtureBackend::set_mole_fractions(const std::vector<CoolPropDbl> &mole_fractions)
{
    if (mole_fractions.size() != this->Ncomp)
    {
        throw ValueError(format("Size of mole fraction vector [%d] does not equal that of component vector [%d]", mole_fractions.size(), this->Ncomp));
    }
    this->mole_fractions = std::vector<CoolPropDbl>(ncmax, 0.0);
    for (std::size_t i = 0; i < mole_fractions.size(); ++i){
        this->mole_fractions[i] = static_cast<double>(mole_fractions[i]);
    }
    this->mole_fractions_long_double = mole_fractions; // same size as Ncomp
    _mole_fractions_set = true;
    _reducing.fill(_HUGE);
}
void REFPROPMixtureBackend::set_mass_fractions(const std::vector<CoolPropDbl> &mass_fractions)
{
{'fraction_non_alphanumeric': 0.11731636170776982, 'fraction_numerical': 0.014673384601785557, 'mean_word_length': 2.4276192207477427, 'pattern_counts': {'":': 0, '<': 133, '<?xml version=': 0, '>': 123, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5003796', 'n_tokens_mistral': 18576, 'n_tokens_neox': 17376, 'n_words': 4417}