CoolProp  6.6.0
An open-source fluid property and humid air property database
CoolProp.cpp
Go to the documentation of this file.
1 #if defined(_MSC_VER)
2 # ifndef _CRTDBG_MAP_ALLOC
3 # define _CRTDBG_MAP_ALLOC
4 # endif
5 # ifndef _CRT_SECURE_NO_WARNINGS
6 # define _CRT_SECURE_NO_WARNINGS
7 # endif
8 # include <crtdbg.h>
9 #endif
10 
11 #include "CoolProp.h"
12 #include "AbstractState.h"
13 
14 #if defined(__ISWINDOWS__)
15 # include <windows.h>
16 # ifdef min
17 # undef min
18 # endif
19 # ifdef max
20 # undef max
21 # endif
22 #else
23 # ifndef DBL_EPSILON
24 # include <limits>
25 # define DBL_EPSILON std::numeric_limits<double>::epsilon()
26 # endif
27 #endif
28 
29 #include <memory>
30 
31 #include <iostream>
32 #include <stdlib.h>
33 #include <vector>
34 #include <exception>
35 #include <stdio.h>
36 #include <string>
37 #include <locale>
38 #include "CoolPropTools.h"
39 #include "Solvers.h"
40 #include "MatrixMath.h"
46 #include "DataStructures.h"
50 
51 #if defined(ENABLE_CATCH)
52 # include <catch2/catch_all.hpp>
53 #endif
54 
55 namespace CoolProp {
56 
57 static int debug_level = 0;
58 static std::string error_string;
59 static std::string warning_string;
60 
61 void set_debug_level(int level) {
62  debug_level = level;
63 }
64 int get_debug_level(void) {
65  return debug_level;
66 }
67 
69 #include "gitrevision.h" // Contents are like "std::string gitrevision = "aa121435436ggregrea4t43t433";"
70 #include "cpversion.h" // Contents are like "char version [] = "2.5";"
71 
72 void set_warning_string(const std::string& warning) {
73  warning_string = warning;
74 }
75 void set_error_string(const std::string& error) {
76  error_string = error;
77 }
78 
79 // Return true if the string has "BACKEND::*" format where * signifies a wildcard
80 bool has_backend_in_string(const std::string& fluid_string, std::size_t& i) {
81  i = fluid_string.find("::");
82  return i != std::string::npos;
83 }
84 
85 void extract_backend(std::string fluid_string, std::string& backend, std::string& fluid) {
86  std::size_t i;
87  // For backwards compatibility reasons, if "REFPROP-" or "REFPROP-MIX:" start
88  // the fluid_string, replace them with "REFPROP::"
89  if (fluid_string.find("REFPROP-MIX:") == 0) {
90  fluid_string.replace(0, 12, "REFPROP::");
91  }
92  if (fluid_string.find("REFPROP-") == 0) {
93  fluid_string.replace(0, 8, "REFPROP::");
94  }
95  if (has_backend_in_string(fluid_string, i)) {
96  // Part without the ::
97  backend = fluid_string.substr(0, i);
98  // Fluid name after the ::
99  fluid = fluid_string.substr(i + 2);
100  } else {
101  backend = "?";
102  fluid = fluid_string;
103  }
104  if (get_debug_level() > 10)
105  std::cout << format("%s:%d: backend extracted. backend: %s. fluid: %s\n", __FILE__, __LINE__, backend.c_str(), fluid.c_str());
106 }
107 
108 bool has_fractions_in_string(const std::string& fluid_string) {
109  // If can find both "[" and "]", it must have mole fractions encoded as string
110  return (fluid_string.find("[") != std::string::npos && fluid_string.find("]") != std::string::npos);
111 }
112 bool has_solution_concentration(const std::string& fluid_string) {
113  // If can find "-", expect mass fractions encoded as string
114  return (fluid_string.find('-') != std::string::npos && fluid_string.find('%') != std::string::npos);
115 }
116 
117 struct delim : std::numpunct<char>
118 {
119  char m_c;
120  delim(char c) : m_c(c){};
121  char do_decimal_point() const {
122  return m_c;
123  }
124 };
125 
126 std::string extract_fractions(const std::string& fluid_string, std::vector<double>& fractions) {
127 
128  if (has_fractions_in_string(fluid_string)) {
129  fractions.clear();
130  std::vector<std::string> names;
131 
132  // Break up into pairs - like "Ethane[0.5]&Methane[0.5]" -> ("Ethane[0.5]","Methane[0.5]")
133  std::vector<std::string> pairs = strsplit(fluid_string, '&');
134 
135  for (std::size_t i = 0; i < pairs.size(); ++i) {
136  const std::string& fluid = pairs[i];
137 
138  // Must end with ']'
139  if (fluid[fluid.size() - 1] != ']') throw ValueError(format("Fluid entry [%s] must end with ']' character", pairs[i].c_str()));
140 
141  // Split at '[', but first remove the ']' from the end by taking a substring
142  std::vector<std::string> name_fraction = strsplit(fluid.substr(0, fluid.size() - 1), '[');
143 
144  if (name_fraction.size() != 2) {
145  throw ValueError(format("Could not break [%s] into name/fraction", fluid.substr(0, fluid.size() - 1).c_str()));
146  }
147 
148  // Convert fraction to a double
149  const std::string &name = name_fraction[0], &fraction = name_fraction[1];
150  // The default locale for conversion from string to double is en_US with . as the deliminter
151  // Good: 0.1234 Bad: 0,1234
152  // But you can change the punctuation character for fraction parsing
153  // with the configuration variable FLOAT_PUNCTUATION to change the locale to something more convenient for you (e.g., a ',')
154  // See also http://en.cppreference.com/w/cpp/locale/numpunct/decimal_point
155  std::stringstream ssfraction(fraction);
156  char c = get_config_string(FLOAT_PUNCTUATION)[0];
157  ssfraction.imbue(std::locale(ssfraction.getloc(), new delim(c)));
158  double f;
159  ssfraction >> f;
160  if (ssfraction.rdbuf()->in_avail() != 0) {
161  throw ValueError(format("fraction [%s] was not converted fully", fraction.c_str()));
162  }
163 
164  if (f > 1 || f < 0) {
165  throw ValueError(format("fraction [%s] was not converted to a value between 0 and 1 inclusive", fraction.c_str()));
166  }
167 
168  if ((f > 10 * DBL_EPSILON) || // Only push component if fraction is positive and non-zero
169  (pairs.size() == 1)) // ..or if there is only one fluid (i.e. INCOMP backend )
170  {
171  // And add to vector
172  fractions.push_back(f);
173 
174  // Add name
175  names.push_back(name);
176  }
177  }
178 
179  if (get_debug_level() > 10)
180  std::cout << format("%s:%d: Detected fractions of %s for %s.", __FILE__, __LINE__, vec_to_string(fractions).c_str(),
181  (strjoin(names, "&")).c_str());
182  // Join fluids back together
183  return strjoin(names, "&");
184  } else if (has_solution_concentration(fluid_string)) {
185  fractions.clear();
186  double x;
187 
188  std::vector<std::string> fluid_parts = strsplit(fluid_string, '-');
189  // Check it worked
190  if (fluid_parts.size() != 2) {
191  throw ValueError(
192  format("Format of incompressible solution string [%s] is invalid, should be like \"EG-20%\" or \"EG-0.2\" ", fluid_string.c_str()));
193  }
194 
195  // Convert the concentration into a string
196  char* pEnd;
197  x = strtod(fluid_parts[1].c_str(), &pEnd);
198 
199  // Check if per cent or fraction syntax is used
200  if (!strcmp(pEnd, "%")) {
201  x *= 0.01;
202  }
203  fractions.push_back(x);
204  if (get_debug_level() > 10)
205  std::cout << format("%s:%d: Detected incompressible concentration of %s for %s.", __FILE__, __LINE__, vec_to_string(fractions).c_str(),
206  fluid_parts[0].c_str());
207  return fluid_parts[0];
208  } else {
209  return fluid_string;
210  }
211 }
212 
213 void _PropsSI_initialize(const std::string& backend, const std::vector<std::string>& fluid_names, const std::vector<double>& z,
214  shared_ptr<AbstractState>& State) {
215 
216  if (fluid_names.empty()) {
217  throw ValueError("fluid_names cannot be empty");
218  }
219 
220  std::vector<double> fractions(1, 1.0); // Default to one component, unity fraction
221  const std::vector<double>* fractions_ptr = NULL; // Pointer to the array to be used;
222 
223  if (fluid_names.size() > 1) {
224  // Set the pointer - we are going to use the supplied fractions; they must be provided
225  fractions_ptr = &z;
226  // Reset the state
227  State.reset(AbstractState::factory(backend, fluid_names));
228  } else if (fluid_names.size() == 1) {
229  if (has_fractions_in_string(fluid_names[0]) || has_solution_concentration(fluid_names[0])) {
230  // Extract fractions from the string
231  std::string fluid_string = extract_fractions(fluid_names[0], fractions);
232  // Set the pointer - we are going to use the extracted fractions
233  fractions_ptr = &fractions;
234  // Reset the state
235  State.reset(AbstractState::factory(backend, fluid_string));
236  } else {
237  if (z.empty()) {
238  // Set the pointer - we are going to use the default fractions
239  fractions_ptr = &fractions;
240  } else {
241  // Set the pointer - we are going to use the provided fractions
242  fractions_ptr = &z;
243  }
244  // Reset the state
245  State.reset(AbstractState::factory(backend, fluid_names));
246  }
247  } else { // The only path where fractions_ptr stays NULL
248  throw ValueError("fractions_ptr is NULL");
249  }
250  if (!State->available_in_high_level()) {
251  throw ValueError(
252  "This AbstractState derived class cannot be used in the high-level interface; see www.coolprop.org/dev/coolprop/LowLevelAPI.html");
253  }
254 
255  // Set the fraction for the state
256  if (State->using_mole_fractions()) {
257  // If a predefined mixture or a pure fluid, the fractions will already be set
258  if (State->get_mole_fractions().empty()) {
259  State->set_mole_fractions(*fractions_ptr);
260  }
261  } else if (State->using_mass_fractions()) {
262  State->set_mass_fractions(*fractions_ptr);
263  } else if (State->using_volu_fractions()) {
264  State->set_volu_fractions(*fractions_ptr);
265  } else {
266  if (get_debug_level() > 50)
267  std::cout << format("%s:%d: _PropsSI, could not set composition to %s, defaulting to mole fraction.\n", __FILE__, __LINE__,
268  vec_to_string(z).c_str())
269  .c_str();
270  }
271 }
272 
274 {
276  {
283  };
288  static std::vector<output_parameter> get_output_parameters(const std::vector<std::string>& Outputs) {
289  std::vector<output_parameter> outputs;
290  for (std::vector<std::string>::const_iterator str = Outputs.begin(); str != Outputs.end(); ++str) {
291  output_parameter out;
292  CoolProp::parameters iOutput;
293  if (is_valid_parameter(*str, iOutput)) {
294  out.Of1 = iOutput;
295  if (is_trivial_parameter(iOutput)) {
297  } else {
298  out.type = OUTPUT_TYPE_NORMAL;
299  }
300  } else if (is_valid_first_saturation_derivative(*str, out.Of1, out.Wrt1)) {
302  } else if (is_valid_first_derivative(*str, out.Of1, out.Wrt1, out.Constant1)) {
304  } else if (is_valid_second_derivative(*str, out.Of1, out.Wrt1, out.Constant1, out.Wrt2, out.Constant2)) {
306  } else {
307  throw ValueError(format("Output string is invalid [%s]", str->c_str()));
308  }
309  outputs.push_back(out);
310  }
311  return outputs;
312  };
313 };
314 
315 void _PropsSI_outputs(shared_ptr<AbstractState>& State, const std::vector<output_parameter>& output_parameters, CoolProp::input_pairs input_pair,
316  const std::vector<double>& in1, const std::vector<double>& in2, std::vector<std::vector<double>>& IO) {
317 
318  // Check the inputs
319  if (in1.size() != in2.size()) {
320  throw ValueError(format("lengths of in1 [%d] and in2 [%d] are not the same", in1.size(), in2.size()));
321  }
322  bool one_input_one_output = (in1.size() == 1 && in2.size() == 1 && output_parameters.size() == 1);
323  // If all trivial outputs, never do a state update
324  bool all_trivial_outputs = true;
325  for (std::size_t j = 0; j < output_parameters.size(); ++j) {
326  if (output_parameters[j].type != output_parameter::OUTPUT_TYPE_TRIVIAL) {
327  all_trivial_outputs = false;
328  }
329  }
330  parameters p1, p2;
331  // If all outputs are also inputs, never do a state update
332  bool all_outputs_in_inputs = true;
333  if (input_pair != INPUT_PAIR_INVALID) {
334  // Split the input pair into parameters
335  split_input_pair(input_pair, p1, p2);
336  // See if each parameter is in the output vector and is a normal type input
337  for (std::size_t j = 0; j < output_parameters.size(); ++j) {
338  if (output_parameters[j].type != output_parameter::OUTPUT_TYPE_NORMAL) {
339  all_outputs_in_inputs = false;
340  break;
341  }
342  if (!(output_parameters[j].Of1 == p1 || output_parameters[j].Of1 == p2)) {
343  all_outputs_in_inputs = false;
344  break;
345  }
346  }
347  } else {
348  if (!all_trivial_outputs) {
349  throw ValueError(format("Input pair variable is invalid and output(s) are non-trivial; cannot do state update"));
350  }
351  all_outputs_in_inputs = false;
352  }
353 
354  if (get_debug_level() > 100) {
355  std::cout << format("%s (%d): input pair = %d ", __FILE__, __LINE__, input_pair) << std::endl;
356  std::cout << format("%s (%d): in1 = %s ", __FILE__, __LINE__, vec_to_string(in1).c_str()) << std::endl;
357  std::cout << format("%s (%d): in2 = %s ", __FILE__, __LINE__, vec_to_string(in2).c_str()) << std::endl;
358  }
359 
360  // Get configuration variable for line tracing, see #1443
361  const bool use_guesses = get_config_bool(USE_GUESSES_IN_PROPSSI);
362  GuessesStructure guesses;
363 
364  // Resize the output matrix
365  std::size_t N1 = std::max(static_cast<std::size_t>(1), in1.size());
366  std::size_t N2 = std::max(static_cast<std::size_t>(1), output_parameters.size());
367  IO.resize(N1, std::vector<double>(N2, _HUGE));
368 
369  // Throw an error if at the end, there were no successes
370  bool success = false;
371  bool success_inner = false;
372 
373  if (get_debug_level() > 100) {
374  std::cout << format("%s (%d): Iterating over %d input value pairs.", __FILE__, __LINE__, IO.size()) << std::endl;
375  }
376 
377  // Iterate over the state variable inputs
378  for (std::size_t i = 0; i < IO.size(); ++i) {
379  // Reset the success indicator for the current state point
380  success_inner = false;
381  try {
382  if (input_pair != INPUT_PAIR_INVALID && !all_trivial_outputs && !all_outputs_in_inputs) {
383  // Update the state since it is a valid set of inputs
384  if (!use_guesses || i == 0) {
385  State->update(input_pair, in1[i], in2[i]);
386  } else {
387  State->update_with_guesses(input_pair, in1[i], in2[i], guesses);
388  guesses.clear();
389  }
390  }
391  } catch (...) {
392  if (one_input_one_output) {
393  IO.clear();
394  throw;
395  } // Re-raise the exception since we want to bubble the error
396  // All the outputs are filled with _HUGE; go to next input
397  for (std::size_t j = 0; j < IO[i].size(); ++j) {
398  IO[i][j] = _HUGE;
399  }
400  continue;
401  }
402 
403  for (std::size_t j = 0; j < IO[i].size(); ++j) {
404  // If all the outputs are inputs, there is no need for a state input
405  if (all_outputs_in_inputs) {
406  if (p1 == output_parameters[j].Of1) {
407  IO[i][j] = in1[i];
408  success_inner = true;
409  continue;
410  } else if (p2 == output_parameters[j].Of1) {
411  IO[i][j] = in2[i];
412  success_inner = true;
413  continue;
414  } else {
415  throw ValueError();
416  }
417  }
418  try {
419  const output_parameter& output = output_parameters[j];
420  switch (output.type) {
423  IO[i][j] = State->keyed_output(output.Of1);
424  if (use_guesses) {
425  switch (output.Of1) {
426  case iDmolar:
427  guesses.rhomolar = IO[i][j];
428  break;
429  case iT:
430  guesses.T = IO[i][j];
431  break;
432  case iP:
433  guesses.p = IO[i][j];
434  break;
435  case iHmolar:
436  guesses.hmolar = IO[i][j];
437  break;
438  case iSmolar:
439  guesses.smolar = IO[i][j];
440  break;
441  default:
442  throw ValueError("Don't understand this parameter");
443  }
444  }
445  break;
447  IO[i][j] = State->first_partial_deriv(output.Of1, output.Wrt1, output.Constant1);
448  break;
450  IO[i][j] = State->first_saturation_deriv(output.Of1, output.Wrt1);
451  break;
453  IO[i][j] = State->second_partial_deriv(output.Of1, output.Wrt1, output.Constant1, output.Wrt2, output.Constant2);
454  break;
455  default:
456  throw ValueError(format(""));
457  break;
458  }
459  // At least one has succeeded
460  success_inner = true;
461  } catch (...) {
462  if (one_input_one_output) {
463  IO.clear();
464  throw;
465  } // Re-raise the exception since we want to bubble the error
466  IO[i][j] = _HUGE;
467  }
468  }
469  // We want to have at least rhomolar and T, but we do not raise errors here
470  if (use_guesses && success_inner) {
471  if (!ValidNumber(guesses.rhomolar)) {
472  try {
473  guesses.rhomolar = State->rhomolar();
474  } catch (...) {
475  guesses.rhomolar = _HUGE;
476  }
477  }
478  if (!ValidNumber(guesses.T)) {
479  try {
480  guesses.T = State->T();
481  } catch (...) {
482  guesses.T = _HUGE;
483  }
484  }
485  }
486  // Save the success indicator, just a single valid output is enough
487  success |= success_inner;
488  }
489  if (success == false) {
490  IO.clear();
491  throw ValueError(format("No outputs were able to be calculated"));
492  }
493 }
494 
495 bool StripPhase(std::string& Name, shared_ptr<AbstractState>& State)
496 // Parses an imposed phase out of the Input Name string using the "|" delimiter
497 {
498  std::vector<std::string> strVec = strsplit(Name, '|'); // Split input key string in to vector containing input key [0] and phase string [1]
499  if (strVec.size() > 1) { // If there is a phase string (contains "|" character)
500  // Check for invalid backends for setting phase in PropsSI
501  std::string strBackend = State->backend_name();
502  if (strBackend == get_backend_string(INCOMP_BACKEND))
503  throw ValueError("Cannot set phase on Incompressible Fluid; always liquid phase"); // incompressible fluids are always "liquid".
504  if (strBackend == get_backend_string(IF97_BACKEND))
505  throw ValueError("Can't set phase on IF97 Backend"); // IF97 has to calculate it's own phase region
506  if (strBackend == get_backend_string(TTSE_BACKEND))
507  throw ValueError("Can't set phase on TTSE Backend in PropsSI"); // Shouldn't be calling from High-Level anyway
508  if (strBackend == get_backend_string(BICUBIC_BACKEND))
509  throw ValueError("Can't set phase on BICUBIC Backend in PropsSI"); // Shouldn't be calling from High-Level anyway
510  if (strBackend == get_backend_string(VTPR_BACKEND))
511  throw ValueError("Can't set phase on VTPR Backend in PropsSI"); // VTPR has no phase functions to call
512 
513  phases imposed = iphase_not_imposed; // Initialize imposed phase
514  if (strVec.size() > 2) // If there's more than on phase separator, throw error
515  {
516  throw ValueError(format("Invalid phase format: \"%s\"", Name));
517  }
518  // Handle prefixes of iphase_, phase_, or <none>
519  std::basic_string<char>::iterator str_Iter;
520  std::string strPhase = strVec[1]; // Create a temp string so we can modify the prefix
521  if (strPhase.find("iphase_") != strPhase.npos) {
522  str_Iter = strPhase.erase(strPhase.begin());
523  } // Change "iphase_" to "phase_"
524  if (strPhase.find("phase_") == strPhase.npos) {
525  strPhase.insert(0, "phase_");
526  } // Prefix with "phase_" if missing
527  // See if phase is a valid phase string, updating imposed while we're at it...
528  if (!is_valid_phase(strPhase, imposed)) {
529  throw ValueError(format("Phase string \"%s\" is not a valid phase", strVec[1])); // throw error with original string if not valid
530  }
531  // Parsed phase string was valid
532  Name = strVec[0]; // Update input name to just the key string part
533  State->specify_phase(imposed); // Update the specified phase on the backend State
534  return true; // Return true because a valid phase string was found
535  }
536  return false; // Return false if there was no phase string on this key.
537 }
538 
539 void _PropsSImulti(const std::vector<std::string>& Outputs, const std::string& Name1, const std::vector<double>& Prop1, const std::string& Name2,
540  const std::vector<double>& Prop2, const std::string& backend, const std::vector<std::string>& fluids,
541  const std::vector<double>& fractions, std::vector<std::vector<double>>& IO) {
542  shared_ptr<AbstractState> State;
543  CoolProp::parameters key1 = INVALID_PARAMETER, key2 = INVALID_PARAMETER; // Initialize to invalid parameter values
544  CoolProp::input_pairs input_pair = INPUT_PAIR_INVALID; // Initialize to invalid input pair
545  std::vector<output_parameter> output_parameters;
546  std::vector<double> v1, v2;
547 
548  try {
549  // Initialize the State class
550  _PropsSI_initialize(backend, fluids, fractions, State);
551  } catch (std::exception& e) {
552  // Initialization failed. Stop.
553  throw ValueError(format("Initialize failed for backend: \"%s\", fluid: \"%s\" fractions \"%s\"; error: %s", backend.c_str(),
554  strjoin(fluids, "&").c_str(), vec_to_string(fractions, "%0.10f").c_str(), e.what()));
555  }
556 
557  //strip any imposed phase from input key strings here
558  std::string N1 = Name1; // Make Non-constant copy of Name1 that we can modify
559  std::string N2 = Name2; // Make Non-constant copy of Name2 that we can modify
560  bool HasPhase1 = StripPhase(N1, State); // strip phase string from first name if needed
561  bool HasPhase2 = StripPhase(N2, State); // strip phase string from second name if needed
562  if (HasPhase1 && HasPhase2) // if both Names have a phase string, don't allow it.
563  throw ValueError("Phase can only be specified on one of the input key strings");
564 
565  try {
566  // Get update pair
567  if (is_valid_parameter(N1, key1) && is_valid_parameter(N2, key2)) input_pair = generate_update_pair(key1, Prop1, key2, Prop2, v1, v2);
568  } catch (std::exception& e) {
569  // Input parameter parsing failed. Stop
570  throw ValueError(format("Input pair parsing failed for Name1: \"%s\", Name2: \"%s\"; err: %s", Name1.c_str(), Name2.c_str(), e.what()));
571  }
572 
573  try {
574  output_parameters = output_parameter::get_output_parameters(Outputs);
575  } catch (std::exception& e) {
576  // Output parameter parsing failed. Stop.
577  throw ValueError(format("Output parameter parsing failed; error: %s", e.what()));
578  }
579 
580  // Calculate the output(s). In the case of a failure, all values will be filled with _HUGE
581  _PropsSI_outputs(State, output_parameters, input_pair, v1, v2, IO);
582 }
583 
584 std::vector<std::vector<double>> PropsSImulti(const std::vector<std::string>& Outputs, const std::string& Name1, const std::vector<double>& Prop1,
585  const std::string& Name2, const std::vector<double>& Prop2, const std::string& backend,
586  const std::vector<std::string>& fluids, const std::vector<double>& fractions) {
587  std::vector<std::vector<double>> IO;
588 
589 #if !defined(NO_ERROR_CATCHING)
590  try {
591 #endif
592 
593  // Call the subfunction that can bubble errors
594  _PropsSImulti(Outputs, Name1, Prop1, Name2, Prop2, backend, fluids, fractions, IO);
595 
596  // Return the value(s)
597  return IO;
598 
599 #if !defined(NO_ERROR_CATCHING)
600  } catch (const std::exception& e) {
601  set_error_string(e.what());
602 # if defined(PROPSSI_ERROR_STDOUT)
603  std::cout << e.what() << std::endl;
604 # endif
605  if (get_debug_level() > 1) {
606  std::cout << e.what() << std::endl;
607  }
608  } catch (...) {
609  }
610 #endif
611  return std::vector<std::vector<double>>();
612 }
613 double PropsSI(const std::string& Output, const std::string& Name1, double Prop1, const std::string& Name2, double Prop2, const std::string& Ref) {
614 #if !defined(NO_ERROR_CATCHING)
615  try {
616 #endif
617 
618  // BEGIN OF TRY
619  // Here is the real code that is inside the try block
620 
621  std::string backend, fluid;
622  extract_backend(Ref, backend, fluid);
623  std::vector<double> fractions(1, 1.0);
624  // extract_fractions checks for has_fractions_in_string / has_solution_concentration; no need to double check
625  std::string fluid_string = extract_fractions(fluid, fractions);
626  std::vector<std::vector<double>> IO;
627  _PropsSImulti(strsplit(Output, '&'), Name1, std::vector<double>(1, Prop1), Name2, std::vector<double>(1, Prop2), backend,
628  strsplit(fluid_string, '&'), fractions, IO);
629  if (IO.empty()) {
630  throw ValueError(get_global_param_string("errstring").c_str());
631  }
632  if (IO.size() != 1 || IO[0].size() != 1) {
633  throw ValueError(format("output should be 1x1; error was %s", get_global_param_string("errstring").c_str()));
634  }
635 
636  double val = IO[0][0];
637 
638  if (get_debug_level() > 1) {
639  std::cout << format("_PropsSI will return %g", val) << std::endl;
640  }
641  return val;
642  // END OF TRY
643 #if !defined(NO_ERROR_CATCHING)
644  } catch (const std::exception& e) {
646  e.what()
647  + format(" : PropsSI(\"%s\",\"%s\",%0.10g,\"%s\",%0.10g,\"%s\")", Output.c_str(), Name1.c_str(), Prop1, Name2.c_str(), Prop2, Ref.c_str()));
648 # if defined(PROPSSI_ERROR_STDOUT)
649  std::cout << e.what() << std::endl;
650 # endif
651  if (get_debug_level() > 1) {
652  std::cout << e.what() << std::endl;
653  }
654  return _HUGE;
655  } catch (...) {
656  return _HUGE;
657  }
658 #endif
659 }
660 
661 bool add_fluids_as_JSON(const std::string& backend, const std::string& fluidstring) {
662  if (backend == "SRK" || backend == "PR") {
664  return true;
665  } else if (backend == "HEOS") {
666  JSONFluidLibrary::add_many(fluidstring);
667  return true;
668  } else if (backend == "PCSAFT") {
670  return true;
671  } else {
672  throw ValueError(format("You have provided an invalid backend [%s] to add_fluids_as_JSON; valid options are SRK, PR, HEOS", backend.c_str()));
673  }
674 }
675 
676 #if defined(ENABLE_CATCH)
677 TEST_CASE("Check inputs to PropsSI", "[PropsSI]") {
678  SECTION("Single state, single output") {
679  CHECK(ValidNumber(CoolProp::PropsSI("T", "P", 101325, "Q", 0, "Water")));
680  };
681  SECTION("Single state, single output, saturation derivative") {
682  CHECK(ValidNumber(CoolProp::PropsSI("d(P)/d(T)|sigma", "P", 101325, "Q", 0, "Water")));
683  };
684  SECTION("Single state, single output, pure incompressible") {
685  CHECK(ValidNumber(CoolProp::PropsSI("D", "P", 101325, "T", 300, "INCOMP::DowQ")));
686  };
687  SECTION("Single state, trivial output, pure incompressible") {
688  CHECK(ValidNumber(CoolProp::PropsSI("Tmin", "P", 0, "T", 0, "INCOMP::DowQ")));
689  };
690  SECTION("Bad input pair") {
691  CHECK(!ValidNumber(CoolProp::PropsSI("D", "Q", 0, "Q", 0, "Water")));
692  };
693  SECTION("Single state, single output, 40% incompressible") {
694  CHECK(ValidNumber(CoolProp::PropsSI("D", "P", 101325, "T", 300, "INCOMP::MEG[0.40]")));
695  };
696  SECTION("Single state, single output, predefined CoolProp mixture") {
697  CHECK(ValidNumber(CoolProp::PropsSI("T", "Q", 1, "P", 3e6, "HEOS::R125[0.7]&R32[0.3]")));
698  };
699  SECTION("Single state, single output") {
700  CHECK(ValidNumber(CoolProp::PropsSI("T", "P", 101325, "Q", 0, "HEOS::Water")));
701  };
702  SECTION("Single state, single output, predefined mixture") {
703  CHECK(ValidNumber(CoolProp::PropsSI("T", "P", 101325, "Q", 0, "R410A.mix")));
704  };
705  SECTION("Single state, single output, predefined mixture from REFPROP") {
706  CHECK(ValidNumber(CoolProp::PropsSI("T", "P", 101325, "Q", 0, "REFPROP::R410A.MIX")));
707  };
708  SECTION("Single state, single output, bad predefined mixture from REFPROP") {
709  CHECK(!ValidNumber(CoolProp::PropsSI("T", "P", 101325, "Q", 0, "REFPROP::RRRRRR.mix")));
710  };
711  SECTION("Predefined mixture") {
712  std::vector<double> p(1, 101325), Q(1, 1.0), z;
713  std::vector<std::string> outputs(1, "T");
714  outputs.push_back("Dmolar");
715  std::vector<std::vector<double>> IO;
716  std::vector<std::string> fluids(1, "R410A.mix");
717  CHECK_NOTHROW(IO = CoolProp::PropsSImulti(outputs, "P", p, "Q", Q, "HEOS", fluids, z));
718  };
719  SECTION("Single state, two outputs") {
720  std::vector<double> p(1, 101325), Q(1, 1.0), z(1, 1.0);
721  std::vector<std::string> outputs(1, "T");
722  outputs.push_back("Dmolar");
723  std::vector<std::string> fluids(1, "Water");
724  CHECK_NOTHROW(CoolProp::PropsSImulti(outputs, "P", p, "Q", Q, "HEOS", fluids, z));
725  };
726  SECTION("Single state, two bad outputs") {
727  std::vector<double> p(1, 101325), Q(1, 1.0), z(1, 1.0);
728  std::vector<std::vector<double>> IO;
729  std::vector<std::string> outputs(1, "???????");
730  outputs.push_back("?????????");
731  std::vector<std::string> fluids(1, "Water");
732  CHECK_NOTHROW(IO = CoolProp::PropsSImulti(outputs, "P", p, "Q", Q, "HEOS", fluids, z));
733  CHECK(IO.size() == 0);
734  };
735  SECTION("Two states, one output") {
736  std::vector<double> p(2, 101325), Q(2, 1.0), z(1, 1.0);
737  std::vector<std::string> outputs(1, "T");
738  std::vector<std::string> fluids(1, "Water");
739  CHECK_NOTHROW(CoolProp::PropsSImulti(outputs, "P", p, "Q", Q, "HEOS", fluids, z));
740  };
741  SECTION("Two states, two outputs") {
742  std::vector<double> p(2, 101325), Q(2, 1.0), z(1, 1.0);
743  std::vector<std::string> outputs(1, "T");
744  outputs.push_back("Dmolar");
745  std::vector<std::string> fluids(1, "Water");
746  CHECK_NOTHROW(CoolProp::PropsSImulti(outputs, "P", p, "Q", Q, "HEOS", fluids, z));
747  };
748  SECTION("cp and its derivative representation") {
749  std::vector<double> p(1, 101325), Q(1, 1.0), z(1, 1.0);
750  std::vector<std::vector<double>> IO;
751  std::vector<std::string> outputs(1, "Cpmolar");
752  outputs.push_back("d(Hmolar)/d(T)|P");
753  std::vector<std::string> fluids(1, "Water");
754  CHECK_NOTHROW(IO = CoolProp::PropsSImulti(outputs, "P", p, "Q", Q, "HEOS", fluids, z));
755  std::string errstring = get_global_param_string("errstring");
756  CAPTURE(errstring);
757  REQUIRE(!IO.empty());
758  CAPTURE(IO[0][0]);
759  CAPTURE(IO[0][1]);
760  CHECK(std::abs(IO[0][0] - IO[0][1]) < 1e-5);
761  };
762  SECTION("bad fluid") {
763  std::vector<double> p(1, 101325), Q(1, 1.0), z(1, 1.0);
764  std::vector<std::vector<double>> IO;
765  std::vector<std::string> outputs(1, "Cpmolar");
766  outputs.push_back("d(Hmolar)/d(T)|P");
767  std::vector<std::string> fluids(1, "????????");
768  CHECK_NOTHROW(IO = CoolProp::PropsSImulti(outputs, "P", p, "Q", Q, "HEOS", fluids, z));
769  std::string errstring = get_global_param_string("errstring");
770  CAPTURE(errstring);
771  REQUIRE(IO.empty());
772  };
773  SECTION("bad mole fraction length") {
774  std::vector<double> p(1, 101325), Q(1, 1.0), z(1, 1.0);
775  std::vector<std::vector<double>> IO;
776  std::vector<std::string> outputs(1, "T");
777  std::vector<std::string> fluids(1, "Water&Ethanol");
778  CHECK_NOTHROW(IO = CoolProp::PropsSImulti(outputs, "P", p, "Q", Q, "HEOS", fluids, z));
779  std::string errstring = get_global_param_string("errstring");
780  CAPTURE(errstring);
781  REQUIRE(IO.empty());
782  };
783  SECTION("bad input lengths") {
784  std::vector<double> p(1, 101325), Q(2, 1.0), z(100, 1.0);
785  std::vector<std::vector<double>> IO;
786  std::vector<std::string> outputs(1, "Cpmolar");
787  outputs.push_back("d(Hmolar)/d(T)|P");
788  std::vector<std::string> fluids(1, "Water");
789  CHECK_NOTHROW(IO = CoolProp::PropsSImulti(outputs, "P", p, "Q", Q, "HEOS", fluids, z));
790  std::string errstring = get_global_param_string("errstring");
791  CAPTURE(errstring);
792  REQUIRE(IO.empty());
793  };
794  SECTION("bad input pair") {
795  std::vector<double> Q(2, 1.0), z(1, 1.0);
796  std::vector<std::vector<double>> IO;
797  std::vector<std::string> outputs(1, "Cpmolar");
798  outputs.push_back("d(Hmolar)/d(T)|P");
799  std::vector<std::string> fluids(1, "Water");
800  CHECK_NOTHROW(IO = CoolProp::PropsSImulti(outputs, "Q", Q, "Q", Q, "HEOS", fluids, z));
801  std::string errstring = get_global_param_string("errstring");
802  CAPTURE(errstring);
803  REQUIRE(IO.empty());
804  };
805 };
806 #endif
807 
808 /****************************************************
809  * Props1SI *
810  ****************************************************/
811 
812 double Props1SI(std::string FluidName, std::string Output) {
813  bool valid_fluid1 = is_valid_fluid_string(FluidName);
814  bool valid_fluid2 = is_valid_fluid_string(Output);
815  if (valid_fluid1 && valid_fluid2) {
816  set_error_string(format("Both inputs to Props1SI [%s,%s] are valid fluids", Output.c_str(), FluidName.c_str()));
817  return _HUGE;
818  }
819  if (!valid_fluid1 && !valid_fluid2) {
820  set_error_string(format("Neither input to Props1SI [%s,%s] is a valid fluid", Output.c_str(), FluidName.c_str()));
821  return _HUGE;
822  }
823  if (!valid_fluid1 && valid_fluid2) {
824  // They are backwards, swap
825  std::swap(Output, FluidName);
826  }
827 
828  // First input is the fluid, second input is the input parameter
829  double val1 = PropsSI(Output, "", 0, "", 0, FluidName);
830  if (!ValidNumber(val1)) {
831  set_error_string(format("Unable to use input parameter [%s] in Props1SI for fluid %s; error was %s", Output.c_str(), FluidName.c_str(),
832  get_global_param_string("errstring").c_str()));
833  return _HUGE;
834  } else {
835  return val1;
836  }
837 }
838 
839 std::vector<std::vector<double>> Props1SImulti(const std::vector<std::string>& Outputs, const std::string& backend, const std::vector<std::string>& fluids, const std::vector<double>& fractions) {
840  std::vector<double> zero_vector(1, 0.);
841  std::vector<std::vector<double>> val1 = PropsSImulti(Outputs, "", zero_vector, "", zero_vector, backend, fluids, fractions);
842  // error handling is done in PropsSImulti, val1 will be an empty vector if an error occured
843  return val1;
844 }
845 
846 
847 #if defined(ENABLE_CATCH)
848 TEST_CASE("Check inputs to Props1SI", "[Props1SI],[PropsSI]") {
849  SECTION("Good fluid, good parameter") {
850  CHECK(ValidNumber(CoolProp::Props1SI("Tcrit", "Water")));
851  };
852  SECTION("Good fluid, good parameter") {
853  CHECK(ValidNumber(CoolProp::PropsSI("Tcrit", "", 0, "", 0, "Water")));
854  };
855  SECTION("Good fluid, good parameter, inverted") {
856  CHECK(ValidNumber(CoolProp::Props1SI("Water", "Tcrit")));
857  };
858  SECTION("Good fluid, bad parameter") {
859  CHECK(!ValidNumber(CoolProp::Props1SI("Water", "????????????")));
860  };
861  SECTION("Bad fluid, good parameter") {
862  CHECK(!ValidNumber(CoolProp::Props1SI("?????", "Tcrit")));
863  };
864 };
865 #endif
866 
867 bool is_valid_fluid_string(const std::string& input_fluid_string) {
868  try {
869  std::string backend, fluid;
870  std::vector<double> fractions;
871  // First try to extract backend and fractions
872  extract_backend(input_fluid_string, backend, fluid);
873  std::string fluid_string = extract_fractions(fluid, fractions);
874  // We are going to let the factory function load the state
875  shared_ptr<AbstractState> State(AbstractState::factory(backend, fluid_string));
876  return true;
877  } catch (...) {
878  return false;
879  }
880 }
881 double saturation_ancillary(const std::string& fluid_name, const std::string& output, int Q, const std::string& input, double value) {
882 
883  // Generate the state instance
884  std::vector<std::string> names(1, fluid_name);
886 
887  parameters iInput = get_parameter_index(input);
888  parameters iOutput = get_parameter_index(output);
889 
890  return HEOS.saturation_ancillary(iOutput, Q, iInput, value);
891 }
892 void set_reference_stateS(const std::string& fluid_string, const std::string& reference_state) {
893  std::string backend, fluid;
894  extract_backend(fluid_string, backend, fluid);
895  if (backend == "REFPROP") {
896 
897  int ierr = 0, ixflag = 1;
898  double h0 = 0, s0 = 0, t0 = 0, p0 = 0;
899  char herr[255], hrf[4];
900  double x0[1] = {1};
901  const char* refstate = reference_state.c_str();
902  if (strlen(refstate) > 3) {
903  if (reference_state == "ASHRAE") {
904  strcpy(hrf, "ASH");
905  } else {
906  throw ValueError(format("Reference state string [%s] is more than 3 characters long", reference_state.c_str()));
907  }
908  } else {
909  strcpy(hrf, refstate);
910  }
911  REFPROP_SETREF(hrf, ixflag, x0, h0, s0, t0, p0, ierr, herr, 3, 255);
912  } else if (backend == "HEOS" || backend == "?") {
913  CoolProp::HelmholtzEOSMixtureBackend HEOS(std::vector<std::string>(1, fluid));
914  if (!reference_state.compare("IIR")) {
915  if (HEOS.Ttriple() > 273.15) {
916  throw ValueError(format("Cannot use IIR reference state; Ttriple [%Lg] is greater than 273.15 K", HEOS.Ttriple()));
917  }
918  HEOS.update(QT_INPUTS, 0, 273.15);
919 
920  // Get current values for the enthalpy and entropy
921  double deltah = HEOS.hmass() - 200000; // offset from 200000 J/kg enthalpy
922  double deltas = HEOS.smass() - 1000; // offset from 1000 J/kg/K entropy
923  double delta_a1 = deltas / (HEOS.gas_constant() / HEOS.molar_mass());
924  double delta_a2 = -deltah / (HEOS.gas_constant() / HEOS.molar_mass() * HEOS.get_reducing_state().T);
925  // Change the value in the library for the given fluid
926  set_fluid_enthalpy_entropy_offset(fluid, delta_a1, delta_a2, "IIR");
927  if (get_debug_level() > 0) {
928  std::cout << format("set offsets to %0.15g and %0.15g\n", delta_a1, delta_a2);
929  }
930  } else if (!reference_state.compare("ASHRAE")) {
931  if (HEOS.Ttriple() > 233.15) {
932  throw ValueError(format("Cannot use ASHRAE reference state; Ttriple [%Lg] is greater than than 233.15 K", HEOS.Ttriple()));
933  }
934  HEOS.update(QT_INPUTS, 0, 233.15);
935 
936  // Get current values for the enthalpy and entropy
937  double deltah = HEOS.hmass() - 0; // offset from 0 J/kg enthalpy
938  double deltas = HEOS.smass() - 0; // offset from 0 J/kg/K entropy
939  double delta_a1 = deltas / (HEOS.gas_constant() / HEOS.molar_mass());
940  double delta_a2 = -deltah / (HEOS.gas_constant() / HEOS.molar_mass() * HEOS.get_reducing_state().T);
941  // Change the value in the library for the given fluid
942  set_fluid_enthalpy_entropy_offset(fluid, delta_a1, delta_a2, "ASHRAE");
943  if (get_debug_level() > 0) {
944  std::cout << format("set offsets to %0.15g and %0.15g\n", delta_a1, delta_a2);
945  }
946  } else if (!reference_state.compare("NBP")) {
947  if (HEOS.p_triple() > 101325) {
948  throw ValueError(format("Cannot use NBP reference state; p_triple [%Lg Pa] is greater than than 101325 Pa", HEOS.p_triple()));
949  }
950  // Saturated liquid boiling point at 1 atmosphere
951  HEOS.update(PQ_INPUTS, 101325, 0);
952 
953  double deltah = HEOS.hmass() - 0; // offset from 0 kJ/kg enthalpy
954  double deltas = HEOS.smass() - 0; // offset from 0 kJ/kg/K entropy
955  double delta_a1 = deltas / (HEOS.gas_constant() / HEOS.molar_mass());
956  double delta_a2 = -deltah / (HEOS.gas_constant() / HEOS.molar_mass() * HEOS.get_reducing_state().T);
957  // Change the value in the library for the given fluid
958  set_fluid_enthalpy_entropy_offset(fluid, delta_a1, delta_a2, "NBP");
959  if (get_debug_level() > 0) {
960  std::cout << format("set offsets to %0.15g and %0.15g\n", delta_a1, delta_a2);
961  }
962  } else if (!reference_state.compare("DEF")) {
963  set_fluid_enthalpy_entropy_offset(fluid, 0, 0, "DEF");
964  } else if (!reference_state.compare("RESET")) {
965  set_fluid_enthalpy_entropy_offset(fluid, 0, 0, "RESET");
966  } else {
967  throw ValueError(format("Reference state string is invalid: [%s]", reference_state.c_str()));
968  }
969  }
970 }
971 void set_reference_stateD(const std::string& Ref, double T, double rhomolar, double hmolar0, double smolar0) {
972  std::vector<std::string> _comps(1, Ref);
974 
975  HEOS.update(DmolarT_INPUTS, rhomolar, T);
976 
977  // Get current values for the enthalpy and entropy
978  double deltah = HEOS.hmolar() - hmolar0; // offset from specified enthalpy in J/mol
979  double deltas = HEOS.smolar() - smolar0; // offset from specified entropy in J/mol/K
980  double delta_a1 = deltas / (HEOS.gas_constant());
981  double delta_a2 = -deltah / (HEOS.gas_constant() * HEOS.get_reducing_state().T);
982  set_fluid_enthalpy_entropy_offset(Ref, delta_a1, delta_a2, "custom");
983 }
984 
985 std::string get_global_param_string(const std::string& ParamName) {
986  if (!ParamName.compare("version")) {
987  return version;
988  } else if (!ParamName.compare("gitrevision")) {
989  return gitrevision;
990  } else if (!ParamName.compare("errstring")) {
991  std::string temp = error_string;
992  error_string = "";
993  return temp;
994  } else if (!ParamName.compare("warnstring")) {
995  std::string temp = warning_string;
996  warning_string = "";
997  return temp;
998  } else if (!ParamName.compare("FluidsList") || !ParamName.compare("fluids_list") || !ParamName.compare("fluidslist")) {
999  return get_fluid_list();
1000  } else if (!ParamName.compare("incompressible_list_pure")) {
1002  } else if (!ParamName.compare("incompressible_list_solution")) {
1004  } else if (!ParamName.compare("mixture_binary_pairs_list")) {
1006  } else if (!ParamName.compare("parameter_list")) {
1007  return get_csv_parameter_list();
1008  } else if (!ParamName.compare("predefined_mixtures")) {
1009  return get_csv_predefined_mixtures();
1010  } else if (!ParamName.compare("HOME")) {
1011  return get_home_dir();
1012  } else if (ParamName == "REFPROP_version") {
1014  } else if (ParamName == "cubic_fluids_schema") {
1016  } else if (ParamName == "cubic_fluids_list") {
1018  } else if (ParamName == "pcsaft_fluids_schema") {
1020  } else {
1021  throw ValueError(format("Input parameter [%s] is invalid", ParamName.c_str()));
1022  }
1023 };
1024 #if defined(ENABLE_CATCH)
1025 TEST_CASE("Check inputs to get_global_param_string", "[get_global_param_string]") {
1026  const int num_good_inputs = 8;
1027  std::string good_inputs[num_good_inputs] = {
1028  "version", "gitrevision", "fluids_list", "incompressible_list_pure", "incompressible_list_solution", "mixture_binary_pairs_list",
1029  "parameter_list", "predefined_mixtures"};
1030  std::ostringstream ss3c;
1031  for (int i = 0; i < num_good_inputs; ++i) {
1032  ss3c << "Test for" << good_inputs[i];
1033  SECTION(ss3c.str(), "") {
1034  CHECK_NOTHROW(CoolProp::get_global_param_string(good_inputs[i]));
1035  };
1036  }
1037  CHECK_THROWS(CoolProp::get_global_param_string(""));
1038 };
1039 #endif
1040 std::string get_fluid_param_string(const std::string& FluidName, const std::string& ParamName) {
1041  std::string backend, fluid;
1042  extract_backend(FluidName, backend, fluid);
1043  shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory(backend, fluid));
1044  return AS->fluid_param_string(ParamName);
1045 }
1046 #if defined(ENABLE_CATCH)
1047 TEST_CASE("Check inputs to get_fluid_param_string", "[get_fluid_param_string]") {
1048  const int num_good_inputs = 10;
1049  std::string good_inputs[num_good_inputs] = {"aliases",
1050  "CAS",
1051  "ASHRAE34",
1052  "REFPROPName",
1053  "BibTeX-CONDUCTIVITY",
1054  "BibTeX-EOS",
1055  "BibTeX-CP0",
1056  "BibTeX-SURFACE_TENSION",
1057  "BibTeX-MELTING_LINE",
1058  "BibTeX-VISCOSITY"};
1059  std::ostringstream ss3c;
1060  for (int i = 0; i < num_good_inputs; ++i) {
1061  ss3c << "Test for" << good_inputs[i];
1062  SECTION(ss3c.str(), "") {
1063  CHECK_NOTHROW(CoolProp::get_fluid_param_string("Water", good_inputs[i]));
1064  };
1065  }
1066  CHECK_THROWS(CoolProp::get_fluid_param_string("", "aliases"));
1067  CHECK_THROWS(CoolProp::get_fluid_param_string("Water", ""));
1068  CHECK_THROWS(CoolProp::get_fluid_param_string("Water", "BibTeX-"));
1069  CHECK(CoolProp::get_fluid_param_string("Water", "pure") == "true");
1070  CHECK(CoolProp::get_fluid_param_string("R410A", "pure") == "false");
1071 };
1072 #endif
1073 
1074 std::string phase_lookup_string(phases Phase) {
1075  switch (Phase) {
1076  case iphase_liquid:
1077  return "liquid";
1078  case iphase_supercritical:
1079  return "supercritical";
1081  return "supercritical_gas";
1083  return "supercritical_liquid";
1084  case iphase_critical_point:
1085  return "critical_point";
1086  case iphase_gas:
1087  return "gas";
1088  case iphase_twophase:
1089  return "twophase";
1090  case iphase_unknown:
1091  return "unknown";
1092  case iphase_not_imposed:
1093  return "not_imposed";
1094  }
1095  throw ValueError("I should never be thrown");
1096 }
1097 std::string PhaseSI(const std::string& Name1, double Prop1, const std::string& Name2, double Prop2, const std::string& FluidName) {
1098  double Phase_double = PropsSI("Phase", Name1, Prop1, Name2, Prop2, FluidName); // Attempt to get "Phase" from PropsSI()
1099  if (!ValidNumber(Phase_double)) { // if the returned phase is invalid...
1100  std::string strPhase = phase_lookup_string(iphase_unknown); // phase is unknown.
1101  std::string strError = get_global_param_string("errstring").c_str(); // fetch waiting error string
1102  if (strError != "") { // if error string is not empty,
1103  strPhase.append(": " + strError); // append it to the phase string.
1104  }
1105  return strPhase; // return the "unknown" phase string
1106  } // else
1107  std::size_t Phase_int = static_cast<std::size_t>(Phase_double); // convert returned phase to int
1108  return phase_lookup_string(static_cast<phases>(Phase_int)); // return phase as a string
1109 }
1110 
1111 /*
1112 std::string PhaseSI(const std::string &Name1, double Prop1, const std::string &Name2, double Prop2, const std::string &FluidName, const std::vector<double> &z)
1113 {
1114  double Phase_double = PropsSI("Phase",Name1,Prop1,Name2,Prop2,FluidName,z);
1115  if (!ValidNumber(Phase_double)){ return "";}
1116  std::size_t Phase_int = static_cast<std::size_t>(Phase_double);
1117  return phase_lookup_string(static_cast<phases>(Phase_int));
1118 }
1119 */
1120 } /* namespace CoolProp */