text: #if defined(_MSC_VER)
  #ifndef _CRTDBG_MAP_ALLOC
  #define _CRTDBG_MAP_ALLOC
  #endif
  #ifndef _CRT_SECURE_NO_WARNINGS
  #define _CRT_SECURE_NO_WARNINGS
  #endif
  #include <crtdbg.h>
  #endif
  
  #include "CoolProp.h"
  #include "AbstractState.h"
  
  #if defined(__ISWINDOWS__)
  #include <windows.h>
  #ifdef min
  #undef min
  #endif
  #ifdef max
  #undef max
  #endif
  #else
  #ifndef DBL_EPSILON
      #include <limits>
      #define DBL_EPSILON std::numeric_limits<double>::epsilon()
  #endif
  #endif
  
  #include <memory>
  
  #include <iostream>
  #include <stdlib.h>
  #include <vector>
  #include <exception>
  #include <stdio.h>
  #include <string>
  #include <locale>
  #include "CoolPropTools.h"
  #include "Solvers.h"
  #include "MatrixMath.h"
  #include "Backends/Helmholtz/Fluids/FluidLibrary.h"
  #include "Backends/Incompressible/IncompressibleLibrary.h"
  #include "Backends/Incompressible/IncompressibleBackend.h"
  #include "Backends/Helmholtz/HelmholtzEOSBackend.h"
  #include "Backends/Helmholtz/MixtureParameters.h"
  #include "DataStructures.h"
  #include "Backends/REFPROP/REFPROPMixtureBackend.h"
  #include "Backends/Cubics/CubicsLibrary.h"
  #include "Backends/PCSAFT/PCSAFTLibrary.h"
  
  #if defined(ENABLE_CATCH)
      #include "catch.hpp"
  #endif
  
  namespace CoolProp
  {
  
  static int debug_level = 0;
  static std::string error_string;
  static std::string warning_string;
  
  void set_debug_level(int level){debug_level = level;}
  int get_debug_level(void){return debug_level;}
  
  //// This is very hacky, but pull the git revision from the file
  #include "gitrevision.h" // Contents are like "std::string gitrevision = "aa121435436ggregrea4t43t433";"
  #include "cpversion.h" // Contents are like "char version [] = "2.5";"
  
  void set_warning_string(const std::string &warning){
      warning_string = warning;
  }
  void set_error_string(const std::string &error){
      error_string = error;
  }
  
  // Return true if the string has "BACKEND::*" format where * signifies a wildcard
  bool has_backend_in_string(const std::string &fluid_string, std::size_t &i)
  {
      i = fluid_string.find("::");
      return i != std::string::npos;
  }
  
  void extract_backend(std::string fluid_string, std::string &backend, std::string &fluid)
  {
      std::size_t i;
      // For backwards compatibility reasons, if "REFPROP-" or "REFPROP-MIX:" start
      // the fluid_string, replace them with "REFPROP::"
      if (fluid_string.find("REFPROP-MIX:") == 0)
      {
          fluid_string.replace(0, 12, "REFPROP::");
      }
      if (fluid_string.find("REFPROP-") == 0)
      {
          fluid_string.replace(0, 8, "REFPROP::");
      }
      if (has_backend_in_string(fluid_string, i))
      {
          // Part without the ::
          backend = fluid_string.substr(0, i);
          // Fluid name after the ::
          fluid = fluid_string.substr(i+2);
      }
      else
      {
          backend = "?";
          fluid = fluid_string;
      }
      if (get_debug_level()>10) std::cout << format("%s:%d: backend extracted. backend: %s. fluid: %s\n",__FILE__,__LINE__, backend.c_str(), fluid.c_str());
  }
  
  bool has_fractions_in_string(const std::string &fluid_string)
  {
      // If can find both "[" and "]", it must have mole fractions encoded as string
      return (fluid_string.find("[")!=std::string::npos && fluid_string.find("]")!=std::string::npos);
  }
  bool has_solution_concentration(const std::string &fluid_string)
  {
      // If can find "-", expect mass fractions encoded as string
      return (fluid_string.find('-') != std::string::npos && fluid_string.find('%') != std::string::npos);
  }
  
  struct delim : std::numpunct<char> {
      char m_c;
      delim(char c): m_c(c) {};
      char do_decimal_point() const { return m_c; }
  };
  
  std::string extract_fractions(const std::string &fluid_string, std::vector<double> &fractions)
  {
  
      if (has_fractions_in_string(fluid_string))
      {
          fractions.clear();
          std::vector<std::string> names;
  
          // Break up into pairs - like "Ethane[0.5]&Methane[0.5]" -> ("Ethane[0.5]","Methane[0.5]")
          std::vector<std::string> pairs = strsplit(fluid_string, '&');
  
          for (std::size_t i = 0; i < pairs.size(); ++i)
          {
              const std::string &fluid = pairs[i];
  
              // Must end with ']'
              if (fluid[fluid.size()-1] != ']')
                  throw ValueError(format("Fluid entry [%s] must end with ']' character",pairs[i].c_str()));
  
              // Split at '[', but first remove the ']' from the end by taking a substring
              std::vector<std::string> name_fraction = strsplit(fluid.substr(0, fluid.size()-1), '[');
  
              if (name_fraction.size() != 2){throw ValueError(format("Could not break [%s] into name/fraction", fluid.substr(0, fluid.size()-1).c_str()));}
  
              // Convert fraction to a double
              const std::string &name = name_fraction[0], &fraction = name_fraction[1];
              // The default locale for conversion from string to double is en_US with . as the deliminter
              // Good: 0.1234 Bad: 0,1234
              // But you can change the punctuation character for fraction parsing
              // with the configuration variable FLOAT_PUNCTUATION to change the locale to something more convenient for you (e.g., a ',')
              // See also http://en.cppreference.com/w/cpp/locale/numpunct/decimal_point
              std::stringstream ssfraction(fraction);
              char c = get_config_string(FLOAT_PUNCTUATION)[0];
              ssfraction.imbue(std::locale(ssfraction.getloc(), new delim(c)));
              double f;
              ssfraction >> f;
              if (ssfraction.rdbuf()->in_avail() != 0){
                  throw ValueError(format("fraction [%s] was not converted fully", fraction.c_str()));
              }
  
              if (f > 1 || f < 0){
                  throw ValueError(format("fraction [%s] was not converted to a value between 0 and 1 inclusive", fraction.c_str()));
              }
  
              if ((f > 10*DBL_EPSILON) ||  // Only push component if fraction is positive and non-zero
                  (pairs.size() == 1))     // ..or if there is only one fluid (i.e. INCOMP backend )
              {
                  // And add to vector
                  fractions.push_back(f);
  
                  // Add name
                  names.push_back(name);
              }
          }
  
          if (get_debug_level()>10) std::cout << format("%s:%d: Detected fractions of %s for %s.",__FILE__,__LINE__,vec_to_string(fractions).c_str(), (strjoin(names, "&")).c_str());
          // Join fluids back together
          return strjoin(names, "&");
      }
      else if (has_solution_concentration(fluid_string))
      {
          fractions.clear();
          double x;
  
          std::vector<std::string> fluid_parts = strsplit(fluid_string,'-');
          // Check it worked
          if (fluid_parts.size() != 2){
              throw ValueError(format("Format of incompressible solution string [%s] is invalid, should be like \"EG-20%\" or \"EG-0.2\" ", fluid_string.c_str()) );
          }
  
          // Convert the concentration into a string
          char* pEnd;
          x = strtod(fluid_parts[1].c_str(), &pEnd);
  
          // Check if per cent or fraction syntax is used
          if (!strcmp(pEnd,"%")){    x *= 0.01;}
          fractions.push_back(x);
          if (get_debug_level()>10) std::cout << format("%s:%d: Detected incompressible concentration of %s for %s.",__FILE__,__LINE__,vec_to_string(fractions).c_str(), fluid_parts[0].c_str());
          return fluid_parts[0];
      }
      else
      {
          return fluid_string;
      }
  }
  
  void _PropsSI_initialize(const std::string &backend,
                           const std::vector<std::string> &fluid_names,
                           const std::vector<double> &z,
                           shared_ptr<AbstractState> &State){
  
      if (fluid_names.empty()){throw ValueError("fluid_names cannot be empty");}
  
      std::vector<double> fractions(1, 1.0); // Default to one component, unity fraction
      const std::vector<double> *fractions_ptr = NULL; // Pointer to the array to be used;
  
      if (fluid_names.size() > 1){
          // Set the pointer - we are going to use the supplied fractions; they must be provided
          fractions_ptr = &z;
          // Reset the state
          State.reset(AbstractState::factory(backend, fluid_names));
      }
      else if (fluid_names.size() == 1){
          if (has_fractions_in_string(fluid_names[0]) || has_solution_concentration(fluid_names[0])){
              // Extract fractions from the string
              std::string fluid_string = extract_fractions(fluid_names[0], fractions);
              // Set the pointer - we are going to use the extracted fractions
              fractions_ptr = &fractions;
              // Reset the state
              State.reset(AbstractState::factory(backend, fluid_string));
          }
          else{
              if (z.empty()){
                  // Set the pointer - we are going to use the default fractions
                  fractions_ptr = &fractions;
              }
              else{
                  // Set the pointer - we are going to use the provided fractions
                  fractions_ptr = &z;
              }
              // Reset the state
              State.reset(AbstractState::factory(backend, fluid_names));
          }
      }
      else { // The only path where fractions_ptr stays NULL
        throw ValueError("fractions_ptr is NULL");
      }
      if (!State->available_in_high_level()){
          throw ValueError("This AbstractState derived class cannot be used in the high-level interface; see www.coolprop.org/dev/coolprop/LowLevelAPI.html");
      }
  
      // Set the fraction for the state
      if (State->using_mole_fractions()){
          // If a predefined mixture or a pure fluid, the fractions will already be set
          if (State->get_mole_fractions().empty()){
              State->set_mole_fractions(*fractions_ptr);
          }
      } else if (State->using_mass_fractions()){
          State->set_mass_fractions(*fractions_ptr);
      } else if (State->using_volu_fractions()){
          State->set_volu_fractions(*fractions_ptr);
      } else {
          if (get_debug_level()>50) std::cout << format("%s:%d: _PropsSI, could not set composition to %s, defaulting to mole fraction.\n",__FILE__,__LINE__, vec_to_string(z).c_str()).c_str();
      }
  }
  
  struct output_parameter{
  	enum OutputParametersType {OUTPUT_TYPE_UNSET = 0, OUTPUT_TYPE_TRIVIAL, OUTPUT_TYPE_NORMAL, OUTPUT_TYPE_FIRST_DERIVATIVE, OUTPUT_TYPE_FIRST_SATURATION_DERIVATIVE, OUTPUT_TYPE_SECOND_DERIVATIVE};
  	CoolProp::parameters Of1, Wrt1, Constant1, Wrt2, Constant2;
  	OutputParametersType type;
      /// Parse a '&' separated string into a data structure with one entry per output
      /// Covers both normal and derivative outputs
      static std::vector<output_parameter> get_output_parameters(const std::vector<std::string> &Outputs){
          std::vector<output_parameter> outputs;
          for (std::vector<std::string>::const_iterator str = Outputs.begin(); str != Outputs.end(); ++str){
              output_parameter out;
              CoolProp::parameters iOutput;
              if (is_valid_parameter(*str, iOutput)){
                  out.Of1 = iOutput;
                  if (is_trivial_parameter(iOutput)){ out.type = OUTPUT_TYPE_TRIVIAL; }
                  else{ out.type = OUTPUT_TYPE_NORMAL; }
              }
              else if (is_valid_first_saturation_derivative(*str, out.Of1, out.Wrt1)){
                  out.type = OUTPUT_TYPE_FIRST_SATURATION_DERIVATIVE;
              }
              else if (is_valid_first_derivative(*str, out.Of1, out.Wrt1, out.Constant1)){
                  out.type = OUTPUT_TYPE_FIRST_DERIVATIVE;
              }
              else if (is_valid_second_derivative(*str, out.Of1, out.Wrt1, out.Constant1, out.Wrt2, out.Constant2)){
                  out.type = OUTPUT_TYPE_SECOND_DERIVATIVE;
              }
              else{
                  throw ValueError(format("Output string is invalid [%s]", str->c_str()));
              }
              outputs.push_back(out);
          }
          return outputs;
      };
  };
  
  void _PropsSI_outputs(shared_ptr<AbstractState> &State,
  	     			 const std::vector<output_parameter> &output_parameters,
  		    		 CoolProp::input_pairs input_pair,
  			    	 const std::vector<double> &in1,
  			    	 const std::vector<double> &in2,
  			    	 std::vector<std::vector<double> > &IO){
  
  	// Check the inputs
  	if (in1.size() != in2.size()){ throw ValueError(format("lengths of in1 [%d] and in2 [%d] are not the same", in1.size(), in2.size()));}
  	bool one_input_one_output = (in1.size() == 1 && in2.size() == 1 && output_parameters.size() == 1);
      // If all trivial outputs, never do a state update
      bool all_trivial_outputs = true;
      for (std::size_t j = 0; j < output_parameters.size(); ++j){
          if (output_parameters[j].type != output_parameter::OUTPUT_TYPE_TRIVIAL){
              all_trivial_outputs = false;
          }
      }
      parameters p1, p2;
      // If all outputs are also inputs, never do a state update
      bool all_outputs_in_inputs = true;
      if (input_pair != INPUT_PAIR_INVALID){
          // Split the input pair into parameters
          split_input_pair(input_pair, p1, p2);
          // See if each parameter is in the output vector and is a normal type input
          for (std::size_t j = 0; j < output_parameters.size(); ++j){
              if (output_parameters[j].type != output_parameter::OUTPUT_TYPE_NORMAL){
                  all_outputs_in_inputs = false; break;
              }
              if (!(output_parameters[j].Of1 == p1 || output_parameters[j].Of1 == p2)){
                  all_outputs_in_inputs = false; break;
              }
          }
      }
      else{
          if (!all_trivial_outputs){
              throw ValueError(format("Input pair variable is invalid and output(s) are non-trivial; cannot do state update"));
          }
          all_outputs_in_inputs = false;
      }
  
  	if (get_debug_level() > 100)
  	{
  	   std::cout << format("%s (%d): input pair = %d ",__FILE__,__LINE__, input_pair)          << std::endl;
  	   std::cout << format("%s (%d): in1 = %s ",__FILE__,__LINE__, vec_to_string(in1).c_str()) << std::endl;
  	   std::cout << format("%s (%d): in2 = %s ",__FILE__,__LINE__, vec_to_string(in2).c_str()) << std::endl;
  	}
  
      // Get configuration variable for line tracing, see #1443
      const bool use_guesses = get_config_bool(USE_GUESSES_IN_PROPSSI);
      GuessesStructure guesses;
  
  	// Resize the output matrix
      std::size_t N1 = std::max(static_cast<std::size_t>(1), in1.size());
      std::size_t N2 = std::max(static_cast<std::size_t>(1), output_parameters.size());
  	  IO.resize(N1, std::vector<double>(N2, _HUGE));
  
      // Throw an error if at the end, there were no successes
      bool success = false;
      bool success_inner = false;
  
      if (get_debug_level() > 100)
      {
          std::cout << format("%s (%d): Iterating over %d input value pairs.",__FILE__,__LINE__,IO.size()) << std::endl;
      }
  
  	// Iterate over the state variable inputs
  	for (std::size_t i = 0; i < IO.size(); ++i){
          // Reset the success indicator for the current state point
          success_inner = false;
  		try{
              if (input_pair != INPUT_PAIR_INVALID && !all_trivial_outputs && !all_outputs_in_inputs){
                  // Update the state since it is a valid set of inputs
                  if (!use_guesses || i == 0) {
                      State->update(input_pair, in1[i], in2[i]);
                  } else {
                      State->update_with_guesses(input_pair, in1[i], in2[i], guesses);
                      guesses.clear();
                  }
              }
          }
          catch(...){
              if (one_input_one_output){IO.clear(); throw;} // Re-raise the exception since we want to bubble the error
              // All the outputs are filled with _HUGE; go to next input
              for (std::size_t j = 0; j < IO[i].size(); ++j){ IO[i][j] = _HUGE; }
              continue;
          }
  
          for (std::size_t j = 0; j < IO[i].size(); ++j){
              // If all the outputs are inputs, there is no need for a state input
              if (all_outputs_in_inputs){
                  if (p1 == output_parameters[j].Of1){
                      IO[i][j] = in1[i]; success_inner = true; continue;
                  }
                  else if (p2 == output_parameters[j].Of1){
                      IO[i][j] = in2[i]; success_inner = true; continue;
                  }
                  else{
                      throw ValueError();
                  }
              }
              try{
                  const output_parameter &output = output_parameters[j];
                  switch (output.type){
                      case output_parameter::OUTPUT_TYPE_TRIVIAL:
                      case output_parameter::OUTPUT_TYPE_NORMAL:
                          IO[i][j] = State->keyed_output(output.Of1);
                          if (use_guesses) {
                              switch (output.Of1) {
                              case iDmolar: guesses.rhomolar = IO[i][j]; break;
                              case iT: guesses.T = IO[i][j]; break;
                              case iP: guesses.p = IO[i][j]; break;
                              case iHmolar: guesses.hmolar = IO[i][j]; break;
                              case iSmolar: guesses.smolar = IO[i][j]; break;
                              default: throw ValueError("Don't understand this parameter");
                              }
                          }
                          break;
                      case output_parameter::OUTPUT_TYPE_FIRST_DERIVATIVE:
                          IO[i][j] = State->first_partial_deriv(output.Of1, output.Wrt1, output.Constant1); break;
                      case output_parameter::OUTPUT_TYPE_FIRST_SATURATION_DERIVATIVE:
                          IO[i][j] = State->first_saturation_deriv(output.Of1, output.Wrt1); break;
                      case output_parameter::OUTPUT_TYPE_SECOND_DERIVATIVE:
                          IO[i][j] = State->second_partial_deriv(output.Of1, output.Wrt1, output.Constant1, output.Wrt2, output.Constant2); break;
                      default:
                          throw ValueError(format("")); break;
                  }
                  // At least one has succeeded
                  success_inner = true;
              }
              catch(...){
                  if (one_input_one_output){IO.clear(); throw;} // Re-raise the exception since we want to bubble the error
                  IO[i][j] = _HUGE;
              }
          }
          // We want to have at least rhomolar and T, but we do not raise errors here
          if (use_guesses && success_inner) {
              if (!ValidNumber(guesses.rhomolar)) {
                  try { guesses.rhomolar = State->rhomolar(); }
                  catch (...) { guesses.rhomolar = _HUGE; }
              }
              if (!ValidNumber(guesses.T)) {
                  try { guesses.T = State->T(); }
                  catch (...) { guesses.T = _HUGE; }
              }
          }
          // Save the success indicator, just a single valid output is enough
          success |= success_inner;
  	}
      if (success == false) { IO.clear(); throw ValueError(format("No outputs were able to be calculated"));}
  }
  
  
  bool StripPhase(std::string &Name, shared_ptr<AbstractState> &State)
  // Parses an imposed phase out of the Input Name string using the "|" delimiter
  {
      std::vector<std::string> strVec = strsplit(Name, '|');    // Split input key string in to vector containing input key [0] and phase string [1]
      if (strVec.size() > 1) {                                  // If there is a phase string (contains "|" character)
          // Check for invalid backends for setting phase in PropsSI
          std::string strBackend = State->backend_name();
          if (strBackend == get_backend_string(INCOMP_BACKEND))
              throw ValueError("Cannot set phase on Incompressible Fluid; always liquid phase");   // incompressible fluids are always "liquid".
          if (strBackend == get_backend_string(IF97_BACKEND))
              throw ValueError("Can't set phase on IF97 Backend");                                 // IF97 has to calculate it's own phase region
          if (strBackend == get_backend_string(TTSE_BACKEND))
              throw ValueError("Can't set phase on TTSE Backend in PropsSI");                      // Shouldn't be calling from High-Level anyway
          if (strBackend == get_backend_string(BICUBIC_BACKEND))
              throw ValueError("Can't set phase on BICUBIC Backend in PropsSI");                   // Shouldn't be calling from High-Level anyway
          if (strBackend == get_backend_string(VTPR_BACKEND))
              throw ValueError("Can't set phase on VTPR Backend in PropsSI");                      // VTPR has no phase functions to call
  
          phases imposed = iphase_not_imposed;                  // Initialize imposed phase
          if (strVec.size() > 2)                                // If there's more than on phase separator, throw error
          {
              throw ValueError(format("Invalid phase format: \"%s\"", Name));
          }
          // Handle prefixes of iphase_, phase_, or <none>
          std::basic_string <char>::iterator str_Iter;
          std::string strPhase = strVec[1];                     //    Create a temp string so we can modify the prefix
          if (strPhase.find("iphase_") != strPhase.npos) { str_Iter = strPhase.erase(strPhase.begin()); }  // Change "iphase_" to "phase_"
          if (strPhase.find("phase_") == strPhase.npos) { strPhase.insert(0, "phase_"); }                  // Prefix with "phase_" if missing
          // See if phase is a valid phase string, updating imposed while we're at it...
          if ( !is_valid_phase(strPhase, imposed) )
          {
              throw ValueError(format("Phase string \"%s\" is not a valid phase", strVec[1]));    // throw error with original string if not valid
          }
          // Parsed phase string was valid
          Name = strVec[0];                                     //     Update input name to just the key string part
          State->specify_phase(imposed);                        //     Update the specified phase on the backend State
          return true;                                          //     Return true because a valid phase string was found
      }
      return false;                                             // Return false if there was no phase string on this key.
  }
  
  void _PropsSImulti(const std::vector<std::string> &Outputs,
                     const std::string &Name1,
                     const std::vector<double> &Prop1,
                     const std::string &Name2,
                     const std::vector<double> &Prop2,
                     const std::string &backend,
                     const std::vector<std::string> &fluids,
                     const std::vector<double> &fractions,
                     std::vector<std::vector<double> > &IO)
  {
      shared_ptr<AbstractState> State;
      CoolProp::parameters key1 = INVALID_PARAMETER, key2 = INVALID_PARAMETER;   // Initialize to invalid parameter values
      CoolProp::input_pairs input_pair = INPUT_PAIR_INVALID;                     // Initialize to invalid input pair
      std::vector<output_parameter> output_parameters;
      std::vector<double> v1, v2;
  
      try{
          // Initialize the State class
          _PropsSI_initialize(backend, fluids, fractions, State);
      }
      catch(std::exception &e){
          // Initialization failed.  Stop.
          throw ValueError(format("Initialize failed for backend: \"%s\", fluid: \"%s\" fractions \"%s\"; error: %s",backend.c_str(), strjoin(fluids,"&").c_str(), vec_to_string(fractions, "%0.10f").c_str(), e.what()) );
      }
  
      //strip any imposed phase from input key strings here
      std::string N1 = Name1;                  // Make Non-constant copy of Name1 that we can modify
      std::string N2 = Name2;                  // Make Non-constant copy of Name2 that we can modify
      bool HasPhase1 = StripPhase(N1, State);  // strip phase string from first name if needed
      bool HasPhase2 = StripPhase(N2, State);  // strip phase string from second name if needed
      if (HasPhase1 && HasPhase2)              // if both Names have a phase string, don't allow it.
              throw ValueError("Phase can only be specified on one of the input key strings");
  
      try{
          // Get update pair
          if (is_valid_parameter(N1, key1) && is_valid_parameter(N2, key2))
              input_pair = generate_update_pair(key1, Prop1, key2, Prop2, v1, v2);
      }
      catch (std::exception &e){
          // Input parameter parsing failed.  Stop
          throw ValueError(format("Input pair parsing failed for Name1: \"%s\", Name2: \"%s\"; err: %s", Name1.c_str(), Name2.c_str(), e.what()));
      }
  
      try{
          output_parameters = output_parameter::get_output_parameters(Outputs);
      }
      catch (std::exception &e){
          // Output parameter parsing failed.  Stop.
          throw ValueError(format("Output parameter parsing failed; error: %s", e.what()));
      }
  
      // Calculate the output(s).  In the case of a failure, all values will be filled with _HUGE
      _PropsSI_outputs(State, output_parameters, input_pair, v1, v2, IO);
  }
  
  std::vector<std::vector<double> > PropsSImulti(const std::vector<std::string> &Outputs,
                                                 const std::string &Name1,
                                                 const std::vector<double> &Prop1,
                                                 const std::string &Name2,
                                                 const std::vector<double> &Prop2,
                                                 const std::string &backend,
                                                 const std::vector<std::string> &fluids,
                                                 const std::vector<double> &fractions)
  {
      std::vector<std::vector<double> > IO;
  
      #if !defined(NO_ERROR_CATCHING)
      try{
      #endif
  
          // Call the subfunction that can bubble errors
          _PropsSImulti(Outputs, Name1, Prop1, Name2, Prop2, backend, fluids, fractions, IO);
  
          // Return the value(s)
          return IO;
  
      #if !defined(NO_ERROR_CATCHING)
      }
      catch(const std::exception& e){
          set_error_string(e.what());
          #if defined (PROPSSI_ERROR_STDOUT)
          std::cout << e.what() << std::endl;
          #endif
          if (get_debug_level() > 1){std::cout << e.what() << std::endl;}
      }
      catch(...){
      }
      #endif
      return std::vector<std::vector<double> >();
  }
  double PropsSI(const std::string &Output, const std::string &Name1, double Prop1, const std::string &Name2, double Prop2, const std::string &Ref)
  {
      #if !defined(NO_ERROR_CATCHING)
      try{
      #endif
  
          // BEGIN OF TRY
          // Here is the real code that is inside the try block
  
  
          std::string backend, fluid;
          extract_backend(Ref, backend, fluid);
          std::vector<double> fractions(1, 1.0);
          // extract_fractions checks for has_fractions_in_string / has_solution_concentration; no need to double check
          std::string fluid_string = extract_fractions(fluid, fractions);
          std::vector<std::vector<double> > IO;
          _PropsSImulti(strsplit(Output,'&'), Name1, std::vector<double>(1, Prop1), Name2, std::vector<double>(1, Prop2), backend, strsplit(fluid_string, '&'), fractions, IO);
          if (IO.empty()){ throw ValueError(get_global_param_string("errstring").c_str()); }
          if (IO.size()!= 1 || IO[0].size() != 1){ throw ValueError(format("output should be 1x1; error was %s", get_global_param_string("errstring").c_str())); }
  
          double val = IO[0][0];
  
          if (get_debug_level() > 1){ std::cout << format("_PropsSI will return %g",val) << std::endl; }
          return val;
          // END OF TRY
      #if !defined(NO_ERROR_CATCHING)
      }
      catch(const std::exception& e){
          set_error_string(e.what() + format(" : PropsSI(\"%s\",\"%s\",%0.10g,\"%s\",%0.10g,\"%s\")",Output.c_str(),Name1.c_str(), Prop1, Name2.c_str(), Prop2, Ref.c_str()));
          #if defined (PROPSSI_ERROR_STDOUT)
          std::cout << e.what() << std::endl;
          #endif
          if (get_debug_level() > 1){std::cout << e.what() << std::endl;}
          return _HUGE;
      }
      catch(...){
          return _HUGE;
      }
      #endif
  }
  
  bool add_fluids_as_JSON(const std::string &backend, const std::string &fluidstring)
  {
      if (backend == "SRK" || backend == "PR")
      {
          CubicLibrary::add_fluids_as_JSON(fluidstring); return true;
      }
      else if (backend == "HEOS")
      {
          JSONFluidLibrary::add_many(fluidstring); return true;
      }
      else if (backend == "PCSAFT")
      {
          PCSAFTLibrary::add_fluids_as_JSON(fluidstring); return true;
      }
      else{
          throw ValueError(format("You have provided an invalid backend [%s] to add_fluids_as_JSON; valid options are SRK, PR, HEOS",backend.c_str()));
      }
  }
  #if defined(ENABLE_CATCH)
  TEST_CASE("Check inputs to PropsSI","[PropsSI]")
  {
      SECTION("Single state, single output"){
          CHECK(ValidNumber(CoolProp::PropsSI("T","P",101325,"Q",0,"Water")));
      };
      SECTION("Single state, single output, saturation derivative"){
          CHECK(ValidNumber(CoolProp::PropsSI("d(P)/d(T)|sigma","P",101325,"Q",0,"Water")));
      };
      SECTION("Single state, single output, pure incompressible"){
          CHECK(ValidNumber(CoolProp::PropsSI("D","P",101325,"T",300,"INCOMP::DowQ")));
      };
      SECTION("Single state, trivial output, pure incompressible"){
          CHECK(ValidNumber(CoolProp::PropsSI("Tmin","P",0,"T",0,"INCOMP::DowQ")));
      };
      SECTION("Bad input pair"){
          CHECK(!ValidNumber(CoolProp::PropsSI("D","Q",0,"Q",0,"Water")));
      };
      SECTION("Single state, single output, 40% incompressible"){
          CHECK(ValidNumber(CoolProp::PropsSI("D","P",101325,"T",300,"INCOMP::MEG[0.40]")));
      };
      SECTION("Single state, single output, predefined CoolProp mixture"){
          CHECK(ValidNumber(CoolProp::PropsSI("T","Q",1,"P",3e6,"HEOS::R125[0.7]&R32[0.3]")));
      };
      SECTION("Single state, single output"){
          CHECK(ValidNumber(CoolProp::PropsSI("T","P",101325,"Q",0,"HEOS::Water")));
      };
      SECTION("Single state, single output, predefined mixture"){
          CHECK(ValidNumber(CoolProp::PropsSI("T","P",101325,"Q",0,"R410A.mix")));
      };
      SECTION("Single state, single output, predefined mixture from REFPROP"){
          CHECK(ValidNumber(CoolProp::PropsSI("T","P",101325,"Q",0,"REFPROP::R410A.MIX")));
      };
      SECTION("Single state, single output, bad predefined mixture from REFPROP"){
          CHECK(!ValidNumber(CoolProp::PropsSI("T","P",101325,"Q",0,"REFPROP::RRRRRR.mix")));
      };
      SECTION("Predefined mixture"){
          std::vector<double> p(1, 101325), Q(1, 1.0), z;
          std::vector<std::string> outputs(1,"T"); outputs.push_back("Dmolar");
          std::vector<std::vector<double> > IO;
          std::vector<std::string> fluids(1, "R410A.mix");
          CHECK_NOTHROW(IO = CoolProp::PropsSImulti(outputs,"P",p,"Q",Q,"HEOS",fluids,z));
      };
      SECTION("Single state, two outputs"){
          std::vector<double> p(1, 101325), Q(1, 1.0), z(1, 1.0);
          std::vector<std::string> outputs(1,"T"); outputs.push_back("Dmolar");
          std::vector<std::string> fluids(1, "Water");
          CHECK_NOTHROW(CoolProp::PropsSImulti(outputs,"P",p,"Q",Q,"HEOS",fluids,z));
      };
      SECTION("Single state, two bad outputs"){
          std::vector<double> p(1, 101325), Q(1, 1.0), z(1, 1.0);
          std::vector<std::vector<double> > IO;
          std::vector<std::string> outputs(1,"???????"); outputs.push_back("?????????");
          std::vector<std::string> fluids(1, "Water");
          CHECK_NOTHROW(IO = CoolProp::PropsSImulti(outputs,"P",p,"Q",Q,"HEOS",fluids,z));
          CHECK(IO.size() == 0);
      };
      SECTION("Two states, one output"){
          std::vector<double> p(2, 101325), Q(2, 1.0), z(1, 1.0);
          std::vector<std::string> outputs(1,"T");
          std::vector<std::string> fluids(1, "Water");
          CHECK_NOTHROW(CoolProp::PropsSImulti(outputs,"P",p,"Q",Q,"HEOS",fluids,z));
      };
      SECTION("Two states, two outputs"){
          std::vector<double> p(2, 101325), Q(2, 1.0), z(1, 1.0);
          std::vector<std::string> outputs(1,"T"); outputs.push_back("Dmolar");
          std::vector<std::string> fluids(1, "Water");
          CHECK_NOTHROW(CoolProp::PropsSImulti(outputs,"P",p,"Q",Q,"HEOS",fluids,z));
      };
      SECTION("cp and its derivative representation"){
          std::vector<double> p(1, 101325), Q(1, 1.0), z(1, 1.0);
          std::vector<std::vector<double> > IO;
          std::vector<std::string> outputs(1,"Cpmolar"); outputs.push_back("d(Hmolar)/d(T)|P");
          std::vector<std::string> fluids(1, "Water");
          CHECK_NOTHROW(IO = CoolProp::PropsSImulti(outputs,"P",p,"Q",Q,"HEOS",fluids,z));
          std::string errstring = get_global_param_string("errstring");
          CAPTURE(errstring);
          REQUIRE(!IO.empty());
          CAPTURE(IO[0][0]);
          CAPTURE(IO[0][1]);
          CHECK(std::abs(IO[0][0] - IO[0][1]) < 1e-5);
      };
      SECTION("bad fluid"){
          std::vector<double> p(1, 101325), Q(1, 1.0), z(1, 1.0);
          std::vector<std::vector<double> > IO;
          std::vector<std::string> outputs(1,"Cpmolar"); outputs.push_back("d(Hmolar)/d(T)|P");
          std::vector<std::string> fluids(1, "????????");
          CHECK_NOTHROW(IO = CoolProp::PropsSImulti(outputs,"P",p,"Q",Q,"HEOS",fluids,z));
          std::string errstring = get_global_param_string("errstring");
          CAPTURE(errstring);
          REQUIRE(IO.empty());
      };
      SECTION("bad mole fraction length"){
          std::vector<double> p(1, 101325), Q(1, 1.0), z(1, 1.0);
          std::vector<std::vector<double> > IO;
          std::vector<std::string> outputs(1,"T");
          std::vector<std::string> fluids(1, "Water&Ethanol");
          CHECK_NOTHROW(IO = CoolProp::PropsSImulti(outputs,"P",p,"Q",Q,"HEOS",fluids,z));
          std::string errstring = get_global_param_string("errstring");
          CAPTURE(errstring);
          REQUIRE(IO.empty());
      };
      SECTION("bad input lengths"){
          std::vector<double> p(1, 101325), Q(2, 1.0), z(100, 1.0);
          std::vector<std::vector<double> > IO;
          std::vector<std::string> outputs(1,"Cpmolar"); outputs.push_back("d(Hmolar)/d(T)|P");
          std::vector<std::string> fluids(1, "Water");
          CHECK_NOTHROW(IO = CoolProp::PropsSImulti(outputs,"P",p,"Q",Q,"HEOS",fluids,z));
          std::string errstring = get_global_param_string("errstring");
          CAPTURE(errstring);
          REQUIRE(IO.empty());
      };
      SECTION("bad input pair"){
          std::vector<double> Q(2, 1.0), z(1, 1.0);
          std::vector<std::vector<double> > IO;
          std::vector<std::string> outputs(1,"Cpmolar"); outputs.push_back("d(Hmolar)/d(T)|P");
          std::vector<std::string> fluids(1, "Water");
          CHECK_NOTHROW(IO = CoolProp::PropsSImulti(outputs,"Q",Q,"Q",Q,"HEOS",fluids,z));
          std::string errstring = get_global_param_string("errstring");
          CAPTURE(errstring);
          REQUIRE(IO.empty());
      };
  };
  #endif
  
  /****************************************************
   *                  Props1SI                        *
   ****************************************************/
  
  double Props1SI(std::string FluidName, std::string Output)
  {
      bool valid_fluid1 = is_valid_fluid_string(FluidName);
      bool valid_fluid2 = is_valid_fluid_string(Output);
      if (valid_fluid1 && valid_fluid2){
          set_error_string(format("Both inputs to Props1SI [%s,%s] are valid fluids", Output.c_str(), FluidName.c_str()));
          return _HUGE;
      }
      if (!valid_fluid1 && !valid_fluid2){
          set_error_string(format("Neither input to Props1SI [%s,%s] is a valid fluid", Output.c_str(), FluidName.c_str()));
          return _HUGE;
      }
      if (!valid_fluid1 && valid_fluid2){
          // They are backwards, swap
          std::swap(Output, FluidName);
      }
  
      // First input is the fluid, second input is the input parameter
      double val1 = PropsSI(Output, "", 0, "", 0, FluidName);
      if (!ValidNumber(val1)){
          set_error_string(format("Unable to use input parameter [%s] in Props1SI for fluid %s; error was %s", Output.c_str(), FluidName.c_str(), get_global_param_string("errstring").c_str()));
          return _HUGE;
      }
      else{
          return val1;
      }
  }
  #if defined(ENABLE_CATCH)
  TEST_CASE("Check inputs to Props1SI","[Props1SI],[PropsSI]")
  {
      SECTION("Good fluid, good parameter"){
          CHECK(ValidNumber(CoolProp::Props1SI("Tcrit","Water")));
      };
      SECTION("Good fluid, good parameter"){
          CHECK(ValidNumber(CoolProp::PropsSI("Tcrit","",0,"",0,"Water")));
      };
      SECTION("Good fluid, good parameter, inverted"){
          CHECK(ValidNumber(CoolProp::Props1SI("Water","Tcrit")));
      };
      SECTION("Good fluid, bad parameter"){
          CHECK(!ValidNumber(CoolProp::Props1SI("Water","????????????")));
      };
      SECTION("Bad fluid, good parameter"){
          CHECK(!ValidNumber(CoolProp::Props1SI("?????","Tcrit")));
      };
  };
  #endif
  
  
  bool is_valid_fluid_string(const std::string &input_fluid_string)
  {
      try{
          std::string backend, fluid;
          std::vector<double> fractions;
          // First try to extract backend and fractions
          extract_backend(input_fluid_string, backend, fluid);
          std::string fluid_string = extract_fractions(fluid, fractions);
          // We are going to let the factory function load the state
          shared_ptr<AbstractState> State(AbstractState::factory(backend, fluid_string));
          return true;
      }
      catch (...){
          return false;
      }
  }
  double saturation_ancillary(const std::string &fluid_name, const std::string &output, int Q, const std::string &input, double value){
  
      // Generate the state instance
      std::vector<std::string> names(1, fluid_name);
      CoolProp::HelmholtzEOSMixtureBackend HEOS(names);
  
      parameters iInput = get_parameter_index(input);
      parameters iOutput = get_parameter_index(output);
  
      return HEOS.saturation_ancillary(iOutput, Q, iInput, value);
  }
  void set_reference_stateS(const std::string &fluid_string, const std::string &reference_state)
  {
      std::string backend, fluid;
      extract_backend(fluid_string, backend, fluid);
      if (backend == "REFPROP"){
  
          int ierr = 0, ixflag = 1;
          double h0 = 0, s0 = 0, t0 = 0, p0 = 0;
          char herr[255], hrf[4];
          double x0[1] = {1};
          const char * refstate = reference_state.c_str();
          if (strlen(refstate) > 3){
              if (reference_state == "ASHRAE"){
                  strcpy(hrf, "ASH");
              }
              else{
                  throw ValueError(format("Reference state string [%s] is more than 3 characters long", reference_state.c_str()));
              }
          }
          else{
              strcpy(hrf, refstate);
          }
          REFPROP_SETREF(hrf, ixflag, x0, h0, s0, t0, p0, ierr, herr, 3, 255);
      }
      else if (backend == "HEOS" || backend == "?"){
          CoolProp::HelmholtzEOSMixtureBackend HEOS(std::vector<std::string>(1, fluid));
          if (!reference_state.compare("IIR"))
          {
              if (HEOS.Ttriple() > 273.15){
                  throw ValueError(format("Cannot use IIR reference state; Ttriple [%Lg] is greater than 273.15 K",HEOS.Ttriple()));
              }
              HEOS.update(QT_INPUTS, 0, 273.15);
  
              // Get current values for the enthalpy and entropy
              double deltah = HEOS.hmass() - 200000; // offset from 200000 J/kg enthalpy
              double deltas = HEOS.smass() - 1000; // offset from 1000 J/kg/K entropy
              double delta_a1 = deltas/(HEOS.gas_constant()/HEOS.molar_mass());
              double delta_a2 = -deltah/(HEOS.gas_constant()/HEOS.molar_mass()*HEOS.get_reducing_state().T);
              // Change the value in the library for the given fluid
              set_fluid_enthalpy_entropy_offset(fluid, delta_a1, delta_a2, "IIR");
              if (get_debug_level() > 0){
                  std::cout << format("set offsets to %0.15g and %0.15g\n", delta_a1, delta_a2);
              }
          }
          else if (!reference_state.compare("ASHRAE"))
          {
              if (HEOS.Ttriple() > 233.15){
                  throw ValueError(format("Cannot use ASHRAE reference state; Ttriple [%Lg] is greater than than 233.15 K", HEOS.Ttriple()));
              }
              HEOS.update(QT_INPUTS, 0, 233.15);
  
              // Get current values for the enthalpy and entropy
              double deltah = HEOS.hmass() - 0; // offset from 0 J/kg enthalpy
              double deltas = HEOS.smass() - 0; // offset from 0 J/kg/K entropy
              double delta_a1 = deltas/(HEOS.gas_constant()/HEOS.molar_mass());
              double delta_a2 = -deltah/(HEOS.gas_constant()/HEOS.molar_mass()*HEOS.get_reducing_state().T);
              // Change the value in the library for the given fluid
              set_fluid_enthalpy_entropy_offset(fluid, delta_a1, delta_a2, "ASHRAE");
              if (get_debug_level() > 0){
                  std::cout << format("set offsets to %0.15g and %0.15g\n", delta_a1, delta_a2);
              }
          }
          else if (!reference_state.compare("NBP"))
          {
              if (HEOS.p_triple() > 101325){
                  throw ValueError(format("Cannot use NBP reference state; p_triple [%Lg Pa] is greater than than 101325 Pa",HEOS.p_triple()));
              }
              // Saturated liquid boiling point at 1 atmosphere
              HEOS.update(PQ_INPUTS, 101325, 0);
  
              double deltah = HEOS.hmass() - 0; // offset from 0 kJ/kg enthalpy
              double deltas = HEOS.smass() - 0; // offset from 0 kJ/kg/K entropy
              double delta_a1 = deltas/(HEOS.gas_constant()/HEOS.molar_mass());
              double delta_a2 = -deltah/(HEOS.gas_constant()/HEOS.molar_mass()*HEOS.get_reducing_state().T);
              // Change the value in the library for the given fluid
              set_fluid_enthalpy_entropy_offset(fluid, delta_a1, delta_a2, "NBP");
              if (get_debug_level() > 0){
                  std::cout << format("set offsets to %0.15g and %0.15g\n", delta_a1, delta_a2);
              }
          }
          else if (!reference_state.compare("DEF"))
          {
              set_fluid_enthalpy_entropy_offset(fluid, 0, 0, "DEF");
          }
          else if (!reference_state.compare("RESET"))
          {
              set_fluid_enthalpy_entropy_offset(fluid, 0, 0, "RESET");
          }
          else
          {
              throw ValueError(format("Reference state string is invalid: [%s]",reference_state.c_str()));
          }
      }
  }
  void set_reference_stateD(const std::string &Ref, double T, double rhomolar, double hmolar0, double smolar0)
  {
      std::vector<std::string> _comps(1, Ref);
      CoolProp::HelmholtzEOSMixtureBackend HEOS(_comps);
  
      HEOS.update(DmolarT_INPUTS, rhomolar, T);
  
      // Get current values for the enthalpy and entropy
      double deltah = HEOS.hmolar() - hmolar0; // offset from specified enthalpy in J/mol
      double deltas = HEOS.smolar() - smolar0; // offset from specified entropy in J/mol/K
      double delta_a1 = deltas/(HEOS.gas_constant());
      double delta_a2 = -deltah/(HEOS.gas_constant()*HEOS.get_reducing_state().T);
      set_fluid_enthalpy_entropy_offset(Ref, delta_a1, delta_a2, "custom");
  }
  
  
  std::string get_global_param_string(const std::string &ParamName)
  {
      if (!ParamName.compare("version")){ return version; }
      else if (!ParamName.compare("gitrevision")){
          return gitrevision;
      }
      else if (!ParamName.compare("errstring")){
          std::string temp = error_string; error_string = ""; return temp;
      }
      else if (!ParamName.compare("warnstring")){
          std::string temp = warning_string; warning_string = ""; return temp;
      }
      else if (!ParamName.compare("FluidsList") || !ParamName.compare("fluids_list") || !ParamName.compare("fluidslist")){
          return get_fluid_list();
      }
      else if (!ParamName.compare("incompressible_list_pure")){
          return get_incompressible_list_pure();
      }
      else if (!ParamName.compare("incompressible_list_solution")){
          return get_incompressible_list_solution();
      }
      else if (!ParamName.compare("mixture_binary_pairs_list")){
          return get_csv_mixture_binary_pairs();
      }
      else if (!ParamName.compare("parameter_list") ){
          return get_csv_parameter_list();
      }
  	else if (!ParamName.compare("predefined_mixtures") ){
  		return get_csv_predefined_mixtures();
      }
      else if (!ParamName.compare("HOME")){
          return get_home_dir();
      }
    	else if (ParamName == "REFPROP_version"){
    		  return REFPROPMixtureBackend::version();
    	}
      else if (ParamName == "cubic_fluids_schema"){
          return CoolProp::CubicLibrary::get_cubic_fluids_schema();
      }
      else if (ParamName == "cubic_fluids_list"){
          return CoolProp::CubicLibrary::get_cubic_fluids_list();
      }
      else if (ParamName == "pcsaft_fluids_schema"){
          return CoolProp::PCSAFTLibrary::get_pcsaft_fluids_schema();
      }
      else{
          throw ValueError(format("Input parameter [%s] is invalid",ParamName.c_str()));
      }
  };
  #if defined(ENABLE_CATCH)
  TEST_CASE("Check inputs to get_global_param_string","[get_global_param_string]")
  {
      const int num_good_inputs = 8;
      std::string good_inputs[num_good_inputs] = {"version", "gitrevision", "fluids_list", "incompressible_list_pure", "incompressible_list_solution", "mixture_binary_pairs_list","parameter_list","predefined_mixtures"};
      std::ostringstream ss3c;
      for (int i = 0; i<num_good_inputs; ++i){
          ss3c << "Test for" << good_inputs[i];
          SECTION(ss3c.str(), ""){
              CHECK_NOTHROW(CoolProp::get_global_param_string(good_inputs[i]));
          };
      }
      CHECK_THROWS(CoolProp::get_global_param_string(""));
  };
  #endif
  std::string get_fluid_param_string(const std::string &FluidName, const std::string &ParamName)
  {
      std::string backend, fluid;
      extract_backend(FluidName, backend, fluid);
      shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory(backend, fluid));
      return AS->fluid_param_string(ParamName);
  }
  #if defined(ENABLE_CATCH)
  TEST_CASE("Check inputs to get_fluid_param_string", "[get_fluid_param_string]")
  {
      const int num_good_inputs = 10;
      std::string good_inputs[num_good_inputs] = {"aliases", "CAS", "ASHRAE34", "REFPROPName", "BibTeX-CONDUCTIVITY", "BibTeX-EOS", "BibTeX-CP0", "BibTeX-SURFACE_TENSION","BibTeX-MELTING_LINE","BibTeX-VISCOSITY"};
      std::ostringstream ss3c;
      for (int i = 0; i < num_good_inputs; ++i){
          ss3c << "Test for" << good_inputs[i];
          SECTION(ss3c.str(), ""){
              CHECK_NOTHROW(CoolProp::get_fluid_param_string("Water", good_inputs[i]));
          };
      }
      CHECK_THROWS(CoolProp::get_fluid_param_string("","aliases"));
      CHECK_THROWS(CoolProp::get_fluid_param_string("Water",""));
      CHECK_THROWS(CoolProp::get_fluid_param_string("Water","BibTeX-"));
      CHECK(CoolProp::get_fluid_param_string("Water","pure") == "true");
      CHECK(CoolProp::get_fluid_param_string("R410A","pure") == "false");
  };
  #endif
  
  std::string phase_lookup_string(phases Phase)
  {
      switch (Phase)
      {
      case iphase_liquid: ///< Liquid
          return "liquid";
      case iphase_supercritical: ///< Supercritical (p > pc, T > Tc)
          return "supercritical";
      case iphase_supercritical_gas: ///< Supercritical gas (p < pc, T > Tc)
          return "supercritical_gas";
      case iphase_supercritical_liquid: ///< Supercritical liquid (p > pc, T < Tc)
          return "supercritical_liquid";
      case iphase_critical_point: ///< At the critical point
          return "critical_point";
      case iphase_gas: ///< Subcritical gas
          return "gas";
      case iphase_twophase: ///< Twophase (between saturation curves - inclusive)
          return "twophase";
      case iphase_unknown: ///< Unknown phase
          return "unknown";
      case iphase_not_imposed:
          return "not_imposed";
      }
      throw ValueError("I should never be thrown");
  }
  std::string PhaseSI(const std::string &Name1, double Prop1, const std::string &Name2, double Prop2, const std::string &FluidName)
  {
      double Phase_double = PropsSI("Phase",Name1,Prop1,Name2,Prop2,FluidName);   // Attempt to get "Phase" from PropsSI()
      if (!ValidNumber(Phase_double)){                                            // if the returned phase is invalid...
          std::string strPhase = phase_lookup_string(iphase_unknown);             //     phase is unknown.
          std::string strError = get_global_param_string("errstring").c_str();    //     fetch waiting error string
          if (strError != "") {                                                   //     if error string is not empty,
              strPhase.append(": " + strError);                                   //        append it to the phase string.
          }
          return strPhase;                                                        //     return the "unknown" phase string
      }                                                                           // else
      std::size_t Phase_int = static_cast<std::size_t>(Phase_double);             //     convert returned phase to int
      return phase_lookup_string(static_cast<phases>(Phase_int));                 //     return phase as a string
  }
  
  /*
  std::string PhaseSI(const std::string &Name1, double Prop1, const std::string &Name2, double Prop2, const std::string &FluidName, const std::vector<double> &z)
  {
      double Phase_double = PropsSI("Phase",Name1,Prop1,Name2,Prop2,FluidName,z);
      if (!ValidNumber(Phase_double)){ return "";}
      std::size_t Phase_int = static_cast<std::size_t>(Phase_double);
      return phase_lookup_string(static_cast<phases>(Phase_int));
  }
  */
  } /* namespace CoolProp */
  