CoolProp  4.2.5
An open-source fluid property and humid air property database
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
FluidClass.cpp
Go to the documentation of this file.
1 #define _CRT_SECURE_NO_WARNINGS
2 
3 #include "rapidjson_CoolProp.h"
4 
5 #include <stdlib.h>
6 #include <string>
7 #include <vector>
8 #include <map>
9 #include <algorithm>
10 #include <list>
11 #include <exception>
12 #include <iostream>
13 #include <fstream>
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include "math.h"
17 #include <cmath>
18 
19 #include "MatrixMath.h"
20 #include "Helmholtz.h"
21 #include "FluidClass.h"
22 #include "CoolProp.h"
23 
24 #include "REFPROP.h"
25 #include "Solvers.h"
26 #include "CPState.h"
27 #include "Brine.h"
29 
30 
31 #ifdef __ISWINDOWS__
32  #define _USE_MATH_DEFINES
33  #include "float.h"
34 #else
35  #ifndef DBL_EPSILON
36  #include <limits>
37  #define DBL_EPSILON std::numeric_limits<double>::epsilon()
38  #endif
39 #endif
40 
41 #ifndef M_PI
42 #define M_PI 3.14159265358979323846
43 #endif
44 
45 static const bool use_cache = false;
46 static bool UseCriticalSpline = true;
47 
48 
49 double JSON_lookup_double(rapidjson::Document &root, std::string FluidName, std::string key)
50 {
51  if (root.HasMember(FluidName.c_str()) && root[FluidName.c_str()][key.c_str()].IsDouble()){
52  return root[FluidName.c_str()][key.c_str()].GetDouble();
53  }
54  else if (root.HasMember(FluidName.c_str()) && root[FluidName.c_str()][key.c_str()].IsInt()){
55  return root[FluidName.c_str()][key.c_str()].GetInt();
56  }
57  else{
58  return _HUGE;
59  }
60 }
61 
62 std::string JSON_lookup_string(rapidjson::Document &root, std::string FluidName, std::string key)
63 {
64  if (root.HasMember(FluidName.c_str()) && root[FluidName.c_str()][key.c_str()].IsString()){
65  return root[FluidName.c_str()][key.c_str()].GetString();
66  }
67  else{
68  return "";
69  }
70 }
71 
72 std::string JSON_lookup_CAS(rapidjson::Document &root, std::string FluidName)
73 {
74  if (root.HasMember(FluidName.c_str()) && root[FluidName.c_str()].IsString()){
75  return root[FluidName.c_str()].GetString();
76  }
77  else{
78  return "";
79  }
80 }
81 
82 std::vector<double> JSON_lookup_dblvector(rapidjson::Document &root, std::string FluidName, std::string key)
83 {
84  std::vector<double> v;
85 
86  if (root.HasMember(FluidName.c_str()) && root[FluidName.c_str()][key.c_str()].IsArray()){
87  rapidjson::Value& arr = root[FluidName.c_str()][key.c_str()];
88  v.resize(arr.Size());
89 
90  for (rapidjson::SizeType i = 0; i < arr.Size(); i++)
91  {
92  v[i] = arr[i].GetDouble();
93  }
94  return v;
95  }
96  else{
97  v.resize(0);
98  return v;
99  }
100 }
101 std::vector<int> JSON_lookup_intvector(rapidjson::Document &root, std::string FluidName, std::string key)
102 {
103  std::vector<int> v;
104 
105  if (root.HasMember(FluidName.c_str()) && root[FluidName.c_str()][key.c_str()].IsArray()){
106  rapidjson::Value& arr = root[FluidName.c_str()][key.c_str()];
107  v.resize(arr.Size());
108 
109  for (rapidjson::SizeType i = 0; i < arr.Size(); i++)
110  {
111  v[i] = arr[i].GetInt();
112  }
113  return v;
114  }
115  else{
116  v.resize(0);
117  return v;
118  }
119 }
120 
121 
122 
123 
124 
125 
126 
127 
130 {
131 private:
132  double p,T;
133  Fluid *pFluid;
134 public:
135  DensityTpResids(Fluid *pFluid, double T, double p){this->pFluid = pFluid; this->p = p; this->T = T;};
137 
138  double call(double rho)
139  {
140  return this->p - pFluid->pressure_Trho(T,rho);
141  }
142 };
143 //
144 
145 // Destructor needs to free all dynamically allocated objects
146 // In this case, the phir and phi0 components
148 {
149  while (!phirlist.empty())
150  {
151  delete phirlist.back();
152  phirlist.pop_back();
153  }
154  while (!phi0list.empty())
155  {
156  delete phi0list.back();
157  phi0list.pop_back();
158  }
159  if (h_ancillary != NULL){ delete h_ancillary; h_ancillary = NULL;}
160  if (s_ancillary != NULL){ delete s_ancillary; s_ancillary = NULL;}
161 }
163 {
164  // Set the reducing values from the pointer
165  reduce = *preduce;
166 
167  // Get the critical molar density in mol/m^3 (kg/m^3)*(kmol/kg)*(1000 mol/kmol)
168  reduce.rhobar = reduce.rho/params.molemass*1000;
169  crit.rhobar = crit.rho/params.molemass*1000;
170 
171  // Set the enthalpy and entropy at the critical point and the reducing point
176 
177  // REFPROP name is equal to fluid name if not provided
178  if (REFPROPname.empty())
179  {
180  REFPROPname = name;
181  }
182 
183  // Set the triple-point pressure if not set in code
185  double pL,pV,rhoL,rhoV;
186  saturation_T(limits.Tmin, false, pL, pV, rhoL, rhoV);
187  HS.sV_Tmin = entropy_Trho(limits.Tmin, rhoV);
188  HS.sL_Tmin = entropy_Trho(limits.Tmin, rhoL);
191  params.rhoVtriple = rhoV;
192  params.rhoLtriple = rhoL;
193 
194  // Set the triple-point pressure, over-writing whatever is provided by the class
195  // Use the dewpoint pressure for pseudo-pure fluids
196  params.ptriple = pV;
197 
199  h_ancillary = new AncillaryCurveClass(this,std::string("H"));
200  s_ancillary = new AncillaryCurveClass(this,std::string("S"));
201 
202  // The CAS number for the fluid
203  params.CAS = JSON_lookup_CAS(JSON_CAS,this->name);
204 
205  // Load up environmental factors for this fluid including ODP, GWP, etc.
206  environment.ODP = JSON_lookup_double(JSON,this->params.CAS,"ODP");
207  environment.GWP20 = JSON_lookup_double(JSON,this->params.CAS,"GWP20");
208  environment.GWP100 = JSON_lookup_double(JSON,this->params.CAS,"GWP100");
209  environment.GWP500 = JSON_lookup_double(JSON,this->params.CAS,"GWP500");
210  environment.HH = JSON_lookup_double(JSON,this->params.CAS,"HH");
211  environment.FH = JSON_lookup_double(JSON,this->params.CAS,"FH");
212  environment.PH = JSON_lookup_double(JSON,this->params.CAS,"PH");
213  environment.ASHRAE34 = JSON_lookup_string(JSON,this->params.CAS,"ASHRAE34");
214 
215  limits.Tmin = JSON_lookup_double(JSON,this->params.CAS,"Tmin");
216  limits.Tmax = JSON_lookup_double(JSON,this->params.CAS,"Tmax");
217  limits.pmax = JSON_lookup_double(JSON,this->params.CAS,"pmax");
218 
219  // Inputs for the enthalpy-entropy solver which is the most problematic solver
220  HS.hmax = JSON_lookup_double(JSON,this->params.CAS,"hsatVmax");
221  HS.T_hmax = JSON_lookup_double(JSON,this->params.CAS,"T_hsatVmax");
222  HS.s_hmax = JSON_lookup_double(JSON,this->params.CAS,"s_hsatVmax");
223  HS.rho_hmax = JSON_lookup_double(JSON,this->params.CAS,"rho_hsatVmax");
224  HS.a_hs_satL = JSON_lookup_dblvector(JSON,this->params.CAS,"a_hs_satL");
225  HS.n_hs_satL = JSON_lookup_intvector(JSON,this->params.CAS,"n_hs_satL");
226 
227  // REFPROP name is equal to fluid name if not provided
228  if (!params.HSReferenceState.empty())
229  {
230  set_reference_stateP(this,params.HSReferenceState);
231  }
232 }
233 //--------------------------------------------
234 // Residual Part
235 //--------------------------------------------
236 
237 double Fluid::phir(double tau, double delta)
238 {
239  if (use_cache && double_equal(tau,cache.phir.tau) && double_equal(delta,cache.phir.delta))
240  {
241  return cache.phir.cached_val;
242  }
243  else
244  {
245  double summer = 0;
246  for (std::vector<phi_BC*>::iterator it = phirlist.begin(); it != phirlist.end(); it++)
247  summer += (*it)->base(tau,delta);
248  cache.phir.tau = tau;
249  cache.phir.delta = delta;
250  cache.phir.cached_val = summer;
251  return summer;
252  }
253 }
254 
255 
256 
257 double Fluid::dphir_dDelta(double tau, double delta)
258 {
259  if (use_cache && double_equal(tau,cache.dphir_dDelta.tau) && double_equal(delta,cache.dphir_dDelta.delta))
260  {
262  }
263  else
264  {
265  double summer = 0;
266  for (std::vector<phi_BC*>::iterator it = phirlist.begin(); it != phirlist.end(); ++it)
267  {
268  summer += (*it)->dDelta(tau,delta);
269  }
270  cache.dphir_dDelta.tau = tau;
271  cache.dphir_dDelta.delta = delta;
272  cache.dphir_dDelta.cached_val = summer;
273  return summer;
274  }
275 }
276 double Fluid::d2phir_dDelta2(double tau, double delta)
277 {
278  if (use_cache && double_equal(tau,cache.d2phir_dDelta2.tau) && double_equal(delta,cache.d2phir_dDelta2.delta))
279  {
281  }
282  else
283  {
284  double summer = 0;
285  for (std::vector<phi_BC*>::iterator it = phirlist.begin(); it != phirlist.end(); it++)
286  {
287  summer += (*it)->dDelta2(tau,delta);
288  }
289  cache.d2phir_dDelta2.tau = tau;
290  cache.d2phir_dDelta2.delta = delta;
292  return summer;
293  }
294 }
295 double Fluid::dphir_dTau(double tau, double delta)
296 {
297  if (use_cache && double_equal(tau,cache.dphir_dTau.tau) && double_equal(delta,cache.dphir_dTau.delta))
298  {
299  return cache.dphir_dTau.cached_val;
300  }
301  else
302  {
303  double summer = 0;
304  for (std::vector<phi_BC*>::iterator it = phirlist.begin(); it != phirlist.end(); it++)
305  summer += (*it)->dTau(tau,delta);
306  cache.dphir_dTau.tau = tau;
307  cache.dphir_dTau.delta = delta;
308  cache.dphir_dTau.cached_val = summer;
309  return summer;
310  }
311 }
312 double Fluid::d2phir_dTau2(double tau, double delta)
313 {
314  if (use_cache && double_equal(tau,cache.d2phir_dTau2.tau) && double_equal(delta,cache.d2phir_dTau2.delta))
315  {
317  }
318  else
319  {
320  double summer = 0;
321  for (std::vector<phi_BC*>::iterator it = phirlist.begin(); it != phirlist.end(); it++)
322  summer += (*it)->dTau2(tau,delta);
323  cache.d2phir_dTau2.tau = tau;
324  cache.d2phir_dTau2.delta = delta;
325  cache.d2phir_dTau2.cached_val = summer;
326  return summer;
327  }
328 }
329 double Fluid::d2phir_dDelta_dTau(double tau, double delta)
330 {
332  {
334  }
335  else
336  {
337  double summer = 0;
338  for (std::vector<phi_BC*>::iterator it = phirlist.begin(); it != phirlist.end(); it++)
339  summer += (*it)->dDelta_dTau(tau,delta);
343  return summer;
344  }
345 }
346 double Fluid::d3phir_dTau3(double tau, double delta)
347 {
348  double summer = 0;
349  for (std::vector<phi_BC*>::iterator it = phirlist.begin(); it != phirlist.end(); it++)
350  summer += (*it)->dTau3(tau,delta);
351  return summer;
352 }
353 double Fluid::d3phir_dDelta3(double tau, double delta)
354 {
355  double summer = 0;
356  for (std::vector<phi_BC*>::iterator it = phirlist.begin(); it != phirlist.end(); it++)
357  {
358  summer += (*it)->dDelta3(tau,delta);
359  }
360  return summer;
361 }
362 double Fluid::d3phir_dDelta_dTau2(double tau, double delta)
363 {
364  double summer = 0;
365  for (std::vector<phi_BC*>::iterator it = phirlist.begin(); it != phirlist.end(); it++)
366  summer += (*it)->dDelta_dTau2(tau,delta);
367  return summer;
368 }
369 
370 double Fluid::d3phir_dDelta2_dTau(double tau, double delta)
371 {
372  double summer = 0;
373  for (std::vector<phi_BC*>::iterator it = phirlist.begin(); it != phirlist.end(); it++)
374  summer += (*it)->dDelta2_dTau(tau,delta);
375  return summer;
376 }
377 
378 //--------------------------------------------
379 // Ideal Gas Part
380 //--------------------------------------------
381 double Fluid::phi0(double tau, double delta)
382 {
383  double summer = 0;
384  for (std::vector<phi_BC*>::iterator it = phi0list.begin(); it != phi0list.end(); it++){
385  summer += (*it)->base(tau,delta);
386  }
387  return summer;
388 }
389 double Fluid::dphi0_dDelta(double tau, double delta)
390 {
391  double summer = 0;
392  for (std::vector<phi_BC*>::iterator it = phi0list.begin(); it != phi0list.end(); it++)
393  summer += (*it)->dDelta(tau,delta);
394  return summer;
395 }
396 double Fluid::d2phi0_dDelta2(double tau, double delta)
397 {
398  double summer = 0;
399  for (std::vector<phi_BC*>::iterator it = phi0list.begin(); it != phi0list.end(); it++)
400  summer += (*it)->dDelta2(tau,delta);
401  return summer;
402 }
403 double Fluid::dphi0_dTau(double tau, double delta)
404 {
405  double summer = 0;
406  for (std::vector<phi_BC*>::iterator it = phi0list.begin(); it != phi0list.end(); it++){
407  summer += (*it)->dTau(tau,delta);
408  }
409  return summer;
410 }
411 double Fluid::d2phi0_dTau2(double tau, double delta)
412 {
413  double summer = 0;
414  for (std::vector<phi_BC*>::iterator it = phi0list.begin(); it != phi0list.end(); it++){
415  summer += (*it)->dTau2(tau,delta);
416  }
417  return summer;
418 }
419 double Fluid::d3phi0_dTau3(double tau, double delta)
420 {
421  double summer = 0;
422  for (std::vector<phi_BC*>::iterator it = phi0list.begin(); it != phi0list.end(); it++){
423  summer += (*it)->dTau3(tau,delta);
424  }
425  return summer;
426 }
427 double Fluid::d2phi0_dDelta_dTau(double tau, double delta)
428 {
429  double summer = 0;
430  for (std::vector<phi_BC*>::iterator it = phi0list.begin(); it != phi0list.end(); it++)
431  summer += (*it)->dDelta_dTau(tau,delta);
432  return summer;
433 }
434 
435 bool Fluid::isAlias(std::string name)
436 {
437  // Returns true if name is an alias of the fluid
438  for (std::vector<std::string>::iterator it = aliases.begin(); it != aliases.end(); it++)
439  if (name.compare((*it))==0)
440  {
441  return true;
442  }
443  return false;
444 }
445 // ----------------------------------------
446 // Properties
447 // ----------------------------------------
448 
449 double Fluid::pressure_Trho(double T, double rho)
450 {
451  double delta,tau;
452  tau=reduce.T/T;
453  delta=rho/reduce.rho;
454  return R()*T*rho*(1.0+delta*dphir_dDelta(tau,delta));
455 }
456 
457 double Fluid::enthalpy_Trho(double T, double rho)
458 {
459  double delta,tau;
460  tau=reduce.T/T;
461  delta=rho/reduce.rho;
462  return R()*T*(1.0+tau*(dphi0_dTau(tau,delta)+dphir_dTau(tau,delta))+delta*dphir_dDelta(tau,delta));
463 }
464 
465 double Fluid::internal_energy_Trho(double T, double rho)
466 {
467  double delta,tau;
468  tau=reduce.T/T;
469  delta=rho/reduce.rho;
470  return R()*T*tau*(dphi0_dTau(tau,delta)+dphir_dTau(tau,delta));
471 }
472 
473 double Fluid::entropy_Trho(double T, double rho)
474 {
475  double delta,tau;
476  tau=reduce.T/T;
477  delta=rho/reduce.rho;
478  return R()*(tau*(dphi0_dTau(tau,delta)+dphir_dTau(tau,delta))-phi0(tau,delta)-phir(tau,delta));
479 }
480 double Fluid::specific_heat_v_Trho(double T, double rho)
481 {
482  double delta,tau;
483  tau=reduce.T/T;
484  delta=rho/reduce.rho;
485  return -R()*tau*tau*(d2phi0_dTau2(tau,delta)+d2phir_dTau2(tau,delta));
486 }
487 double Fluid::specific_heat_p_Trho(double T, double rho)
488 {
489  double delta,tau,c1,c2,dphir_dDelta_;
490  tau=reduce.T/T;
491  delta=rho/reduce.rho;
492  dphir_dDelta_=dphir_dDelta(tau,delta);
493  c1=pow(1.0+delta*dphir_dDelta_-delta*tau*d2phir_dDelta_dTau(tau,delta),2);
494  c2=(1.0+2.0*delta*dphir_dDelta_+pow(delta,2)*d2phir_dDelta2(tau,delta));
495  return R()*(-pow(tau,2)*(d2phi0_dTau2(tau,delta)+d2phir_dTau2(tau,delta))+c1/c2);
496 }
498 {
499  double tau;
500  tau=reduce.T/T;
501  return R()*(1-tau*tau*d2phi0_dTau2(tau, 1e-12));
502 }
503 double Fluid::speed_sound_Trho(double T, double rho)
504 {
505  double delta,tau,c1,c2;
506  tau=reduce.T/T;
507  delta=rho/reduce.rho;
508 
509  c1=-specific_heat_v_Trho(T,rho)/R();
510  c2=(1.0+2.0*delta*dphir_dDelta(tau,delta)+pow(delta,2)*d2phir_dDelta2(tau,delta));
511  return sqrt(-c2*T*specific_heat_p_Trho(T,rho)/c1);
512 }
513 double Fluid::gibbs_Trho(double T,double rho)
514 {
515  double delta,tau;
516  tau=reduce.T/T;
517  delta=rho/reduce.rho;
518 
519  return R()*T*(1+phi0(tau,delta)+phir(tau,delta)+delta*dphir_dDelta(tau,delta));
520 }
521 double Fluid::dpdT_Trho(double T,double rho)
522 {
523  double delta,tau;
524  tau=reduce.T/T;
525  delta=rho/reduce.rho;
526 
527  return rho*R()*(1+delta*dphir_dDelta(tau,delta)-delta*tau*d2phir_dDelta_dTau(tau,delta));
528 }
529 double Fluid::dpdrho_Trho(double T,double rho)
530 {
531  double delta,tau;
532  tau=reduce.T/T;
533  delta=rho/reduce.rho;
534 
535  return R()*T*(1+2*delta*dphir_dDelta(tau,delta)+delta*delta*d2phir_dDelta2(tau,delta));
536 }
537 double Fluid::drhodT_p_Trho(double T,double rho)
538 {
539  return DerivTerms(iDERdrho_dT__p,T,rho,this);
540 }
541 
543 double Fluid::density_Tp_Soave(double T, double p, int iValue)
544 {
545  double omega, R, m, a, b, A, B, r, q, D, u, Y, Y1, Y2, Y3, theta, phi, rho;
546  omega = params.accentricfactor;
547  R = params.R_u/params.molemass*1000; // SI units are used internally
548  m = 0.480+1.574*omega-0.176*omega*omega;
549  a = 0.42747*R*R*crit.T*crit.T/crit.p.Pa*pow(1+m*(1-sqrt(T/reduce.T)),2);
550  b = 0.08664*R*crit.T/crit.p.Pa;
551  A = a*p/(R*R*T*T);
552  B = b*p/(R*T);
553 
554  // Terms in reduced cubic equation
555  r = (3*(A-B-B*B)-1)/3;
556  q = -2/27.0+1.0/3.0*(A-B-B*B)-A*B;
557 
558  // Discriminant
559  D = pow(r/3,3)+pow(q/2,2);
560 
561  if (D>0)
562  {
563  // One real root
564  u = pow(-q/2+sqrt(D),1.0/3.0);
565  Y = u-r/(3*u);
566  rho = p/(R*T*(Y+1.0/3.0));
567  return rho;
568  }
569  else
570  {
571  theta = sqrt(-r*r*r/27);
572  phi = acos(-q/(2*theta));
573 
574  Y1 = 2*pow(theta,1.0/3.0)*cos(phi/3.0);
575  Y2 = 2*pow(theta,1.0/3.0)*cos(phi/3.0+2.0*M_PI/3.0);
576  Y3 = 2*pow(theta,1.0/3.0)*cos(phi/3.0+4.0*M_PI/3.0);
577 
578  double rho1 = p/(R*T*(Y1+1.0/3.0));
579  double rho2 = p/(R*T*(Y2+1.0/3.0));
580  double rho3 = p/(R*T*(Y3+1.0/3.0));
581 
582  double p1 = pressure_Trho(T,rho1);
583  double p2 = pressure_Trho(T,rho2);
584  double p3 = pressure_Trho(T,rho3);
585 
586  double min_error = 9e50;
587 
588  if (ValidNumber(p1) && fabs(p1-p) < min_error){min_error = fabs(p1-p); rho = rho1;}
589  if (ValidNumber(p2) && fabs(p2-p) < min_error){min_error = fabs(p2-p); rho = rho2;}
590  if (ValidNumber(p3) && fabs(p3-p) < min_error){min_error = fabs(p3-p); rho = rho3;}
591 
592  if (iValue > -1)
593  {
594  if (iValue == 0)
595  {
596  return rho1;
597  }
598  else if (iValue == 1)
599  {
600  return rho3;
601  }
602  }
603  return rho;
604  }
605 }
606 
607 double Fluid::density_Tp_PengRobinson(double T, double p, int solution)
608 {
609  double A, B, m, a, b, Z, rhobar;
610 
611  double Rbar = 8314.472; // Using SI units internally
612 
613  b = 0.077796074*(Rbar*reduce.T)/(reduce.p.Pa);
614  B = 0.077796074*p/reduce.p.Pa*reduce.T/T;
615 
616  m = 0.37464 + 1.54226*params.accentricfactor-0.26992*pow(params.accentricfactor,2);
617  a = 0.45724*pow(Rbar*reduce.T,2)/reduce.p.Pa*pow(1+m*(1-sqrt(T/reduce.T)),2)*1000;
618  A = a*p/(Rbar*Rbar*T*T)/1000;
619 
620  double x0, x1, x2;
621  solve_cubic(1, -1+B, A-3*B*B-2*B, -A*B+B*B+B*B*B, &x0, &x1, &x2);
622  std::vector<double> solns;
623  solns.push_back(x0);
624  solns.push_back(x1);
625  solns.push_back(x2);
626 
627  // Erase negative solutions and unstable solutions
628  // Stable solutions are those for which dpdrho is positive
629  for (int i = (int)solns.size()-1; i >= 0; i--)
630  {
631  if (solns[i] < 0)
632  {
633  solns.erase(solns.begin()+i);
634  }
635  else
636  {
637  //double v = (solns[i]*Rbar*T)/p; //[mol/L]
638  //double dpdrho = -v*v*(-Rbar*T/pow(v-b,2)+a*(2*v+2*b)/pow(v*v+2*b*v-b*b,2));
639  //if (dpdrho < 0)
640  //{
641  // solns.erase(solns.begin()+i);
642  //}
643  }
644  }
645 
646  if (solution == 0)
647  {
648  Z = *std::min_element(solns.begin(), solns.end());
649  }
650  else if (solution == 1)
651  {
652  Z = *std::max_element(solns.begin(), solns.end());
653  }
654  else
655  {
656  throw ValueError();
657  }
658 
659  rhobar = p/(Z*Rbar*T); //[mol/L]
660  //double vbar = 1/rhobar;
661  //double p_check = Rbar*T/(vbar-b)-a/(vbar*vbar+2*b*vbar-b*b);
662 
663  return rhobar*params.molemass;
664 }
665 
687 double Fluid::temperature_prho_PengRobinson(double p, double rho)
688 {
689  double omega, R, kappa, a, b, A, B, C, D, V= 1/rho;
690  omega = params.accentricfactor;
691  R = this->R();
692 
693  kappa = 0.37464+1.54226*omega-0.26992*omega*omega;
694  a = 0.457235*R*R*crit.T*crit.T/crit.p.Pa;
695  b = 0.077796*R*crit.T/crit.p.Pa;
696  double den = V*V+2*b*V-b*b;
697 
698  // A sqrt(Tr)^2 + B sqrt(Tr) + C = 0
699  A = R*reduce.T/(V-b)-a*kappa*kappa/(den);
700  B = +2*a*kappa*(1+kappa)/(den);
701  C = -a*(1+2*kappa+kappa*kappa)/(den)-p;
702 
703  D = B*B-4*A*C;
704 
705  double sqrt_Tr1 = (-B+sqrt(B*B-4*A*C))/(2*A);
706  //double sqrt_Tr2 = (-B-sqrt(B*B-4*A*C))/(2*A);
707  return sqrt_Tr1*sqrt_Tr1*reduce.T;
708 }
709 
710 double Fluid::temperature_prho_VanDerWaals(double p, double rho)
711 {
712  double R = 8314.472; // Using SI units internally
713  double a = 27*pow(R*reduce.T,2)/(64*reduce.p.Pa);
714  double b = R*reduce.T/(8*reduce.p.Pa);
715  double Vm = 1/rho*params.molemass;
716  return 1.0/R*(p+a/Vm/Vm)*(Vm-b);
717 }
718 
719 double Fluid::density_Tp(double T, double p)
720 {
721  // If the guess value is not provided, calculate the guess density using Peng-Robinson
722  // This overload is used to pre-calculate the guess for density using PR if possible
723  if (get_debug_level()>8){
724  std::cout << format("%s:%d: Fluid::density_Tp(double T, double p)\n", __FILE__, __LINE__, T, p).c_str();
725  }
726  if (T < 0)
727  {
728  throw ValueError(format("T [%g] is less than zero",T));
729  }
730  return density_Tp(T,p,_get_rho_guess(T,p));
731 }
732 
733 double Fluid::density_Tp(double T, double p, double rho_guess)
734 {
735  long double tau,dpdrho__constT,dpddelta__constT, error=999,R,p_EOS,rho,change=999;
736 
737  R = params.R_u/params.molemass*1000; // SI units are used internally
738  tau = reduce.T/T;
739 
740  if (get_debug_level()>8){
741  std::cout << format("%s:%d: Fluid::density_Tp(T=%g, p=%g, rho_guess=%g)\n",__FILE__,__LINE__,T,p,rho_guess).c_str();
742  }
743  // Start with the guess value
744  // The first step, use the derivative of dp/drho|T in order to get the next value
745  // In subsequent steps, use secant method because each evaluation of newton step requires two evaluations of derivatives with respect to delta
746  rho=rho_guess;
747  int iter=1;
748  long double delta = rho/reduce.rho;
749 
750  while (std::abs(error) > 1e-9 && std::abs(change) > 1e-13)
751  {
752  // Needed for both p and p derivative
753  // Run once to cut down on calculations
754  long double dphirdDelta = dphir_dDelta(tau, delta);
755 
756  // Pressure from equation of state
757  p_EOS = reduce.rho*delta*R*T*(1+delta*dphirdDelta);
758 
759  // Residual
760  error = (p_EOS-p)/p;
761 
762  // Use Newton's method to find the density since the derivative of pressure w.r.t. density is known from EOS
763  dpdrho__constT = R*T*(1+2*delta*dphirdDelta+delta*delta*d2phir_dDelta2(tau,delta));
764 
765  dpddelta__constT = dpdrho__constT*reduce.rho;
766 
767  // Update the step using Newton's method
768  change = (p_EOS-p)/dpddelta__constT;
769  delta -= change;
770 
771  iter++;
772 
773  if (iter>50)
774  {
775  throw SolutionError(format("Number of steps in density_TP has exceeded 30 with inputs T=%g,p=%g,rho_guess=%g for fluid %s",T,p,rho_guess,name.c_str()));
776  }
777  if (!ValidNumber(delta))
778  {
779  throw SolutionError(format("Non-numeric density obtained in density_TP with inputs T=%g,p=%g,rho_guess=%g for fluid %s",T,p,rho_guess,name.c_str()));
780  }
781  }
782  if (get_debug_level()>8){
783  std::cout << format("%s:%d: Fluid::density_Tp(T = %g, p = %g, rho_guess = %g) = %g\n",__FILE__,__LINE__,T,p,rho_guess,rho).c_str();
784  }
785  return delta*reduce.rho;
786 }
787 
788 double Fluid::viscosity_Trho( double T, double rho)
789 {
790  long iFluid = get_Fluid_index(ECSReferenceFluid);
791  // Calculate the ECS
792  double mu = viscosity_ECS_Trho(T, rho, get_fluid(iFluid));
793  return mu;
794 }
795 double Fluid::conductivity_Trho( double T, double rho)
796 {
797  long iFluid = get_Fluid_index(ECSReferenceFluid);
798  // Calculate the ECS
799  double lambda = conductivity_ECS_Trho(T, rho, get_fluid(iFluid));
800  return lambda;
801 }
802 
803 void Fluid::saturation_s(double s, int Q, double &Tsatout, double &rhoout, double &TLout, double &TVout, double &rhoLout, double &rhoVout)
804 {
805  class SatFuncClass : public FuncWrapper1D
806  {
807  private:
808  int Q;
809  double s;
810  Fluid * pFluid;
811  public:
812  double rho,pL,pV,rhoL,rhoV,T;
813  SatFuncClass(double s, int Q, Fluid *pFluid){
814  this->s = s;
815  this->Q = Q;
816  this->pFluid = pFluid;
817  };
818  double call(double T){
819  pFluid->saturation_T(T, false, pL, pV, rhoL, rhoV);
820  if (Q == 0){
821  this->rho = rhoL;
822  return pFluid->entropy_Trho(T,rhoL) - this->s;
823  }
824  else if (Q == 1){
825  this->rho = rhoV;
826  return pFluid->entropy_Trho(T,rhoV) - this->s;
827  }
828  else{
829  throw ValueError("Q must be 0 or 1");
830  }
831  //this->T = T;
832  return -_HUGE;
833  };
834  } SatFunc(s, Q, this);
835 
836  if (get_debug_level()>5){
837  std::cout << format("%s:%d: Fluid::saturation_s(%g,%d) \n",__FILE__,__LINE__,s,Q).c_str();
838  }
839  if (isPure == true)
840  {
841  if (enabled_TTSE_LUT)
842  {
843  throw NotImplementedError(format("saturation_s not implemented for TTSE"));
844  return;
845  }
846  else
847  {
848  std::string errstr;
849  Tsatout = Brent(&SatFunc,limits.Tmin,crit.T,1e-16,1e-8,100,&errstr);
850  rhoout = SatFunc.rho;
851  rhoLout = SatFunc.rhoL;
852  rhoVout = SatFunc.rhoV;
853  TLout = SatFunc.T;
854  TVout = SatFunc.T;
855  }
856  }
857  else
858  {
859  throw NotImplementedError("Pseudo-pure not currently supported for saturation_s");
861  //*psatLout = psatL(T);
862  //*psatVout = psatV(T);
863  //try{
864  // *rhosatLout = density_Tp(T, *psatLout, rhosatL(T));
865  // *rhosatVout = density_Tp(T, *psatVout, rhosatV(T));
866  //}
867  //catch (std::exception){
868  // // Near the critical point, the behavior is not very nice, so we will just use the ancillary near the critical point
869  // *rhosatLout = rhosatL(T);
870  // *rhosatVout = rhosatV(T);
871  //}
872  //return;
873  }
874 }
875 void Fluid::saturation_h(double h, double Tmin, double Tmax, int Q, double &Tsatout, double &rhoout, double &TsatLout, double &TsatVout, double &rhoLout, double &rhoVout)
876 {
877  class SatFuncClass : public FuncWrapper1D
878  {
879  private:
880  int Q;
881  double h;
882  Fluid * pFluid;
883  public:
884  double rho,pL,pV,rhoL,rhoV,T;
885  SatFuncClass(double h, int Q, Fluid *pFluid){
886  this->h = h;
887  this->Q = Q;
888  this->pFluid = pFluid;
889  };
890  double call(double T){
891  this->T = T;
892  pFluid->saturation_T(T, false, pL, pV, rhoL, rhoV);
893  if (Q == 0){
894  this->rho = rhoL;
895  return pFluid->enthalpy_Trho(T,rhoL) - this->h;
896  }
897  else if (Q == 1){
898  this->rho = rhoV;
899  return pFluid->enthalpy_Trho(T,rhoV) - this->h;
900  }
901  else{
902  throw ValueError("Q must be 0 or 1");
903  }
904 
905  };
906  } SatFunc(h, Q, this);
907 
908  if (get_debug_level()>5){
909  std::cout << format("%s:%d: Fluid::saturation_h(%g,%d) \n",__FILE__,__LINE__,h,Q).c_str();
910  }
911  if (isPure == true)
912  {
913  if (enabled_TTSE_LUT)
914  {
915  throw NotImplementedError(format("saturation_h not implemented for TTSE"));
916  return;
917  }
918  else
919  {
920  std::string errstr;
921  Tsatout = Brent(&SatFunc,Tmin,Tmax,1e-16,1e-10,100,&errstr);
922  rhoout = SatFunc.rho;
923  rhoLout = SatFunc.rhoL;
924  rhoVout = SatFunc.rhoV;
925  TsatLout = SatFunc.T;
926  TsatVout = SatFunc.T;
927  }
928  }
929  else
930  {
931  throw NotImplementedError("Pseudo-pure not currently supported");
933  //*psatLout = psatL(T);
934  //*psatVout = psatV(T);
935  //try{
936  // *rhosatLout = density_Tp(T, *psatLout, rhosatL(T));
937  // *rhosatVout = density_Tp(T, *psatVout, rhosatV(T));
938  //}
939  //catch (std::exception){
940  // // Near the critical point, the behavior is not very nice, so we will just use the ancillary near the critical point
941  // *rhosatLout = rhosatL(T);
942  // *rhosatVout = rhosatV(T);
943  //}
944  //return;
945  }
946 }
947 
948 void Fluid::saturation_T(double T, bool UseLUT, double &psatLout, double &psatVout, double &rhosatLout, double &rhosatVout)
949 {
950  double p;
951  if (get_debug_level()>5){
952  std::cout << format("%s:%d: Fluid::saturation_T(%g,%d) \n",__FILE__,__LINE__,T,UseLUT).c_str();
953  }
954  if (T < limits.Tmin){ throw ValueError(format("Your temperature to saturation_T [%g K] is below the minimum temp [%g K]",T,limits.Tmin));}
955  if (isPure==true)
956  {
957  if (enabled_TTSE_LUT)
958  {
959  throw NotImplementedError(format("saturation_T not implemented for TTSE"));
960  return;
961  }
962  else
963  {
964  if (UseCriticalSpline && ValidNumber(CriticalSpline_T.Tend) && T > CriticalSpline_T.Tend)
965  {
967  rhosatLout = CriticalSpline_T.interpolate_rho(this,0,T);
968  rhosatVout = CriticalSpline_T.interpolate_rho(this,1,T);
969  psatLout = pressure_Trho(T,rhosatLout);
970  psatVout = psatLout;
971  return;
972  }
973 
974  try{
975  // Reduce omega in uniform steps
976  double omega = 1.0;
977  do
978  {
979  try{
980  rhosatPure_Akasaka(T, rhosatLout, rhosatVout, p, omega); psatLout = p; psatVout = p; return;
981  break;
982  }
983  catch (std::exception &)
984  {
985  omega -= 0.3;
986  }
987  }
988  while(omega > 0);
989 
990  // We ran out of steps in omega
991  if (omega < 0.05)
992  {
993  throw ValueError();
994  }
995  }
996  catch(std::exception &)
997  {
998  try{
999 
1000  // Reduce omega in uniform steps
1001  double omega = 1.0;
1002  do
1003  {
1004  try{
1005  rhosatPure(T, rhosatLout, rhosatVout, p, omega, false);
1006  psatLout = p;
1007  psatVout = p;
1008  return;
1009  }
1010  catch (std::exception &)
1011  {
1012  omega -= 0.3;
1013  }
1014  }
1015  while(omega > 0);
1016 
1017  // We ran out of steps in omega
1018  if (omega < 0.05)
1019  {
1020  throw ValueError();
1021  }
1022  }
1023  catch (std::exception &){
1024 
1025  rhosatPure_Brent(T,rhosatLout,rhosatVout,p);
1026  psatLout = p;
1027  psatVout = p;
1028  return;
1029  }
1030  }
1031  }
1032  }
1033  else
1034  {
1035  // Pseudo-pure fluid
1036  psatLout = psatL(T); // These ancillaries are used explicitly
1037  psatVout = psatV(T); // These ancillaries are used explicitly
1038  try{
1039  double rhoLanc = rhosatL(T);
1040  double rhoVanc = rhosatV(T);
1041  if (!ValidNumber(rhoLanc) || !ValidNumber(rhoVanc))
1042  {
1043  throw ValueError("pseudo-pure failed");
1044  }
1045 
1046  rhosatLout = density_Tp(T, psatLout, rhosatL(T));
1047  rhosatVout = density_Tp(T, psatVout, rhosatV(T));
1048  if (!ValidNumber(rhosatLout) || !ValidNumber(rhosatVout) ||
1049  fabs(rhoLanc/rhosatLout-1) > 0.1 || fabs(rhoVanc/rhosatVout-1) > 0.1)
1050  {
1051  throw ValueError("pseudo-pure failed");
1052  }
1053  }
1054  catch (std::exception &){
1055  // Near the critical point, the behavior is not very nice, so we will just use the ancillary near the critical point
1056  rhosatLout = rhosatL(T);
1057  rhosatVout = rhosatV(T);
1058  }
1059  return;
1060  }
1061 }
1062 
1063 
1064 void Fluid::rhosatPure_Brent(double T, double &rhoLout, double &rhoVout, double &pout)
1065 {
1066  /*
1067  Use Brent's method around when Akasaka's method fails. Slow but reliable
1068  */
1069 
1070  class SatFuncClass : public FuncWrapper1D
1071  {
1072  private:
1073  double T,gL,gV;
1074  Fluid * pFluid;
1075  public:
1076  double p,rhoL,rhoV;
1077  SatFuncClass(double T, Fluid *pFluid) :T(T), pFluid(pFluid){};
1078  double call(double p){
1079  // Span, 2000 p 56
1080  DensityTpResids DTPR = DensityTpResids(this->pFluid,T,p);
1081  std::string errstr;
1082  //this->rhoL = this->pFluid->rhosatL(T);
1083  //std::cout << Props("D",'Q',0,'T',T,"REFPROP-R245fa") <<std::endl;
1084  rhoL = Brent(&DTPR,rhoL+1e-3,rhoL-1e-3,DBL_EPSILON,1e-8,30,&errstr);
1085  rhoV = Brent(&DTPR,rhoV,pFluid->crit.rho-10*DBL_EPSILON,DBL_EPSILON,1e-8,40,&errstr);
1086  gL = pFluid->gibbs_Trho(T,rhoL);
1087  gV = pFluid->gibbs_Trho(T,rhoV);
1088  return gL-gV;
1089  };
1090  } SatFunc(T,this);
1091 
1092  std::string errstr;
1093  double pmax = reduce.p.Pa;
1094  double pmin;
1095  try
1096  {
1097  pmin = psatV_anc(T-0.1);
1098  }
1099  catch (std::exception &)
1100  {
1101  pmin = 1e-10;
1102  }
1103  if (pmin < params.ptriple)
1104  pmin = params.ptriple;
1105  Brent(&SatFunc,pmin,pmax,DBL_EPSILON,1e-8,30,&errstr);
1106 }
1107 
1109 {
1110 public:
1112  double T,gL,gV,rhoL,rhoV,p;
1113 
1114  rhosatPure_BrentrhoVResidClass(double T, Fluid *pFluid):pFluid(pFluid),T(T) {
1115  this->rhoL = pFluid->rhosatL(T);
1116  };
1117  double call(double rhoV){
1118  double pV = pFluid->pressure_Trho(T,rhoV);
1119  rhoL = pFluid->density_Tp(T,pV,rhoL);
1120  gL = pFluid->gibbs_Trho(T,rhoL);
1121  gV = pFluid->gibbs_Trho(T,rhoV);
1122  this->p = pV;
1123  this->rhoV = rhoV;
1124  return gL-gV;
1125  };
1126 };
1127 
1128 void Fluid::rhosatPure_BrentrhoV(double T, double &rhoLout, double &rhoVout, double &pout)
1129 {
1131 
1132  std::string errstr;
1133  Brent(&SF,(reduce.rho+0.95*rhosatV(T))/2,0.95*rhosatV(T),DBL_EPSILON,1e-12,30,&errstr);
1134  rhoLout = SF.rhoL;
1135  rhoVout = SF.rhoV;
1136  pout = SF.p;
1137 }
1138 
1141 void Fluid::rhosatPure_Akasaka(double T, double &rhoLout, double &rhoVout, double &pout, double omega = 1.0, bool use_guesses)
1142 {
1143  /*
1144  This function implements the method of Akasaka
1145 
1146  R. Akasaka,"A reliable and Useful Method to Determine the Saturation State from
1147  Helmholtz Energy Equations of State",
1148  Journal of Thermal Science and Technology v3 n3,2008
1149 
1150  Ancillary equations are used to get a sensible starting point
1151  */
1152  long double rhoL,rhoV,dphirL,dphirV,ddphirL,ddphirV,phirL,phirV,JL,JV,KL,KV,dJL,dJV,dKL,dKV;
1153  long double DELTA, deltaL=0, deltaV=0, tau=0, error, PL, PV, stepL, stepV;
1154  int iter=0;
1155  // Use the density ancillary function as the starting point for the solver
1156  try
1157  {
1158  if (!use_guesses)
1159  {
1160  // If very close to the critical temp, evaluate the ancillaries for a slightly lower temperature
1161  if (T > 0.99*reduce.T)
1162  {
1163  rhoL=rhosatL(T-1);
1164  rhoV=rhosatV(T-1);
1165  }
1166  else
1167  {
1168  rhoL=rhosatL(T);
1169  rhoV=rhosatV(T);
1170  }
1171  }
1172  else
1173  {
1174  rhoL = rhoLout;
1175  rhoV = rhoVout;
1176  }
1177 
1178  deltaL = rhoL/reduce.rho;
1179  deltaV = rhoV/reduce.rho;
1180  tau = reduce.T/T;
1181  }
1182  catch(NotImplementedError &)
1183  {
1184  double Tc = crit.T;
1185  double pc = crit.p.Pa;
1186  double w = 6.67228479e-09*Tc*Tc*Tc-7.20464352e-06*Tc*Tc+3.16947758e-03*Tc-2.88760012e-01;
1187  double q = -6.08930221451*w -5.42477887222;
1188  double pt = exp(q*(Tc/T-1))*pc;
1189 
1190  double rhoL = density_Tp_Soave(T, pt, 0), rhoV = density_Tp_Soave(T, pt, 1);
1191 
1192  deltaL = rhoL/reduce.rho;
1193  deltaV = rhoV/reduce.rho;
1194  tau = reduce.T/T;
1195  }
1196  if (get_debug_level()>5){
1197  std::cout << format("%s:%d: Akasaka guess values deltaL = %g deltaV = %g tau = %g\n",__FILE__,__LINE__,deltaL, deltaV, tau).c_str();
1198  }
1199 
1200  do{
1201  if (get_debug_level()>8){
1202  std::cout << format("%s:%d: right before the derivs with deltaL = %g deltaV = %g tau = %g\n",__FILE__,__LINE__,deltaL, deltaV, tau).c_str();
1203  }
1204  // Calculate once to save on calls to EOS
1205  dphirL = dphir_dDelta(tau,deltaL);
1206  dphirV = dphir_dDelta(tau,deltaV);
1207  ddphirL = d2phir_dDelta2(tau,deltaL);
1208  ddphirV = d2phir_dDelta2(tau,deltaV);
1209  phirL = phir(tau,deltaL);
1210  phirV = phir(tau,deltaV);
1211 
1212  JL = deltaL * (1 + deltaL*dphirL);
1213  JV = deltaV * (1 + deltaV*dphirV);
1214  KL = deltaL*dphirL + phirL + log(deltaL);
1215  KV = deltaV*dphirV + phirV + log(deltaV);
1216 
1217  PL = R()*reduce.rho*T*JL;
1218  PV = R()*reduce.rho*T*JV;
1219 
1220  // At low pressure, the magnitude of ddphirL and ddphirV are enormous, truncation problems arise for all the partials
1221  dJL = 1 + 2*deltaL*dphirL + deltaL*deltaL*ddphirL;
1222  dJV = 1 + 2*deltaV*dphirV + deltaV*deltaV*ddphirV;
1223  dKL = 2*dphirL + deltaL*ddphirL + 1/deltaL;
1224  dKV = 2*dphirV + deltaV*ddphirV + 1/deltaV;
1225 
1226  DELTA = dJV*dKL-dJL*dKV;
1227 
1228  error = std::abs(KL-KV)+std::abs(JL-JV);
1229 
1230  // Get the predicted step
1231  stepL = omega/DELTA*( (KV-KL)*dJV-(JV-JL)*dKV);
1232  stepV = omega/DELTA*( (KV-KL)*dJL-(JV-JL)*dKL);
1233 
1234  if (deltaL+stepL > 1 && deltaV+stepV < 1 && deltaV+stepV > 0)
1235  {
1236  deltaL += stepL;
1237  deltaV += stepV;
1238  }
1239  else
1240  {
1241  throw ValueError(format("rhosatPure_Akasaka failed"));
1242  }
1243  iter++;
1244  if (iter > 100)
1245  {
1246  throw SolutionError(format("Akasaka solver did not converge after 100 iterations"));
1247  }
1248  }
1249  while (error > 1e-10 && std::abs(stepL) > 10*DBL_EPSILON*std::abs(stepL) && std::abs(stepV) > 10*DBL_EPSILON*std::abs(stepV));
1250 
1251  rhoLout = deltaL*reduce.rho;
1252  rhoVout = deltaV*reduce.rho;
1253  pout = PV;
1254 
1255  return;
1256 }
1257 
1258 void Fluid::saturation_VdW(double T, double &rhoL, double &rhoV, double &p, double s0)
1259 {
1260  class resid : public FuncWrapper1D
1261  {
1262  protected:
1263  double Tr;
1264  public:
1265  resid(double Tr){this->Tr = Tr;};
1266  double call(double s)
1267  {
1268  double w = sqrt(s*s-32*Tr/s+36-12*s);
1269  double val = log((s*(6-s)-(16*Tr/3)+s*w)/(s*(6-s)-(16*Tr/3)-s*w))-3*(6-s)*w/8*Tr;
1270  return val;
1271  }
1272  };
1273  resid r(T/this->crit.T);
1274  std::string errstr;
1275  if (s0 < 0)
1276  {
1277  s0 = 2;
1278  }
1279  //double v1 = r.call(s0-1);
1280  //double v2 = r.call(s0);
1281  //double v3 = r.call(s0+1);
1282  double s = Secant(&r,s0,0.01,1e-8,100,&errstr);
1283 
1284 }
1285 void Fluid::rhosatPure(double T, double &rhoLout, double &rhoVout, double &pout, double omega = 1.0, bool use_guesses = false)
1286 {
1287  // Only works for pure fluids (no blends)
1288  // At equilibrium, saturated vapor and saturated liquid are at the same pressure and the same Gibbs energy
1289  double rhoL,rhoV,p=_HUGE,error=999,x1=0,x2=0,x3,y1=0,y2,f,p_guess;
1290  int iter;
1291 
1292  if (T>=crit.T || T<(params.Ttriple-0.0001))
1293  {
1294  throw ValueError(format("Temperature of fluid [%g] is out of range from %g K to %g K in rhosatPure",T,params.Ttriple,crit.T));
1295  }
1296 
1297  if (!use_guesses)
1298  {
1299  // Use the density ancillary function as the starting point for the secant solver
1300  rhoL = rhosatL(T);
1301  rhoV = rhosatV(T);
1302  }
1303  else
1304  {
1305  // Use the guesses provided
1306  rhoL = rhoLout;
1307  rhoV = rhoVout;
1308  }
1309 
1310  p_guess = pressure_Trho(T,rhoV);
1311 
1312  iter=1;
1313  // Use a secant method to obtain pressure
1314  while ((iter<=3 || fabs(error)>1e-6) && iter<100)
1315  {
1316  if (iter==1){x1=p_guess; p=x1;}
1317  else if (iter==2){x2=1.000001*p_guess; p=x2;}
1318  else {p=x2;}
1319  //Recalculate the densities based on the current pressure
1320  rhoL = density_Tp(T,p,rhoL);
1321  rhoV = density_Tp(T,p,rhoV);
1322  // Residual between saturated liquid and saturated vapor gibbs function
1323  f = gibbs_Trho(T,rhoL) - gibbs_Trho(T,rhoV);
1324  if (iter==1){y1=f;}
1325  else //(iter>1)
1326  {
1327  y2=f;
1328  x3=x2-omega*y2/(y2-y1)*(x2-x1);
1329  error=f;
1330  y1=y2; x1=x2; x2=x3;
1331  }
1332  iter++;
1333  if (iter>100)
1334  {
1335  //ERROR
1336  throw SolutionError(format("rhosatPure failed, current values of rhoL and rhoV are %g,%g\n",rhoL,rhoV).c_str());
1337  }
1338  }
1339  rhoLout=rhoL;
1340  rhoVout=rhoV;
1341  pout=p;
1342  return;
1343 }
1344 bool isBetween(double x1,double x2, double x)
1345 {
1346  if (x2>x1 && x>=x1 && x<=x2) return true;
1347  else if (x1>x2 && x<=x1 && x>=x2) return true;
1348  else return false;
1349 }
1350 
1351 // Ancillary equations composed by interpolating within 50-point
1352 // curve that is calculated once
1353 double Fluid::hsatV_anc(double T)
1354 {
1355  if (!h_ancillary->built)
1356  h_ancillary->build(50);
1357  return h_ancillary->interpolateV(T);
1358 }
1359 double Fluid::ssatV_anc(double T)
1360 {
1361  if (!s_ancillary->built)
1362  s_ancillary->build(50);
1363  return s_ancillary->interpolateV(T);
1364 }
1365 double Fluid::hsatL_anc(double T)
1366 {
1367  if (!h_ancillary->built)
1368  h_ancillary->build(50);
1369  return h_ancillary->interpolateL(T);
1370 }
1371 double Fluid::ssatL_anc(double T)
1372 {
1373  if (!s_ancillary->built)
1374  s_ancillary->build(50);
1375  return s_ancillary->interpolateL(T);
1376 }
1377 
1378 double Fluid::_get_rho_guess(double T, double p)
1379 {
1380  double Tc,rho_simple;
1381 
1382  Tc = reduce.T;
1383 
1384  // Then based on phase, select the useful solution(s)
1385  double pL,pV,rhoL,rhoV;
1386 
1387  long phase = phase_Tp_indices(T,p,pL,pV,rhoL,rhoV);
1388 
1389  if (get_debug_level()>5){
1390  std::cout << format("%s:%d: Fluid::_get_rho_guess(%g,%g) phase = %d\n",__FILE__,__LINE__,T,p,phase).c_str();
1391  }
1392  // These are very simplistic guesses for the density, but they work ok
1393  if (phase == iGas)
1394  {
1395  // Perfect gas relation for initial guess
1396  rho_simple = p/(R()*T);
1397  }
1398  else if (phase == iSupercritical)
1399  {
1400  // Use Soave to get first guess
1401  rho_simple = density_Tp_Soave(T,p);
1402  }
1403  else if (phase == iLiquid)
1404  {
1405  // Start at the saturation state, with a known density, using the ancillary
1406  double rhoL = rhosatL(T);
1407  double pL = psatL_anc(T);
1408  if (get_debug_level()>5){
1409  std::cout<<format("%s:%d: pL = %g rhoL = %g \n",__FILE__,__LINE__,pL,rhoL).c_str();
1410  }
1411  double delta = rhoL / reduce.rho;
1412  double tau = reduce.T/T;
1413  double dp_drho = R()*T*(1+2*delta*dphir_dDelta(tau,delta)+delta*delta*d2phir_dDelta2(tau,delta));
1414  double drho_dp = 1/dp_drho;
1415  if (drho_dp*(pL-p)> rhoL)
1416  {
1417  rho_simple = rhoL;
1418  }
1419  else
1420  {
1421  rho_simple = rhoL-drho_dp*(pL-p);
1422  }
1423  }
1424  else if (fabs(psatL_anc(T)-p)<1e-8 || fabs(psatV_anc(T)-p)<1e-8)
1425  {
1426  throw ValueError(format("Input T [%0.16g] & p [%0.16g] are two-phase or saturated which is thermodynamically undefined\n",T,p));
1427  }
1428  else
1429  {
1430  // It is two-phase, we are going to skip the process of
1431  // solving and just return a value somewhere in the two-phase region.
1432  return (rhoL+rhoV)/2;
1433  }
1434  if (get_debug_level()>5){
1435  std::cout << format("%s:%d: _get_rho_guess = %g\n",__FILE__,__LINE__,rho_simple).c_str();
1436  }
1437  return rho_simple;
1438 }
1439 
1440 
1441 std::string Fluid::phase_Tp(double T, double p, double &pL, double &pV, double &rhoL, double &rhoV)
1442 {
1443  // Get the value from the long-output function
1444  long iPhase = phase_Tp_indices(T, p, pL, pV, rhoL, rhoV);
1445 
1446  if (get_debug_level()>5){
1447  std::cout << format("%s:%d: phase_Tp() phase index is %d\n",iPhase).c_str();
1448  }
1449 
1450  // Convert it to a std::string
1451  switch (iPhase)
1452  {
1453  case iTwoPhase:
1454  return std::string("Two-Phase");
1455  case iSupercritical:
1456  return std::string("Supercritical");
1457  case iGas:
1458  return std::string("Gas");
1459  case iLiquid:
1460  return std::string("Liquid");
1461  default:
1462  return std::string("");
1463  }
1464 }
1465 long Fluid::phase_Tp_indices(double T, double p, double &pL, double &pV, double &rhoL, double &rhoV)
1466 {
1467  /*
1468  | |
1469  | | Supercritical
1470  | |
1471  p | Liquid (b)------------
1472  | /
1473  | /
1474  | / Gas
1475  | /
1476  | (a)
1477  | -
1478  |------------------------
1479 
1480  T
1481 
1482  a: triple point
1483  b: critical point
1484  a-b: Saturation line
1485 
1486  */
1487  if (get_debug_level()>5){
1488  std::cout << format("%s:%d: phase_Tp_indices(%g,%g)\n",__FILE__,__LINE__,T,p).c_str();
1489  }
1490 
1491  if (T>crit.T && p>=crit.p.Pa){
1492  return iSupercritical;
1493  }
1494  else if (T>crit.T && p<crit.p.Pa){
1495  return iGas;
1496  }
1497  else if (T<crit.T && p>crit.p.Pa){
1498  return iLiquid;
1499  }
1500  else if (p<params.ptriple){
1501  return iGas;
1502  }
1503  else{
1504  // Now start to think about the saturation stuff
1505  // First try to use the ancillary equations if you are far enough away
1506  // Ancillary equations are good to within 1% in pressure in general
1507  // Some industrial fluids might not be within 3%
1508  if (isPure && p<0.94*psat(T)){
1509  return iGas;
1510  }
1511  else if (isPure && p>1.06*psat(T)){
1512  return iLiquid;
1513  }
1514  else if (!isPure && p<0.94*psatV(T)){
1515  return iGas;
1516  }
1517  else if (!isPure && p>1.06*psatL(T)){
1518  return iLiquid;
1519  }
1520  else{
1521  // Actually have to use saturation information sadly
1522  // For the given temperature, find the saturation state
1523  // Run the saturation routines to determine the saturation densities and pressures
1524  saturation_T(T,enabled_TTSE_LUT,pL,pV,rhoL,rhoV);
1525 
1526  if (p>(pL+10*DBL_EPSILON)){
1527  return iLiquid;
1528  }
1529  else if (p<(pV-10*DBL_EPSILON)){
1530  return iGas;
1531  }
1532  else{
1533  return iTwoPhase;
1534  }
1535  }
1536  }
1537 }
1538 
1539 std::string Fluid::phase_Trho(double T, double rho, double &pL, double &pV, double &rhoL, double &rhoV)
1540 {
1541  // Get the value from the long-output function
1542  long iPhase = phase_Trho_indices(T, rho, pL, pV, rhoL, rhoV);
1543  if (get_debug_level()>5){
1544  std::cout << format("%s:%d: phase index is %d\n",__FILE__,__LINE__,iPhase).c_str();
1545  }
1546 
1547  // Convert it to a std::string
1548  switch (iPhase)
1549  {
1550  case iTwoPhase:
1551  return std::string("Two-Phase");
1552  case iSupercritical:
1553  return std::string("Supercritical");
1554  case iGas:
1555  return std::string("Gas");
1556  case iLiquid:
1557  return std::string("Liquid");
1558  default:
1559  return std::string("");
1560  }
1561 }
1562 
1563 long Fluid::phase_Trho_indices(double T, double rho, double &pL, double &pV, double &rhoL, double &rhoV)
1564 {
1565  /*
1566  |
1567  | Liquid
1568  |
1569  | ---
1570  | \
1571  | \
1572  rho | a
1573  | /
1574  | /
1575  | --
1576  |
1577  | Gas
1578  |
1579  |------------------------
1580 
1581  T
1582 
1583  a: triple point
1584  b: critical point
1585  a-b: Saturation line
1586 
1587  */
1588  // If temperature is below the critical temperature, it is either
1589  // subcooled liquid, superheated vapor, or two-phase
1590  if (T < crit.T)
1591  {
1592  // Start to think about the saturation stuff
1593  // First try to use the ancillary equations if you are far enough away
1594  // Ancillary equations are good to within 1% in pressure in general
1595  // Some industrial fluids might not be within 3%
1596  if (rho < 0.95*rhosatV(T)){
1597  return iGas;
1598  }
1599  else if (rho > 1.05*rhosatL(T)){
1600  return iLiquid;
1601  }
1602  else{
1603  // Actually have to use saturation information sadly
1604  // For the given temperature, find the saturation state
1605  // Run the saturation routines to determine the saturation densities and pressures
1606  // Use the passed in variables to save calls to the saturation routine so the values can be re-used again
1607  saturation_T(T, enabled_TTSE_LUT, pL, pV, rhoL, rhoV);
1608  double Q = (1/rho-1/rhoL)/(1/rhoV-1/rhoL);
1609  if (Q < -100*DBL_EPSILON){
1610  return iLiquid;
1611  }
1612  else if (Q > 1+100*DBL_EPSILON){
1613  return iGas;
1614  }
1615  else{
1616  return iTwoPhase;
1617  }
1618  }
1619  }
1620  // Now check the states above the critical temperature
1621  double p = pressure_Trho(T,rho);
1622 
1623  if (T>reduce.T && p>reduce.p.Pa){
1624  return iSupercritical;
1625  }
1626  else if (T>reduce.T && p<reduce.p.Pa){
1627  return iGas;
1628  }
1629  else if (T<reduce.T && p>reduce.p.Pa){
1630  return iLiquid;
1631  }
1632  else if (p<params.ptriple){
1633  return iGas;
1634  }
1635  else{
1636  return -1;
1637  }
1638 }
1639 
1640 long Fluid::phase_prho_indices(double p, double rho, double &T, double &TL, double &TV, double &rhoL, double &rhoV)
1641 {
1642 
1643  // If pressure is below triple point pressure and density is below triple point temperature saturated vapor density
1644  if (p < params.ptriple)
1645  {
1646  return iGas;
1647  }
1648 
1649  // If temperature is below the critical temperature, it is either
1650  // subcooled liquid, superheated vapor, or two-phase
1651 
1652  if (p < crit.p.Pa)
1653  {
1654  // Actually have to use saturation information sadly
1655  // For the given temperature, find the saturation state
1656  // Run the saturation routines to determine the saturation densities and pressures
1657  // Use the passed in variables to save calls to the saturation routine so the values can be re-used again
1658  saturation_p(p,false,TL,TV,rhoL,rhoV);
1659  double Q = (1/rho-1/rhoL)/(1/rhoV-1/rhoL);
1660  if (Q < -1e-10){
1661  return iLiquid;
1662  }
1663  else if (Q > 1+1e-10){
1664  return iGas;
1665  }
1666  else{
1667  T = TL;
1668  return iTwoPhase;
1669  }
1670  }
1671  // Now check the states above the critical pressure
1672  double T0 = temperature_prho_PengRobinson(p,rho);
1673  T = temperature_prho(p, rho, T0);
1674 
1675  if (T > crit.T){
1676  return iSupercritical;
1677  }
1678  else{ // T < crit.T
1679  return iLiquid;
1680  }
1681 }
1682 double Fluid::temperature_prho(double p, double rho, double T0)
1683 {
1684  // solve for T to yield the desired pressure
1685  double error, T, drdT, r;
1686  int iter = 0;
1687  CoolPropStateClassSI CPS(this);
1688 
1689  T = T0;
1690  CPS.update(iT,T,iD,rho);
1691 
1692  do
1693  {
1694  double pEOS = CPS.p();
1695  r = pEOS-p;
1696  drdT = CPS.dpdT_constrho();
1697  T -= r/drdT;
1698  CPS.update(iT,T,iD,rho);
1699  error = fabs(r/pEOS);
1700  iter++;
1701  if (fabs(r/drdT) < 1e-12)
1702  {
1703  return T;
1704  break;
1705  }
1706  if (iter > 100)
1707  throw SolutionError(format("temperature_prho failed with inputs p=%g rho=%g T0=%g for fluid %s",p,rho,T0,name.c_str()));
1708  }
1709  while (error > 1e-10 );
1710  return T;
1711 }
1712 
1713 void Fluid::temperature_ph(double p, double h, double &Tout, double &rhoout, double &rhoLout, double &rhoVout, double &TsatLout, double &TsatVout, double T0, double rho0)
1714 {
1715  int iter;
1716  bool failed = false;
1717  double A[2][2], B[2][2],T_guess, omega = 1.0;
1718  double dar_ddelta,da0_dtau,d2a0_dtau2,dar_dtau,d2ar_ddelta_dtau,d2ar_ddelta2,d2ar_dtau2,d2a0_ddelta_dtau;
1719  double f1,f2,df1_dtau,df1_ddelta,df2_ddelta,df2_dtau;
1720  double rhoL, rhoV, hsatL,hsatV,TsatL,TsatV,tau,delta,worst_error;
1721  double h_guess, hc, rho_guess, T1, T2, h1, h2, rho1, rho2;
1722  double hsat_tol = 3000; //[J/kg]
1723 
1724  // A starting set of temperature and pressure are provided, use them as the guess value
1725  // Their default values are -1 and -1, which are unphysical values
1726  if (T0 > 0 && rho0 > 0)
1727  {
1728  T_guess = T0;
1729  delta = rho0/reduce.rho;
1730  }
1731  else
1732  {
1733  // It is supercritical pressure (or just below the critical pressure)
1734  if (p > 0.999*crit.p.Pa)
1735  {
1736  hc = enthalpy_Trho(crit.T+0.001,crit.rho);
1737  if (h > hc)
1738  {
1739  // Supercritical pseudo-gas - extrapolate to find guess temperature at critical density
1740  T_guess = crit.T+30;
1741  h_guess = enthalpy_Trho(T_guess,crit.rho);
1742  T_guess = (crit.T-T_guess)/(hc-h_guess)*(h-h_guess)+T_guess;
1743  rho_guess = density_Tp(T_guess,p);
1744  delta = rho_guess/reduce.rho;
1745  }
1746  else
1747  {
1748  // Supercritical pseudo-liquid
1749  T_guess = params.Ttriple;
1750  rho_guess = density_Tp(T_guess,p);
1751  h_guess = enthalpy_Trho(T_guess,rho_guess);
1752  // Update the guess with linear interpolation
1753  T_guess = (crit.T-T_guess)/(hc-h_guess)*(h-h_guess)+T_guess;
1754  // Solve for the density
1755  rho_guess = density_Tp(T_guess,p,rho_guess);
1756 
1757  delta = rho_guess/reduce.rho;
1758  }
1759  }
1760  else if (p < params.ptriple)
1761  {
1762  rho_guess = params.rhoVtriple;
1763  T_guess = params.ptriple;
1764  delta = rho_guess/reduce.rho;
1765  }
1766  else
1767  {
1768  // Set to a negative value as a dummy value
1769  delta = -1;
1770 
1771  // If using TTSE, TTSE is the arbiter of two-phase/single-phase boundary
1772  //
1773  if (enabled_TTSE_LUT)
1774  {
1775  hsatL = TTSESatL.evaluate(iH,p);
1776  hsatV = TTSESatV.evaluate(iH,p);
1777  // If TTSE is enabled and we have gotten to this point
1778  if (h>hsatV || h<hsatL)
1779  {
1780  }
1781  }
1782  else
1783  {
1784  //**************************************************
1785  // Step 1: Try to just use the ancillary equations
1786  //**************************************************
1787  TsatL = Tsat_anc(p,0);
1788  if (pure())
1789  TsatV = TsatL;
1790  else
1791  TsatV = Tsat_anc(p,1);
1792  rhoL = rhosatL(TsatL);
1793  rhoV = rhosatV(TsatV);
1794  hsatL = hsatL_anc(TsatL); //[J/kg]
1795  hsatV = hsatV_anc(TsatV); //[J/kg]
1796  rhoLout = -1;
1797  rhoVout = -1;
1798  TsatLout = -1;
1799  TsatVout = -1;
1800 
1801  if (h > hsatV + hsat_tol)
1802  {
1803  // Start at saturated vapor
1804  rho1 = rhoV;
1805  T1 = TsatV;
1806  T2 = 1.1*TsatV;
1807  h1 = hsatV;
1808  do{
1809  rho2 = density_Tp(T2,p,rho1);
1810  h2 = enthalpy_Trho(T2,rho2);
1811  T2 *= 1.1;
1812  }
1813  while (h2 <= h);
1814 
1815  // Linearly interpolate in each parameter to guess the solution
1816  T_guess = (T2-T1)/(h2-h1)*(h-h1)+T1;
1817  rho_guess = (rho2-rho1)/(h2-h1)*(h-h1)+rho1;
1818 
1819  // Reduced density
1820  delta = rho_guess/reduce.rho;
1821  if (get_debug_level() > 8){
1822  std::cout << format("%s:%d: temperature_ph; it is vapor(anc), hsatV = %g\n",__FILE__,__LINE__, hsatV) ;
1823  }
1824  }
1825  else if (h < hsatL - hsat_tol)
1826  {
1827  // Away from the critical point, specific heat is non-infinite, use it to guess temperature
1828  if (0.9*crit.p.Pa > p && p < 1.1*crit.p.Pa)
1829  {
1830  double cp = specific_heat_p_Trho(TsatL,rhoL);
1831  // hsat-h = cp(Tsat-T)
1832  T_guess = TsatL-(hsatL-h)/cp;
1833  rho_guess = density_Tp(T_guess,p);
1834  }
1835  else
1836  {
1837  T_guess = params.Ttriple+1;
1838  rho_guess = density_Tp(T_guess,p);
1839  h_guess = enthalpy_Trho(T_guess,rho_guess);
1840  // Update the guess with linear interpolation
1841  T_guess = (TsatL-T_guess)/(hsatL-h_guess)*(h-h_guess)+T_guess;
1842  // Solve for the density
1843  rho_guess = density_Tp(T_guess,p,rho_guess);
1844  }
1845  delta = rho_guess/reduce.rho;
1846  if (get_debug_level() > 8){
1847  std::cout << format("%s:%d: temperature_ph; it is liquid(anc), hsatL = %g\n",__FILE__,__LINE__, hsatL) ;
1848  }
1849  }
1850  }
1851 
1852  if (delta < 0) // No solution found using ancillary equations (or the saturation LUT is in use) - need to use saturation call (or LUT)
1853  {
1854 
1855  //**************************************************
1856  // Step 2: Not far away from saturation (or it is two-phase) - need to solve saturation as a function of p :( - this is slow
1857  //**************************************************
1859  sat.update(iP, p, iQ, 0);
1860 
1861  rhoL = sat.rhoL();
1862  rhoV = sat.rhoV();
1863  hsatL = sat.hL();
1864  hsatV = sat.hV();
1865  TsatL = sat.TL();
1866  TsatV = sat.TV();
1867  rhoLout = rhoL;
1868  rhoVout = rhoV;
1869  TsatLout = TsatL;
1870  TsatVout = TsatV;
1871 
1872  if (fabs((h-hsatL)/(hsatV-hsatL)) < 1e-8)
1873  {
1874  Tout = TsatL;
1875  rhoout = rhoLout;
1876  return;
1877  }
1878  else if (fabs((h-hsatL)/(hsatV-hsatL)-1) < 1e-8)
1879  {
1880  Tout = TsatV;
1881  rhoout = rhoVout;
1882  return;
1883  }
1884  else if (h > hsatV)
1885  {
1886  // Start at saturated vapor
1887  rho1 = rhoV;
1888  T1 = TsatV;
1889  T2 = 1.1*TsatV;
1890  h1 = hsatV;
1891  do{
1892  rho2 = density_Tp(T2,p,rho1);
1893  h2 = enthalpy_Trho(T2,rho2);
1894  T2 *= 1.1;
1895  }
1896  while (h2 <= h);
1897 
1898  // Linearly interpolate in each parameter to guess the solution
1899  T_guess = (T2-T1)/(h2-h1)*(h-h1)+T1;
1900  rho_guess = (rho2-rho1)/(h2-h1)*(h-h1)+rho1;
1901 
1902  // Reduced density
1903  delta = rho_guess/reduce.rho;
1904  if (get_debug_level() > 8){
1905  std::cout << format("%s:%d: temperature_ph; it is vapor, hsatV = %g\n",__FILE__,__LINE__, hsatV) ;
1906  }
1907  }
1908  else if (h<hsatL)
1909  {
1910  T_guess = TsatL;
1911  rho_guess = rhoLout;
1912 
1913  //For some reason this block was causing DLL to not return value to windows. It makes absolutely no sense
1914  double rho_mid;
1915  T_guess = params.Ttriple;
1916  rho_guess = density_Tp(T_guess,p,rhoL);
1917  h_guess = enthalpy_Trho(T_guess,rho_guess);
1918 
1919  // Same thing at the midpoint temperature
1920  double Tmid = (T_guess + TsatL)/2.0;
1921  try{
1922 
1923  rho_mid = density_Tp(Tmid, p, rhoL);
1924 
1925  if (!ValidNumber(rho_mid)){
1926  rho_guess = (rhoLout-rho_guess)/(hsatL-h_guess)*(h-h_guess) + rho_guess;
1927  T_guess = (TsatL-T_guess)/(hsatL-h_guess)*(h-h_guess) + T_guess;
1928  }
1929  else
1930  {
1931  double h_mid = enthalpy_Trho(Tmid,rho_mid);
1932 
1933  // Quadratic interpolation
1934  T_guess = QuadInterp(h_guess, h_mid, hsatL, T_guess, Tmid, TsatL, h);
1935  rho_guess = QuadInterp(h_guess, h_mid, hsatL, rho_guess, rho_mid, rhoL, h);
1936  }
1937  }
1938  catch(std::exception &)
1939  {
1940  T_guess = TsatL;
1941  rho_guess = rhoLout;
1942  }
1943 
1944  delta = rho_guess / reduce.rho;
1945  if (get_debug_level() > 8){
1946  std::cout << format("%s:%d: temperature_ph; it is liquid, hsatL = %g, T = %g, rho = %g\n",__FILE__,__LINE__, hsatL, T_guess, rho_guess) ;
1947  }
1948  }
1949  else
1950  {
1951 
1952  // It is two-phase
1953  // Return the quality weighted values
1954  double quality = (h-hsatL)/(hsatV-hsatL);
1955  Tout = quality*TsatV+(1-quality)*TsatL;
1956  double v = quality*(1/rhoV)+(1-quality)*1/rhoL;
1957  rhoout = 1/v;
1958  if (get_debug_level() > 8){
1959  std::cout << format("%s:%d: temperature_ph; it is 2phase,Q = %g\n",__FILE__,__LINE__, quality) ;
1960  }
1961  return;
1962  }
1963  }
1964  }
1965  }
1966 
1967  tau=reduce.T/T_guess;
1968  double Tc = reduce.T;
1969  double rhoc = reduce.rho;
1970 
1971  if (get_debug_level() > 8){
1972  std::cout << format("%s:%d: temperature_ph guesses(p=%g,h=%g,tau=%g,delta=%g)\n",__FILE__,__LINE__, p, h, tau, delta) ;
1973  }
1974 
1975  // Now we enter into a Jacobian loop that attempts to simultaneously solve
1976  // for temperature and density using Newton-Raphson
1977  worst_error=999;
1978  iter=0;
1979  while (worst_error>1e-6 && failed == false)
1980  {
1981  // All the required partial derivatives
1982  da0_dtau = dphi0_dTau(tau,delta);
1983  d2a0_dtau2 = d2phi0_dTau2(tau,delta);
1984  d2a0_ddelta_dtau = 0.0;
1985  dar_dtau = dphir_dTau(tau,delta);
1986  dar_ddelta = dphir_dDelta(tau,delta);
1987  d2ar_ddelta_dtau = d2phir_dDelta_dTau(tau,delta);
1988  d2ar_ddelta2 = d2phir_dDelta2(tau,delta);
1989  d2ar_dtau2 = d2phir_dTau2(tau,delta);
1990 
1991  f1 = delta/tau*(1+delta*dar_ddelta)-p/(rhoc*R()*Tc);
1992  f2 = (1+delta*dar_ddelta)+tau*(da0_dtau+dar_dtau)-tau*h/(R()*Tc);
1993  df1_dtau = (1+delta*dar_ddelta)*(-delta/tau/tau)+delta/tau*(delta*d2ar_ddelta_dtau);
1994  df1_ddelta = (1.0/tau)*(1+2.0*delta*dar_ddelta+delta*delta*d2ar_ddelta2);
1995  df2_dtau = delta*d2ar_ddelta_dtau+da0_dtau+dar_dtau+tau*(d2a0_dtau2+d2ar_dtau2)-h/(R()*Tc);
1996  df2_ddelta = (dar_ddelta+delta*d2ar_ddelta2)+tau*(d2a0_ddelta_dtau+d2ar_ddelta_dtau);
1997 
1998  //First index is the row, second index is the column
1999  A[0][0]=df1_dtau;
2000  A[0][1]=df1_ddelta;
2001  A[1][0]=df2_dtau;
2002  A[1][1]=df2_ddelta;
2003 
2004  //double det = A[0][0]*A[1][1]-A[1][0]*A[0][1];
2005 
2006  MatInv_2(A,B);
2007  tau -= omega*(B[0][0]*f1+B[0][1]*f2);
2008  delta -= omega*(B[1][0]*f1+B[1][1]*f2);
2009 
2010  if (fabs(f1)>fabs(f2))
2011  worst_error=fabs(f1);
2012  else
2013  worst_error=fabs(f2);
2014 
2015  if (!ValidNumber(f1) || !ValidNumber(f2))
2016  {
2017  throw SolutionError(format("Invalid values for inputs p=%g h=%g for fluid %s",p,h,(char*)name.c_str()));
2018  }
2019 
2020  iter+=1;
2021  if (iter>100)
2022  {
2023  throw SolutionError(format("Thp did not converge with inputs p=%g h=%g for fluid %s",p,h,(char*)name.c_str()));
2024  Tout = _HUGE;
2025  rhoout = _HUGE;
2026  }
2027  }
2028 
2029  //std::cout<<"Temperature_ph took "<<iter-1<<" steps\n";
2030  Tout = reduce.T/tau;
2031  rhoout = delta*reduce.rho;
2032 }
2033 
2034 void Fluid::temperature_ps(double p, double s, double &Tout, double &rhoout, double &rhoLout, double &rhoVout, double &TsatLout, double &TsatVout)
2035 {
2036  int iter;
2037  double A[2][2], B[2][2],T_guess;
2038  double dar_ddelta,da0_dtau,d2a0_dtau2,dar_dtau,d2ar_ddelta_dtau,d2ar_ddelta2,d2ar_dtau2,d2a0_ddelta_dtau,a0,ar,da0_ddelta;
2039  double f1,f2,df1_dtau,df1_ddelta,df2_ddelta,df2_dtau;
2040  double rhoL, rhoV, ssatL,ssatV,TsatL,TsatV,tau,delta,worst_error;
2041  double s_guess, sc, rho_guess;
2042  double ssat_tol = 1;
2043  // It is supercritical pressure (or just below the critical pressure)
2044  if (p > 0.999*crit.p.Pa)
2045  {
2046  sc = entropy_Trho(crit.T+0.001,crit.rho);
2047  if (s > sc)
2048  {
2049  // Supercritical pseudo-gas - extrapolate to find guess temperature at critical density
2050  T_guess = crit.T+30;
2051  s_guess = entropy_Trho(T_guess,crit.rho);
2052  T_guess = (crit.T-T_guess)/(sc-s_guess)*(s-s_guess)+T_guess;
2053 
2054  // Solve for the density
2055  rho_guess = density_Tp(T_guess,p);
2056 
2057  delta = rho_guess/reduce.rho;
2058  }
2059  else
2060  {
2061  // Supercritical pseudo-liquid
2062  T_guess = params.Ttriple;
2063  rho_guess = density_Tp(T_guess,p);
2064  s_guess = entropy_Trho(T_guess,rho_guess);
2065  // Update the guess with linear interpolation
2066  T_guess = (crit.T-T_guess)/(sc-s_guess)*(s-s_guess)+T_guess;
2067  // Solve for the density
2068  rho_guess = density_Tp(T_guess,p,rho_guess);
2069 
2070  delta = rho_guess/reduce.rho;
2071  }
2072  }
2073  else
2074  {
2075  // Set to a negative value as a dummy value
2076  delta = -1;
2077 
2078  //**************************************************
2079  // Step 1: Try to just use the ancillary equations
2080  //**************************************************
2081  TsatL = Tsat_anc(p,0);
2082  if (pure())
2083  TsatV = TsatL;
2084  else
2085  TsatV = Tsat_anc(p,1);
2086  rhoL = rhosatL(TsatL);
2087  rhoV = rhosatV(TsatV);
2088  ssatL = ssatL_anc(TsatL);
2089  ssatV = ssatV_anc(TsatV);
2090  rhoLout = -1;
2091  rhoVout = -1;
2092  TsatLout = -1;
2093  TsatVout = -1;
2094 
2095  if (s > ssatV + ssat_tol)
2096  {
2097 
2098  T_guess = TsatV+30;
2099  s_guess = entropy_Trho(T_guess,rhoV);
2100  T_guess = (TsatV-T_guess)/(ssatV-s_guess)*(s-s_guess)+T_guess;
2101 
2102  delta = p/(R()*T_guess)/reduce.rho;
2103  }
2104  else if (s < ssatL - ssat_tol)
2105  {
2106  T_guess = params.Ttriple;
2107  rho_guess = density_Tp(T_guess,p);
2108  s_guess = entropy_Trho(T_guess,rho_guess);
2109  // Update the guess with linear interpolation
2110  T_guess = (TsatL-T_guess)/(ssatL-s_guess)*(s-s_guess)+T_guess;
2111  // Solve for the density
2112  rho_guess = density_Tp(T_guess,p,rho_guess);
2113 
2114  delta = rho_guess/reduce.rho;
2115  }
2116 
2117  if (delta<0) // No solution found using ancillary equations - need to use saturation call
2118  {
2119  //**************************************************
2120  // Step 2: Not far away from saturation (or it is two-phase) - need to solve saturation as a function of p :( - this is slow
2121  //**************************************************
2123  sat.update(iP,p,iQ,0);
2124  rhoL = sat.rhoL();
2125  rhoV = sat.rhoV();
2126  ssatL = sat.sL();
2127  ssatV = sat.sV();
2128  TsatL = sat.TL();
2129  TsatV = sat.TV();
2130  rhoLout = rhoL;
2131  rhoVout = rhoV;
2132  TsatLout = TsatL;
2133  TsatVout = TsatV;
2134 
2135  if (fabs(s-ssatL) < 1e-4)
2136  {
2137  Tout = TsatL;
2138  rhoout = rhoLout;
2139  return;
2140  }
2141  else if (fabs(s-ssatV) < 1e-4)
2142  {
2143  Tout = TsatV;
2144  rhoout = rhoVout;
2145  return;
2146  }
2147  else if (s > ssatV)
2148  {
2149  T_guess = TsatV+30;
2150  s_guess = entropy_Trho(T_guess,rhoV);
2151  T_guess = (TsatV-T_guess)/(ssatV-s_guess)*(s-s_guess)+T_guess;
2152 
2153  delta = p/(R()*T_guess)/reduce.rho;
2154  }
2155  else if (s < ssatL)
2156  {
2157  T_guess = params.Ttriple;
2158  rho_guess = density_Tp(T_guess,p,rhoL);
2159  s_guess = entropy_Trho(T_guess,rho_guess);
2160  // Update the guess with linear interpolation
2161  T_guess = (TsatL-T_guess)/(ssatL-s_guess)*(s-s_guess)+T_guess;
2162  // Solve for the density
2163  rho_guess = density_Tp(T_guess,p,rho_guess);
2164 
2165  delta = rho_guess/reduce.rho;
2166  }
2167  else
2168  {
2169  // It is two-phase
2170  // Return the quality weighted values
2171  double quality = (s-ssatL)/(ssatV-ssatL);
2172  Tout = quality*TsatV+(1-quality)*TsatL;
2173  rhoout = 1/(quality/rhoV+(1-quality)/rhoL);
2174  return;
2175  }
2176  }
2177  }
2178 
2179  tau=reduce.T/T_guess;
2180  double Tc = reduce.T;
2181  double rhoc = reduce.rho;
2182 
2183  double tau0 = tau, delta0 = delta;
2184  // Relaxation factor for entropy
2185  for(double omega = 1.0; omega > 0; omega -= 0.3)
2186  {
2187  bool failed = false;
2188  tau = tau0; delta = delta0;
2189 
2190  // Now we enter into a Jacobian loop that attempts to simultaneously solve
2191  // for temperature and density using Newton-Raphson
2192  worst_error=999;
2193  iter=0;
2194  while (worst_error>1e-6)
2195  {
2196  // All the required partial derivatives
2197  a0 = phi0(tau,delta);
2198  da0_dtau = dphi0_dTau(tau,delta);
2199  d2a0_dtau2 = d2phi0_dTau2(tau,delta);
2200  da0_ddelta = dphi0_dDelta(tau,delta);
2201  d2a0_ddelta_dtau = 0.0;
2202 
2203  ar = phir(tau,delta);
2204  dar_dtau = dphir_dTau(tau,delta);
2205  d2ar_dtau2 = d2phir_dTau2(tau,delta);
2206  dar_ddelta = dphir_dDelta(tau,delta);
2207  d2ar_ddelta2 = d2phir_dDelta2(tau,delta);
2208  d2ar_ddelta_dtau = d2phir_dDelta_dTau(tau,delta);
2209 
2210  // Residual and derivatives thereof for entropy
2211  f1 = tau*(da0_dtau+dar_dtau)-ar-a0-s/R();
2212  df1_dtau = tau*(d2a0_dtau2 + d2ar_dtau2)+(da0_dtau+dar_dtau)-dar_dtau-da0_dtau;
2213  df1_ddelta = tau*(d2a0_ddelta_dtau+d2ar_ddelta_dtau)-dar_ddelta-da0_ddelta;
2214 
2215  // Residual and derivatives thereof for pressure
2216  f2 = delta/tau*(1+delta*dar_ddelta)-p/(rhoc*R()*Tc);
2217  df2_dtau = (1+delta*dar_ddelta)*(-delta/tau/tau)+delta/tau*(delta*d2ar_ddelta_dtau);
2218  df2_ddelta = (1.0/tau)*(1+2.0*delta*dar_ddelta+delta*delta*d2ar_ddelta2);
2219 
2220  //First index is the row, second index is the column
2221  A[0][0]=df1_dtau;
2222  A[0][1]=df1_ddelta;
2223  A[1][0]=df2_dtau;
2224  A[1][1]=df2_ddelta;
2225 
2226  MatInv_2(A, B);
2227  tau -= omega*(B[0][0]*f1+B[0][1]*f2);
2228  delta -= omega*(B[1][0]*f1+B[1][1]*f2);
2229 
2230  if (fabs(f1)>fabs(f2))
2231  worst_error=fabs(f1);
2232  else
2233  worst_error=fabs(f2);
2234 
2235  if (tau < 0 || delta < 0)
2236  {
2237  failed = true; break;
2238  }
2239 
2240  iter+=1;
2241  if (iter>100)
2242  {
2243  throw SolutionError(format("Tsp did not converge with inputs p=%g s=%g for fluid %s",p,s,(char*)name.c_str()));
2244  Tout = _HUGE;
2245  rhoout = _HUGE;
2246  }
2247  }
2248  // Solver succeeded, break out of iteration on solver relaxation factor
2249  if (failed == false){break;}
2250  }
2251 
2252  Tout = reduce.T/tau;
2253  rhoout = delta*reduce.rho;
2254 }
2255 
2260 {
2261 private:
2262  double h, s, r;
2263  Fluid * pFluid;
2264 public:
2265  double rho,pL,pV,rhoL,rhoV,hL,hV,sL,sV,Q;
2266  HSSatFuncClass(double h, double s, Fluid *pFluid){
2267  this->h = h;
2268  this->s = s;
2269  this->pFluid = pFluid;
2270  };
2271  double call(double T){
2272  // Try to find a Tsat that yields the same quality when considering enthalpy and entropy
2273  pFluid->saturation_T(T, false, pL, pV, rhoL, rhoV);
2274  hL = pFluid->enthalpy_Trho(T,rhoL);
2275  hV = pFluid->enthalpy_Trho(T,rhoV);
2276  sL = pFluid->entropy_Trho(T,rhoL);
2277  sV = pFluid->entropy_Trho(T,rhoV);
2278  // Enthalpy from the linear h-s curve for the isotherm through the two-phase
2279  double h_s = (hV-hL)/(sV-sL)*(this->s-sL)+hL;
2280  Q = (h-hL)/(hV-hL);
2281  rho = 1/(Q/rhoV+(1-Q)/rhoL);
2282  return this->h-h_s;
2283  };
2284 };
2285 
2286 void Fluid::temperature_hs(double h, double s, double &Tout, double &rhoout, double &rhoLout, double &rhoVout, double &TsatLout, double &TsatVout)
2287 {
2288  bool singlephase_initialized = false;
2289  int iter;
2290  double A[2][2], B[2][2], T_guess;
2291  double dar_ddelta,da0_dtau,d2a0_dtau2,dar_dtau,d2ar_ddelta_dtau,d2ar_ddelta2,d2ar_dtau2,d2a0_ddelta_dtau,a0,ar,da0_ddelta;
2292  double f1,f2,df1_dtau,df1_ddelta,df2_ddelta,df2_dtau;
2293  double tau,delta,worst_error,rho_guess, ssat, htriple_s;
2294  double ssat_tol = 0.0001;
2295  std::string errstr;
2296 
2297  // The enthalpy at the triple point (or minimum) temperature corresponding to the entropy
2298  // Might not be in the two-phase region
2299  htriple_s = (HS.hV_Tmin-HS.hL_Tmin)/(HS.sV_Tmin-HS.sL_Tmin)*(s-HS.sL_Tmin)+HS.hL_Tmin;
2300 
2301  // It's a solid state - not allowed
2302  if (h < htriple_s)
2303  {
2304  throw ValueError(format("HS inputs are solid - not allowed h: %g s: %g",h,s).c_str());
2305  }
2306  // If you are AT the critical point, yield the critical point data
2307  else if (fabs(h-crit.h) < 1e-6 && fabs(s-crit.s) < 1e-6)
2308  {
2309  Tout = crit.T; rhoout = crit.rho; rhoLout = crit.rho; rhoVout = crit.rho; TsatLout = crit.T; TsatVout = crit.T;
2310  return;
2311  }
2312  // If above the maximum enthalpy, no need to do any saturation calls, just determine
2313  // your state using interval bisection on the pressure using the p-h code (slow)
2314  else if (h > HS.hmax)
2315  {
2316  int iter = 0;
2317  double logpL, logpR, logpM, TM,rhoM,dummy, sM;
2318  logpL = log(1e-10);
2319  logpR = log(100*crit.p.Pa);
2320 
2321  do
2322  {
2323  logpM = (logpL + logpR)/2;
2324  temperature_ph(exp(logpM),h,TM,rhoM,dummy,dummy,dummy,dummy);
2325  sM = entropy_Trho(TM,rhoM);
2326  if (sM < s)
2327  {logpR = logpM;} // Keep the left half of the domain
2328  else
2329  {logpL = logpM;}// Keep the right half of the domain
2330  iter += 1;
2331  }
2332  while (iter < 10);
2333 
2334  rho_guess = rhoM;
2335  T_guess = TM;
2336  singlephase_initialized = true;
2337  }
2338 
2339  // Branch #1 (liquid curve)
2340  if (h < HS.hV_Tmin && h < crit.h && !singlephase_initialized)
2341  {
2342  double Tsat,rhosat,TL,TV,rhoL,rhoV;
2343  // Get the saturated liquid state for the given enthalpy
2344  this->saturation_h(h, limits.Tmin, crit.T, 0, Tsat, rhosat, TL, TV, rhoL, rhoV);
2345  // Check the saturated entropy for the given value of the entropy
2346  ssat = this->entropy_Trho(Tsat, rhosat);
2347 
2348  if (fabs(s - ssat) < ssat_tol) // It's saturated liquid
2349  {
2350  Tout = Tsat; rhoout = rhoL; rhoLout = rhoL; rhoVout = rhoV; TsatLout = TL; TsatVout = TV;
2351  return;
2352  }
2353  // If entropy greater than ssat, two-phase solution
2354  // (or it solid which is handled above)
2355  else if (s > ssat)
2356  {
2357  // First check for two-phase
2358  HSSatFuncClass SatFunc(h, s, this);
2359  try{
2360  Tout = Secant(&SatFunc,limits.Tmin,1,1e-8,100,&errstr);
2361  if (SatFunc.Q > 1+1e-8 || SatFunc.Q < 0-1e-8){ throw ValueError("Solution must be within the two-phase region"); }
2362  // It is two-phase, we are done, no exceptions were raised
2363  }
2364  catch(std::exception &)
2365  {
2366  // Split the range into two pieces
2367  try
2368  {
2369  Tout = Brent(&SatFunc, limits.Tmin, crit.T-1e-4, 1e-16, 1e-8, 100, &errstr);
2370  if (SatFunc.Q > 1+1e-8 || SatFunc.Q < 0-1e-8){ throw ValueError("Solution must be within the two-phase region"); }
2371  }
2372  catch(std::exception &)
2373  {
2374  try
2375  {
2376  Tout = Brent(&SatFunc, (limits.Tmin+crit.T)/2.0, crit.T-1e-4, 1e-16, 1e-8, 100, &errstr);
2377  if (SatFunc.Q > 1+1e-8 || SatFunc.Q < 0-1e-8){ throw ValueError("Solution must be within the two-phase region"); }
2378  }
2379  catch (std::exception &)
2380  {
2381  try{
2382  Tout = Brent(&SatFunc, limits.Tmin, (limits.Tmin+crit.T)/2.0, 1e-16, 1e-8, 100, &errstr);
2383  if (SatFunc.Q > 1+1e-8 || SatFunc.Q < 0-1e-8){ throw ValueError("Solution must be within the two-phase region"); }
2384  }
2385  catch (std::exception &)
2386  {
2387  throw ValueError(format("For HS:Branch1, twophase failed").c_str());
2388  }
2389  }
2390  }
2391  }
2392  rhoout = SatFunc.rho;
2393  rhoLout = SatFunc.rhoL;
2394  rhoVout = SatFunc.rhoV;
2395  TsatLout = Tout;
2396  TsatVout = Tout;
2397  return;
2398  }
2399  else // It's subcooled liquid
2400  {
2401  int iter = 0;
2402  double logpL, logpR, logpM,TM,rhoM,dummy, sM;
2403 
2404  logpL = log(pressure_Trho(Tsat,rhosat));
2405  logpR = log(10*crit.p.Pa);
2406 
2407  do
2408  {
2409  logpM = (logpL + logpR)/2;
2410  temperature_ph(exp(logpM),h,TM,rhoM,dummy,dummy,dummy,dummy);
2411  sM = entropy_Trho(TM,rhoM);
2412  if (sM < s)
2413  {logpR = logpM;} // Keep the left half of the domain
2414  else
2415  {logpL = logpM;}// Keep the right half of the domain
2416  iter += 1;
2417  }
2418  while (iter < 10);
2419 
2420  rho_guess = rhoM;
2421  T_guess = TM;
2422  singlephase_initialized = true;
2423  }
2424  }
2425 
2426  // Branch #2 (vapor curve) between hmax and the maximum of the enthalpy corresponding to the
2427  // critical point and the enthalpy corresponding to the
2428  // saturated vapor at the minimum temperature
2429  if ((h > HS.hV_Tmin && h > crit.h) && !singlephase_initialized)
2430  {
2431  double Tsat,rhosat,TL,TV,rhoL,rhoV;
2432 
2433  // Get the saturated vapor state for the given enthalpy
2434  this->saturation_h(h, limits.Tmin, HS.T_hmax, 1, Tsat, rhosat, TL, TV, rhoL, rhoV);
2435 
2436  // Get the saturated vapor entropy for the given value of the enthalpy
2437  ssat = this->entropy_Trho(Tsat, rhosat);
2438 
2439  if (fabs(s-ssat) < ssat_tol) // It's saturated vapor
2440  {
2441  Tout = Tsat; rhoout = rhoV; rhoLout = rhoL; rhoVout = rhoV; TsatLout = TL; TsatVout = TV;
2442  return;
2443  }
2444  else if (s > ssat) // It's superheated vapor
2445  {
2446  int iter = 0;
2447  double logpL, logpR, logpM, TM,rhoM,dummy,sM;
2448  logpL = log(1e-10);
2449  logpR = log(pressure_Trho(Tsat,rhosat));
2450 
2451  do
2452  {
2453  logpM = (logpL + logpR)/2;
2454  temperature_ph(exp(logpM),h,TM,rhoM,dummy,dummy,dummy,dummy);
2455  sM = entropy_Trho(TM,rhoM);
2456  if (sM < s)
2457  {logpR = logpM;} // Keep the left half of the domain
2458  else
2459  {logpL = logpM;}// Keep the right half of the domain
2460  iter += 1;
2461  }
2462  while (iter < 10);
2463 
2464  rho_guess = rhoM;
2465  T_guess = TM;
2466  singlephase_initialized = true;
2467  }
2468  else
2469  {
2470  HSSatFuncClass SatFunc(h,s,this);
2471  Tout = Secant(&SatFunc,limits.Tmin,0.4*(crit.T-limits.Tmin),1e-8,100,&errstr);
2472  // It is two-phase, we are done, no exceptions were raised
2473  rhoout = SatFunc.rho; rhoLout = SatFunc.rhoL; rhoVout = SatFunc.rhoV; TsatLout = Tout; TsatVout = Tout;
2474  return;
2475  }
2476  }
2477 
2478 
2479  // Branch #3 (vapor curve) for enthalpy between critical enthalpy and maximum saturated enthalpy
2480  if (h > crit.h && !singlephase_initialized) // By the above conditional the enthalpy is below max
2481  {
2482  double Tsat,rhosat,TL,TV,rhoL,rhoV;
2483 
2484  // Get the saturated vapor state for the given enthalpy
2485  this->saturation_h(h, HS.T_hmax, crit.T, 1, Tsat, rhosat, TL, TV, rhoL, rhoV);
2486 
2487  // Check the saturated entropy for the given value of the enthalpy
2488  ssat = this->entropy_Trho(Tsat, rhosat);
2489 
2490  if (fabs(s - ssat) < ssat_tol) // It's saturated vapor
2491  {
2492  Tout = Tsat; rhoout = rhoV; rhoLout = rhoL; rhoVout = rhoV; TsatLout = TL; TsatVout = TV;
2493  return;
2494  }
2495  else if (s < ssat)// It's subcooled liquid
2496  {
2497  int iter = 0;
2498  double logpL, logpR, logpM,TM,rhoM,dummy, sM;
2499 
2500  logpL = log(pressure_Trho(Tsat,rhosat)); logpR = log(10*crit.p.Pa);
2501 
2502  do
2503  {
2504  logpM = (logpL + logpR)/2;
2505  temperature_ph(exp(logpM),h,TM,rhoM,dummy,dummy,dummy,dummy);
2506  sM = entropy_Trho(TM,rhoM);
2507  if (sM < s)
2508  {logpR = logpM;} // Keep the left half of the domain
2509  else
2510  {logpL = logpM;}// Keep the right half of the domain
2511  iter += 1;
2512  }
2513  while (iter < 10);
2514 
2515  rho_guess = rhoM;
2516  T_guess = TM;
2517  singlephase_initialized = true;
2518  }
2519  // If entropy greater than ssat, two-phase solution
2520  else
2521  {
2522  HSSatFuncClass SatFunc(h,s,this);
2523  Tout = Secant(&SatFunc,limits.Tmin,0.4*(crit.T-limits.Tmin),1e-8,100,&errstr);
2524  // It is two-phase, we are done, no exceptions were raised
2525  rhoout = SatFunc.rho; rhoLout = SatFunc.rhoL; rhoVout = SatFunc.rhoV; TsatLout = Tout; TsatVout = Tout;
2526  return;
2527  }
2528  }
2529 
2530 
2531  // Branch #4 (liquid curve) for enthalpy between critical enthalpy and minimum of enthalpy corresponding to the
2532  // critical point and the enthalpy corresponding to the
2533  // saturated vapor at the minimum temperature
2534  // (This branch might not exist)
2535  else if (crit.h > HS.hV_Tmin && !singlephase_initialized)
2536  {
2537  double Tsat,rhosat,TL,TV,rhoL,rhoV;
2538 
2539  // Get the saturated liquid state for the given enthalpy
2540  this->saturation_h(h, limits.Tmin, crit.T, 0, Tsat, rhosat, TL, TV, rhoL, rhoV);
2541  //double hcheck = this->enthalpy_Trho(TL,rhoL);
2542 
2543  // Check the saturated entropy for the given value of the entropy
2544  ssat = this->entropy_Trho(Tsat, rhosat);
2545 
2546  if (fabs(s - ssat) < ssat_tol) // It's saturated liquid
2547  {
2548  Tout = Tsat; rhoout = rhoL; rhoLout = rhoL; rhoVout = rhoV; TsatLout = TL; TsatVout = TV;
2549  return;
2550  }
2551  else if (s < ssat) // It's subcooled liquid
2552  {
2553  int iter = 0;
2554  double logpL, logpR, logpM,TM,rhoM,dummy, sM;
2555 
2556  logpL = log(pressure_Trho(Tsat,rhosat));
2557  logpR = log(10*crit.p.Pa);
2558 
2559  do
2560  {
2561  logpM = (logpL + logpR)/2;
2562  temperature_ph(exp(logpM),h,TM,rhoM,dummy,dummy,dummy,dummy);
2563  sM = entropy_Trho(TM,rhoM);
2564  if (sM < s)
2565  {logpR = logpM;} // Keep the left half of the domain
2566  else
2567  {logpL = logpM;}// Keep the right half of the domain
2568  iter += 1;
2569  }
2570  while (iter < 10);
2571 
2572  rho_guess = rhoM;
2573  T_guess = TM;
2574  singlephase_initialized = true;
2575  }
2576  else
2577  {
2578  HSSatFuncClass SatFunc(h,s,this);
2579  Tout = Secant(&SatFunc,limits.Tmin,0.4*(crit.T-limits.Tmin),1e-8,100,&errstr);
2580  // It is two-phase, we are done, no exceptions were raised
2581  rhoout = SatFunc.rho; rhoLout = SatFunc.rhoL; rhoVout = SatFunc.rhoV; TsatLout = Tout; TsatVout = Tout;
2582  return;
2583  }
2584  }
2585 
2587  //if (!singlephase_initialized && TsatL_initialized && TsatV_initialized)
2588  //{
2589  // // First check for two-phase
2590  // HSSatFuncClass SatFunc(h,s,this);
2591  // try{
2592  // *Tout = Brent(&SatFunc, TsatL_candidate, TsatV_candidate, 1e-16, 1e-8, 100, &errstr);
2593  // if (SatFunc.Q > 1+1e-8 || SatFunc.Q < 0-1e-8){
2594  // throw ValueError("Solution must be within the two-phase region");
2595  // }
2596  // // It is two-phase, we are done, no exceptions were raised
2597  // *rhoout = SatFunc.rho; *rhoLout = SatFunc.rhoL; *rhoVout = SatFunc.rhoV; *TsatLout = *Tout; *TsatVout = *Tout;
2598  // return;
2599  // }
2600  // catch(std::exception){
2601  // throw ValueError(format("HS:2phase failed").c_str());
2602  // }
2603  //}
2604 
2605  if (!singlephase_initialized)
2606  {
2607  throw ValueError(format("HS:1phase, but not initialized").c_str());
2608  }
2609 
2610  //HSSatFuncClass SatFunc(h,s,this);
2611 
2612  // Evaluate the function at the minimum temperature which is
2613  // usually the triple point temperature
2614  //double Ttriple_func = SatFunc.call(limits.Tmin);
2615 
2616  /*FILE *fp = fopen("rr.txt","w");
2617  for (double T = limits.Tmin; T < 300; T += 1)
2618  {
2619  double f = SatFunc.call(T);
2620  fprintf(fp, "%g %g %g\n",T,f,SatFunc.Q);
2621  }
2622  fclose(fp);*/
2623 
2624  // If using the minimum temperature yields the h/s pair, quit
2625  /*if (fabs(Ttriple_func)<DBL_EPSILON*10)
2626  {
2627  *Tout = limits.Tmin;
2628  *rhoout = SatFunc.rho;
2629  *rhoLout = SatFunc.rhoL;
2630  *rhoVout = SatFunc.rhoV;
2631  *TsatLout = *Tout;
2632  *TsatVout = *Tout;
2633  return;
2634  }
2635  else if (Ttriple_func < 0){throw ValueError(format("Value for h,s [%g,%g] is solid",h,s));}*/
2636 
2637  //try{
2638  // //*Tout = Brent(&SatFunc,limits.Tmin,reduce.T-100,1e-16,1e-8,100,&errstr);
2639  // *Tout = Secant(&SatFunc,limits.Tmin,1,1e-8,100,&errstr);
2640  // if (SatFunc.Q >1 || SatFunc.Q < 0){ throw ValueError("Solution must be within the two-phase region"); }
2641  // // It is two-phase, we are done, no exceptions were raised
2642  // *rhoout = SatFunc.rho;
2643  // *rhoLout = SatFunc.rhoL;
2644  // *rhoVout = SatFunc.rhoV;
2645  // *TsatLout = *Tout;
2646  // *TsatVout = *Tout;
2647  // return;
2648  //}
2649  //catch(ValueError)
2650  //{
2651  // double Tsat, rhosat, cp, hsat;
2652  // // It is single-phase, either liquid, gas, or supercritical, but we don't know which yet
2653  // try{
2654  // if (s < crit.s){ this->saturation_s(s, 0, &Tsat, &rhosat); }
2655  // else{ this->saturation_s(s, 1, &Tsat, &rhosat); }
2656 
2657  // // Check the saturated enthalpy for the given value of the entropy
2658  // hsat = this->enthalpy_Trho(Tsat, rhosat);
2659  // cp = this->specific_heat_p_Trho(Tsat, rhosat);
2660  // if (cp > 10) cp = 10; // Near critical point cp goes to infinity, clip it
2661  // T_guess = Tsat + (h-hsat)/cp;
2662  // rho_guess = rhosat;
2663  // }
2664  // catch(ValueError)
2665  // {
2666  // double cp0 = specific_heat_p_ideal_Trho(reduce.T);
2667  // T_guess = 298;
2668  // rho_guess = 0.001;
2669  // }
2670  //}
2671 
2672  tau = reduce.T/T_guess;
2673  delta = rho_guess/reduce.rho;
2674 
2677  worst_error=999;
2678  iter=0;
2679  while (worst_error>1e-6)
2680  {
2681  // All the required partial derivatives
2682  a0 = phi0(tau,delta);
2683  da0_dtau = dphi0_dTau(tau,delta);
2684  d2a0_dtau2 = d2phi0_dTau2(tau,delta);
2685  da0_ddelta = dphi0_dDelta(tau,delta);
2686  d2a0_ddelta_dtau = 0.0;
2687 
2688  ar = phir(tau,delta);
2689  dar_dtau = dphir_dTau(tau,delta);
2690  d2ar_dtau2 = d2phir_dTau2(tau,delta);
2691  dar_ddelta = dphir_dDelta(tau,delta);
2692  d2ar_ddelta2 = d2phir_dDelta2(tau,delta);
2693  d2ar_ddelta_dtau = d2phir_dDelta_dTau(tau,delta);
2694 
2695  // Residual and derivatives thereof for entropy
2696  f1 = tau*(da0_dtau+dar_dtau)-ar-a0-s/R();
2697  df1_dtau = tau*(d2a0_dtau2 + d2ar_dtau2)+(da0_dtau+dar_dtau)-dar_dtau-da0_dtau;
2698  df1_ddelta = tau*(d2a0_ddelta_dtau+d2ar_ddelta_dtau)-dar_ddelta-da0_ddelta;
2699 
2700  // Residual and derivatives thereof for enthalpy
2701  f2 = (1+delta*dar_ddelta)+tau*(da0_dtau+dar_dtau)-tau*h/(R()*reduce.T);
2702  df2_dtau = delta*d2ar_ddelta_dtau+da0_dtau+dar_dtau+tau*(d2a0_dtau2+d2ar_dtau2)-h/(R()*reduce.T);
2703  df2_ddelta = (dar_ddelta+delta*d2ar_ddelta2)+tau*(d2a0_ddelta_dtau+d2ar_ddelta_dtau);
2704 
2705  //First index is the row, second index is the column
2706  A[0][0]=df1_dtau;
2707  A[0][1]=df1_ddelta;
2708  A[1][0]=df2_dtau;
2709  A[1][1]=df2_ddelta;
2710 
2711  MatInv_2(A,B);
2712  tau -= B[0][0]*f1+B[0][1]*f2;
2713  delta -= B[1][0]*f1+B[1][1]*f2;
2714 
2715  if (fabs(f1)>fabs(f2))
2716  worst_error=fabs(f1);
2717  else
2718  worst_error=fabs(f2);
2719 
2720  iter += 1;
2721  if (iter>100)
2722  {
2723  throw SolutionError(format("Ths did not converge with inputs h=%g s=%g for fluid %s",h,s,(char*)name.c_str()));
2724  Tout = _HUGE;
2725  rhoout = _HUGE;
2726  }
2727  }
2728 
2729  Tout = reduce.T/tau;
2730  rhoout = delta*reduce.rho;
2731 }
2732 
2733 void Fluid::density_Ts(double T, double s, double &rhoout, double &pout, double &rhoLout, double &rhoVout, double &psatLout, double &psatVout)
2734 {
2735  double rho_guess, ssatL, ssatV;
2736  bool _SinglePhase;
2737 
2738  // Density is solved from entropy_Trho method for single phase and from
2739  // saturation information for two-phase. First get the phase.
2740  if (T >= crit.T)
2741  {
2742  _SinglePhase = true;
2743  rho_guess = crit.p.Pa/R()/T;
2744  }
2745  else
2746  {
2747  // T < crit.T
2748  // First try to define the phase with the ancillary equations, if we are
2749  // far enough away from saturation.
2750  ssatL = ssatL_anc(T);
2751  ssatV = ssatV_anc(T);
2752  if (s < ssatL - 0.05*std::abs(ssatL))
2753  {
2754  // liquid
2755  _SinglePhase = true;
2756  rho_guess = rhosatL(T);
2757  }
2758  else if (s > ssatV + 0.05*std::abs(ssatV))
2759  {
2760  // superheated vapor
2761  _SinglePhase = true;
2762  rho_guess = params.ptriple/R()/T;
2763  }
2764  else
2765  {
2766  // Actually have to use saturation information sadly
2767  // For the given temperature, find the saturation state
2768  // Run the saturation routines to determine the saturation densities and pressures
2769  saturation_T(T, enabled_TTSE_LUT, psatLout, psatVout, rhoLout, rhoVout);
2770  ssatL = entropy_Trho(T, rhoLout);
2771  ssatV = entropy_Trho(T, rhoVout);
2772  double Q = (s-ssatL)/(ssatV-ssatL);
2773  if (Q < -100*DBL_EPSILON)
2774  {
2775  // liquid
2776  _SinglePhase = true;
2777  rho_guess = rhosatL(T);
2778  }
2779  else if (Q > 1+100*DBL_EPSILON)
2780  {
2781  // superheated vapor
2782  _SinglePhase = true;
2783  rho_guess = params.ptriple/R()/T;
2784  }
2785  else
2786  {
2787  // two-phase
2788  // Return the quality weighted values
2789  _SinglePhase = false;
2790  rhoout = 1/(Q/rhoVout +(1-Q)/rhoLout);
2791  pout = Q* psatVout +(1-Q)*psatLout;
2792  return;
2793  }
2794  }
2795  }
2796 
2797  // Function class for iterative solution of entropy_Trho for rho
2798  class rhoFuncClass : public FuncWrapper1D
2799  {
2800  private:
2801  double T, s;
2802  Fluid *pFluid;
2803  public:
2804  rhoFuncClass(double T, double s, Fluid *pFluid)
2805  {
2806  this->T = T;
2807  this->s = s;
2808  this->pFluid = pFluid;
2809  };
2810 
2811  double call(double rho)
2812  {
2813  return pFluid->entropy_Trho(T, rho) - s;
2814  };
2815  };
2816 
2817  // Calculate density and pressure for single phase states
2818  if (_SinglePhase == true)
2819  {
2820  rhoFuncClass rhoFunc(T, s, this);
2821  std::string errstr;
2822  rhoout = BoundedSecant(&rhoFunc, rho_guess, 0.0, 1e10, 0.1*rho_guess, 1e-8, 100, &errstr);
2823  pout = pressure_Trho(T, rhoout);
2824  }
2825 }
2826 
2827 double Fluid::Tsat_anc(double p, double Q)
2828 {
2829  // This one only uses the ancillary equations
2830 
2831  double Tc,Tmax,Tmin,Tmid;
2832 
2833  Tc=reduce.T;
2834  Tmax=Tc;
2835  Tmin=limits.Tmin;
2836 
2837  Tmid = (Tmax+Tmin)/2.0;
2838 
2839  double tau_max = Tc/Tmin;
2840  double tau_min = Tc/Tmax;
2841 
2842  class SatFuncClass : public FuncWrapper1D
2843  {
2844  private:
2845  double p,Q,Tc;
2846  std::string name;
2847  Fluid * pFluid;
2848  public:
2849  SatFuncClass(double p_, double Q_, double Tc_, std::string name_,Fluid *_pFluid){
2850  p=p_;Q=Q_;Tc=Tc_,name=name_,pFluid = _pFluid;
2851  };
2852  double call(double tau){
2853  if (fabs(Q-1)<10*DBL_EPSILON)
2854  return log(pFluid->psatV_anc(Tc/tau)/p);
2855  else if (fabs(Q)<10*DBL_EPSILON)
2856  return log(pFluid->psatL_anc(Tc/tau)/p);
2857  else
2858  throw ValueError("Quality must be 1 or 0");
2859  };
2860  } SatFunc(p,Q,reduce.T,name,this);
2861 
2862  // Use Brent's method to find tau = Tc/T
2863  std::string errstr;
2864  try
2865  {
2866  double tau = Brent(&SatFunc,tau_min,tau_max,1e-10,1e-8,50,&errstr);
2867  if (errstr.size()>0)
2868  throw SolutionError("Saturation calculation failed");
2869  return reduce.T/tau;
2870  }
2871  catch(std::exception &)
2872  {
2873  // At very low pressures the above solver will sometimes fail due to
2874  // the uncertainty of the ancillary pressure equation at low temperature (pressure)
2875  // Here we just do a quadratic interpolation
2876 
2877  double logp_min, logp_mid, logp_max;
2878  double Tmid = (Tmax+Tmin)/2.0;
2879  double tau_mid = Tc/Tmid;
2880 
2881  if (fabs(Q-1)<1e-10)
2882  {
2883  // reduced pressures
2884  logp_min = log(psatV_anc(Tmin));
2885  logp_mid = log(psatV_anc(Tmid));
2886  logp_max = log(psatV_anc(Tmax));
2887  }
2888  else if (fabs(Q)<1e-10)
2889  {
2890  logp_min = log(psatL_anc(Tmin));
2891  logp_mid = log(psatL_anc(Tmid));
2892  logp_max = log(psatL_anc(Tmax));
2893  }
2894  else
2895  {
2896  throw ValueError(format("Quality [%g] must be either 1 or 0",Q));
2897  }
2898 
2899  // plotting tc/t versus log(p) tends to give very close to straight line
2900  // use this fact find t more easily using reverse quadratic interpolation
2901  double tau_interp = QuadInterp(logp_min,logp_mid,logp_max,tau_max,tau_mid,tau_min,log(p));
2902 
2903  //std::cout << "tsat_anc" << psatV_anc(reduce.T/tau_interp) << '\n';
2904  return reduce.T/tau_interp;
2905  }
2906  throw ValueError("Something went wrong");
2907  return -_HUGE;
2908 }
2909 
2910 
2915 {
2916 private:
2917  double arL, arV, dar_ddeltaL, dar_ddeltaV, d2ar_ddelta2L, d2ar_ddelta2V, d2ar_ddelta_dtauL, d2ar_ddelta_dtauV;
2918  double dar_dtauL,dar_dtauV;
2919  double p,R,rhoc,Tc,deltaL,deltaV,tau;
2920  Fluid *pFluid;
2921 public:
2922  SaturationFunctionOfPressureResids(Fluid *pFluid, double p, double R, double rhoc, double Tc){
2923  this->pFluid = pFluid;
2924  this->p = p;
2925  this->R = R;
2926  this->rhoc = rhoc;
2927  this->Tc = Tc;
2928  };
2930 
2931  void calculate_parameters(double deltaV, double deltaL, double tau)
2932  {
2933  arL = pFluid->phir(tau,deltaL);
2934  arV = pFluid->phir(tau,deltaV);
2935  dar_ddeltaL = pFluid->dphir_dDelta(tau,deltaL);
2936  dar_ddeltaV = pFluid->dphir_dDelta(tau,deltaV);
2937  dar_dtauL = pFluid->dphir_dTau(tau,deltaL);
2938  dar_dtauV = pFluid->dphir_dTau(tau,deltaV);
2939  d2ar_ddelta2L = pFluid->d2phir_dDelta2(tau,deltaL);
2940  d2ar_ddelta2V = pFluid->d2phir_dDelta2(tau,deltaV);
2941  d2ar_ddelta_dtauL = pFluid->d2phir_dDelta_dTau(tau,deltaL);
2942  d2ar_ddelta_dtauV = pFluid->d2phir_dDelta_dTau(tau,deltaV);
2943  }
2944  double J(char Q)
2945  {
2946  if (Q=='L')
2947  return deltaL*(1+deltaL*dar_ddeltaL);
2948  else
2949  return deltaV*(1+deltaV*dar_ddeltaV);
2950  }
2951  double J_delta(char Q)
2952  {
2953  if (Q=='L')
2954  return 1+2*deltaL*dar_ddeltaL+deltaL*deltaL*d2ar_ddelta2L;
2955  else
2956  return 1+2*deltaV*dar_ddeltaV+deltaV*deltaV*d2ar_ddelta2V;
2957  }
2958  double J_tau(char Q)
2959  {
2960  if (Q=='L')
2961  return deltaL*deltaL*d2ar_ddelta_dtauL;
2962  else
2963  return deltaV*deltaV*d2ar_ddelta_dtauV;
2964  }
2965  double K(char Q)
2966  {
2967  if (Q=='L')
2968  return deltaL*dar_ddeltaL+arL+log(deltaL);
2969  else
2970  return deltaV*dar_ddeltaV+arV+log(deltaV);
2971  }
2972  double K_delta(char Q)
2973  {
2974  if (Q=='L')
2975  return 2*dar_ddeltaL+deltaL*d2ar_ddelta2L+1/deltaL;
2976  else
2977  return 2*dar_ddeltaV+deltaV*d2ar_ddelta2V+1/deltaV;
2978  }
2979  double K_tau(char Q)
2980  {
2981  if (Q=='L')
2982  return deltaL*d2ar_ddelta_dtauL+dar_dtauL;
2983  else
2984  return deltaV*d2ar_ddelta_dtauV+dar_dtauV;
2985  }
2986 
2987  std::vector<double> call(std::vector<double> x)
2988  {
2989 
2990  deltaV = x[0];
2991  deltaL = x[1];
2992  tau = x[2];
2993 
2994  // Calculate all the parameters that are needed for the derivatives
2995  calculate_parameters(deltaV,deltaL,tau);
2996 
2997  std::vector<double> out = std::vector<double>(3,0);
2998  out[0]=J('V')-J('L');
2999  out[1]=K('V')-K('L');
3000  out[2]=1+deltaV*dar_ddeltaV-p*tau/(R*Tc*deltaV*rhoc);
3001  return out;
3002  }
3003  std::vector<std::vector<double> > Jacobian(std::vector<double> x)
3004  {
3005  deltaV = x[0];
3006  deltaL = x[1];
3007  tau = x[2];
3008  std::vector<std::vector<double> > out;
3009  out.resize(x.size(),std::vector<double>(x.size(),0));
3010 
3011  out[0][0] = J_delta('V');
3012  out[0][1] = -J_delta('L');
3013  out[0][2] = J_tau('V')-J_tau('L');
3014  out[1][0] = K_delta('V');
3015  out[1][1] = -K_delta('L');
3016  out[1][2] = K_tau('V')-K_tau('L');
3017  out[2][0] = deltaV*d2ar_ddelta2V+dar_ddeltaV+p*tau/(R*Tc*deltaV*deltaV*rhoc);
3018  out[2][1] = 0;
3019  out[2][2] = deltaV*d2ar_ddelta_dtauV-p/(R*Tc*deltaV*rhoc);
3020  return out;
3021  }
3022 };
3023 
3025 {
3026 private:
3027  double p;
3028  Fluid *pFluid;
3029 public:
3030  double rhoL, rhoV, resid;
3031  SaturationPressureGivenResids(Fluid *pFluid, double p) : p(p), pFluid(pFluid) {};
3033  double call(double T)
3034  {
3035  rhoL = pFluid->density_Tp(T,p,rhoL);
3036  rhoV = pFluid->density_Tp(T,p,rhoV);
3037  resid = pFluid->gibbs_Trho(T,rhoL)-pFluid->gibbs_Trho(T,rhoV);
3038  return resid;
3039  }
3040 };
3041 
3043 {
3044 private:
3045  double p;
3046  Fluid *pFluid;
3047 public:
3048  double rhoL, rhoV, resid;
3049  Saturation_p_IterateSaturationT_Resids(Fluid *pFluid, double p) : p(p), pFluid(pFluid) {};
3051  double call(double T)
3052  {
3053  double psatL,psatV;
3054  pFluid->saturation_T(T,pFluid->enabled_TTSE_LUT,psatL,psatV,rhoL,rhoV);
3055  resid = psatL-this->p;
3056  return resid;
3057  }
3058 };
3059 
3060 void Fluid::saturation_p(double p, bool UseLUT, double &TsatL, double &TsatV, double &rhoLout, double &rhoVout)
3061 {
3062  double Tsat,rhoL,rhoV;
3063 
3064  // Pseudo-critical pressure based on critical density and temperature
3065  // The highest pressure that be achieved with a temperature <= Tc
3066  // For some EOS, pc != p(Tc,rhoc)
3067  double pc_EOS = pressure_Trho(reduce.T,reduce.rho);
3068 
3069  if (fabs(p-reduce.p.Pa)<DBL_EPSILON || p > pc_EOS)
3070  {
3071  TsatL = reduce.T;
3072  TsatV = reduce.T;
3073  rhoLout = reduce.rho;
3074  rhoVout = reduce.rho;
3075  return;
3076  }
3077 
3078  if (get_debug_level()>5){
3079  std::cout<<format("%s:%d: Fluid::saturation_p(%g,%d) \n",__FILE__,__LINE__,p,UseLUT).c_str();
3080  }
3081  if (pure()==true)
3082  {
3083  if (UseLUT)
3084  {
3085  throw NotImplementedError("Saturation calculation is not implemented for LUT.");
3086  }
3087  else
3088  {
3090  std::string errstr;
3092  Tsat = Tsat_anc(p,0);
3093  rhoL = rhosatL(Tsat);
3094  rhoV = rhosatV(Tsat);
3095  SPGR.rhoL = rhoL;
3096  SPGR.rhoV = rhoV;
3097  try{
3098  //Tsat = Secant(&SPGR,Tsat,1e-2*Tsat,1e-8,50,&errstr);
3099  Tsat = Secant(&SPGR,Tsat,1e-4,1e-10,50,&errstr);
3100  if (errstr.size()>0 || !ValidNumber(Tsat)|| !ValidNumber(SPGR.rhoV)|| !ValidNumber(SPGR.rhoL))
3101  throw SolutionError("Saturation calculation failed");
3102  rhoVout = SPGR.rhoV;
3103  rhoLout = SPGR.rhoL;
3104  TsatL = Tsat;
3105  TsatV = Tsat;
3106  return;
3107  }
3108  catch(std::exception &) // Whoops that failed...
3109  {
3110  errstr.clear();
3111  // Now try to get Tsat by using Brent's method on saturation_T calls
3113  Tsat = Tsat_anc(p,0);
3114  if (Tsat >= crit.T){
3115  Tsat = crit.T-0.0000001;
3116  }
3117  rhoL = rhosatL(Tsat);
3118  rhoV = rhosatV(Tsat);
3119  SPGR.rhoL = rhoL;
3120  SPGR.rhoV = rhoV;
3121  double Tmin = Tsat-3;
3122  if (Tmin < limits.Tmin){
3123  Tmin = limits.Tmin;
3124  }
3125  Tsat = Brent(&SPISTR,Tmin,reduce.T,DBL_EPSILON,1e-10,30,&errstr);
3126  if (errstr.size()>0 || !ValidNumber(Tsat)|| !ValidNumber(SPGR.rhoV)|| !ValidNumber(SPGR.rhoL))
3127  throw SolutionError("Saturation calculation yielded invalid number using Brent's method");
3128  rhoVout = SPGR.rhoV;
3129  rhoLout = SPGR.rhoL;
3130  TsatL = Tsat;
3131  TsatV = Tsat;
3132  return;
3133  }
3134  }
3135  }
3136  else
3137  {
3138  // Pseudo-pure fluid
3139  TsatL = Tsat_anc(p,0);
3140  TsatV = Tsat_anc(p,1);
3141  rhoLout = rhosatL(TsatL);
3142  rhoVout = rhosatV(TsatV);
3143  return;
3144  }
3145 
3146 
3147 
3148 
3149  /*SaturationFunctionOfPressureResids SFPR = SaturationFunctionOfPressureResids(this,p,params.R_u/params.molemass,reduce.rho,reduce.T);
3150  Eigen::Vector3d x0_initial, x;
3151  Tsat = Tsat_anc(p,0);
3152  rhoL = rhosatL(Tsat);
3153  rhoV = rhosatV(Tsat);
3154  x0_initial << rhoV/reduce.rho, rhoL/reduce.rho, reduce.T/Tsat;
3155  std::string errstring;
3156  x=NDNewtonRaphson_Jacobian(&SFPR,x0_initial,1e-7,30,&errstring);
3157  *rhoVout = reduce.rho*x(0);
3158  *rhoLout = reduce.rho*x(1);
3159  *Tout = reduce.T/x(2);
3160  if (errstring.size()>0 && !ValidNumber(Tsat))
3161  throw SolutionError("Saturation calculation failed");
3162  return;*/
3163 }
3164 
3165 double Fluid::Tsat(double p, double Q, double T_guess)
3166 {
3167  double rhoLout,rhoVout;
3168  return Tsat(p, Q, T_guess, false, rhoLout, rhoVout);
3169 }
3170 
3171 double Fluid::Tsat(double p, double Q, double T_guess, bool UseLUT, double &rhoLout, double &rhoVout)
3172 {
3173  if (isPure && !UseLUT)
3174  {
3175  double TL,TV;
3176  saturation_p(p,UseLUT,TL,TV,rhoLout,rhoVout);
3177  return TL;
3178  }
3179  else
3180  {
3181  double Tc,Tmax,Tmin;
3182 
3183  Tc=crit.T;
3184  Tmax=Tc-0.001;
3185  Tmin=params.Ttriple+1;
3186  if (Tmin <= limits.Tmin)
3187  Tmin = limits.Tmin;
3188 
3189  // Plotting Tc/T versus log(p) tends to give very close to straight line
3190  // Use this fact find T more easily
3191 
3192  class SatFuncClass : public FuncWrapper1D
3193  {
3194  private:
3195  double p,Q,Tc;
3196  std::string name;
3197  Fluid * pFluid;
3198  public:
3199  SatFuncClass(double p_, double Q_, double Tc_, std::string name, Fluid * pFluid){
3200  p=p_;Q=Q_;Tc=Tc_,this->name=name,this->pFluid = pFluid;
3201  };
3202  double call(double tau){
3203  if (fabs(Q)<10*DBL_EPSILON)
3204  {
3205  return log(pFluid->psatL(Tc/tau)/p);
3206  }
3207  else if (fabs(Q-1)<10*DBL_EPSILON)
3208  {
3209  return log(pFluid->psatV(Tc/tau)/p);
3210  }
3211  else
3212  {
3213  throw ValueError(format("Must be either saturated liquid or vapor"));
3214  }
3215  };
3216  } SatFunc(p,Q,reduce.T,name,this);
3217 
3218  double tau_max = Tc/Tmin;
3219  double tau_min = Tc/Tmax;
3220 
3221  // Use Brent's method to find tau = Tc/T
3222  std::string errstr;
3223  double tau = Brent(&SatFunc,tau_min,tau_max,1e-10,1e-10,50,&errstr);
3224  if (errstr.size()>0)
3225  throw SolutionError("Saturation calculation failed");
3226  if (!isPure)
3227  {
3228  rhoLout = rhosatL(reduce.T/tau);
3229  rhoVout = rhosatV(reduce.T/tau);
3230  }
3231  return reduce.T/tau;
3232  }
3233 }
3234 
3235 double Fluid::R()
3236 {
3237  return params.R_u*1000.0/params.molemass;
3238 }
3239 
3240 double Fluid::viscosity_dilute(double T, double e_k, double sigma)
3241 {
3242  // T in [K], e_k in [K], sigma in [nm]
3243  // viscosity returned is in [Pa-s]
3244 
3245  /*
3246  Model for the Viscosity and Thermal Conductivity of Refrigerants,
3247  Including a New Correlation for the Viscosity of R134a,
3248  Marcia L. Huber, Arno Laesecke, and Richard A. Perkins
3249  Ind. Eng. Chem. Res. 2003, 42, 3163-3178
3250  */
3251 
3252  double eta_star, Tstar, OMEGA_2_2;
3253  Tstar = T/e_k;
3254  // From Neufeld, 1972, Journal of Chemical Physics - checked coefficients
3255  OMEGA_2_2 = 1.16145*pow(Tstar,-0.14874)+ 0.52487*exp(-0.77320*Tstar)+2.16178*exp(-2.43787*Tstar);
3256  // Using the leading constant from McLinden, 2000 since the leading term from Huber 2003 gives crazy values
3257  eta_star = 26.692e-3*sqrt(params.molemass*T)/(pow(sigma,2)*OMEGA_2_2)/1e6;
3258  return eta_star;
3259 }
3260 double Fluid::conductivity_critical(double T, double rho, double qd, double GAMMA, double zeta0)
3261 {
3262  // Olchowy and Sengers cross-over term
3263 
3264  double k=1.3806488e-23, //[J/K]
3265  R0=1.03,
3266  gamma=1.239,
3267  nu=0.63,
3268  Pcrit = reduce.p.Pa, //[Pa]
3269  Tref = 1.5*reduce.T, //[K]
3270  cp,cv,delta,num,zeta,mu,
3271  OMEGA_tilde,OMEGA_tilde0,pi=M_PI,tau;
3272 
3273  delta = rho/reduce.rho;
3274 
3275  tau = reduce.T/T;
3276  double dp_drho=R()*T*(1+2*delta*dphir_dDelta(tau,delta)+delta*delta*d2phir_dDelta2(tau,delta));
3277  double X = Pcrit/pow(reduce.rho,2)*rho/dp_drho;
3278 
3279  tau = reduce.T/Tref;
3280  double dp_drho_ref=R()*Tref*(1+2*delta*dphir_dDelta(tau,delta)+delta*delta*d2phir_dDelta2(tau,delta));
3281  double Xref = Pcrit/pow(reduce.rho,2)*rho/dp_drho_ref*Tref/T;
3282  num=X-Xref;
3283 
3284  // no critical enhancement if numerator is negative
3285  if (num<0)
3286  return 0.0;
3287  else
3288  zeta=zeta0*pow(num/GAMMA,nu/gamma); //[m]
3289 
3290  cp = specific_heat_p_Trho(T,rho); //[J/kg/K]
3291  cv = specific_heat_v_Trho(T,rho); //[J/kg/K]
3292  mu = viscosity_Trho(T,rho)*1e6; //[uPa-s]
3293 
3294  OMEGA_tilde=2.0/pi*((cp-cv)/cp*atan(zeta*qd)+cv/cp*(zeta*qd)); //[-]
3295  OMEGA_tilde0=2.0/pi*(1.0-exp(-1.0/(1.0/(qd*zeta)+1.0/3.0*(zeta*qd)*(zeta*qd)/delta/delta))); //[-]
3296 
3297  double lambda=rho*cp*1e6*(R0*k*T)/(6*pi*mu*zeta)*(OMEGA_tilde-OMEGA_tilde0); //[W/m/K]
3298  return lambda; //[W/m/K]
3299 }
3301 {
3302  /* Implements the method of Miqeu et al., "An extended scaled equation for the temperature dependence of
3303  the surface tension of pure compounds inferred from an analysis of experimental data",Fluid Phase
3304  Equilibria 172 (2000) 169–182
3305 
3306  This correlation is used as the default, in the absence of other correlation, which can be provided for fluids if desired
3307 
3308  sigma in [mN/m]-->[N/m]
3309  */
3310  double N_A = 6.02214129e23, //[-] CODATA 2010
3311  k = 1.3806488e-23, //[J/K] CODATA 2010
3312  w = params.accentricfactor, //[-]
3313  Vc, //[cm^3/mol]
3314  Tc, //[K]
3315  t, //[K]
3316  sigma; //[mN/m]
3317  Tc=reduce.T;
3318  t=1-T/Tc;
3319  //[m3/kg]*[kg/kmol]*[0.001 kmol/mol] --> [m3/mol]
3320  Vc = 1/reduce.rho*params.molemass/1000;
3321  // N_A has units of 1/mol, Vc has units of m3/mol , (1/m3)^(2/3)->1/m^2
3322  // k has units of J/K
3323  // Tc has units of K
3324  // k*Tc has units of J, or N-m
3325  sigma = k*Tc*pow(N_A/Vc,2.0/3.0)*(4.35+4.14*w)*pow(t,1.26)*(1+0.19*sqrt(t)-0.25*t);
3326  return sigma;
3327 }
3328 
3329 void Fluid::ECSParams(double *e_k, double *sigma)
3330 {
3331  // The default ECS parameters that are used if none are coded for the fluid by overloading this function.
3332  // Applies the method of Chung;
3333  double rhobarc = reduce.rho/params.molemass;
3334  *e_k = reduce.T/1.2593;
3335  *sigma = 0.809/pow(rhobarc,1.0/3.0);
3336 }
3338 {
3339 private:
3340  Fluid * InterestFluid, * ReferenceFluid;
3341  double alpha_j, Z_j;
3342 public:
3343  ConformalTempResids(Fluid *InterestFluid, Fluid *ReferenceFluid, double alpha_j, double Z_j){
3344  this->InterestFluid = InterestFluid;
3345  this->ReferenceFluid = ReferenceFluid;
3346  this->alpha_j = alpha_j;
3347  this->Z_j = Z_j;
3348  };
3350  std::vector<double> call(std::vector<double> x)
3351  {
3352  double T0 = x[0]; double rho0 = x[1];
3353  double alpha_0 = DerivTerms(iDERphir,T0,rho0,ReferenceFluid);
3354  double Z_0 = DerivTerms(iDERZ,T0,rho0,ReferenceFluid);
3355  std::vector<double> out = std::vector<double>(2,0);
3356  out[0]=alpha_j-alpha_0;
3357  out[1]=Z_j-Z_0;
3358  return out;
3359  }
3360  std::vector<std::vector<double> > Jacobian(std::vector<double> x)
3361  {
3362  double T0=x[0]; double rho0=x[1];
3363  double dtau_dT = -ReferenceFluid->reduce.T/T0/T0;
3364  double ddelta_drho = 1/ReferenceFluid->reduce.rho;
3365  std::vector<std::vector<double> > out;
3366  out.resize(x.size(),std::vector<double>(x.size(),0));
3367  // Terms for the fluid of interest drop out
3368  double dalpha_dT0 = -DerivTerms(iDERdphir_dTau,T0,rho0,ReferenceFluid)*dtau_dT;
3369  out[0][0] = dalpha_dT0;
3370  double dalpha_drho0 = -DerivTerms(iDERdphir_dDelta,T0,rho0,ReferenceFluid)*ddelta_drho;
3371  out[0][1] = dalpha_drho0;
3372  double dZ_dT0 = -DerivTerms(iDERdZ_dTau,T0,rho0,ReferenceFluid)*dtau_dT;
3373  out[1][0] = dZ_dT0;
3374  double dZ_drho0 = -DerivTerms(iDERdZ_dDelta,T0,rho0,ReferenceFluid)*ddelta_drho;
3375  out[1][1] = dZ_drho0;
3376 
3377  return out;
3378  }
3379 };
3380 
3381 std::vector<double> Fluid::ConformalTemperature(Fluid *InterestFluid, Fluid *ReferenceFluid,double T, double rho, double T0, double rho0, std::string *errstring)
3382 {
3383  int iter=0;
3384  double error,v0,v1,delta,tau,dp_drho;
3385 
3386  //The values for the fluid of interest that are the target
3387  double alpha_j = DerivTerms(iDERphir,T,rho,InterestFluid);
3388  double Z_j = DerivTerms(iDERZ,T,rho,InterestFluid);
3389 
3390  std::vector<double> f0,v,negative_f0;
3391  std::vector<std::vector<double> > J;
3392  ConformalTempResids CTR = ConformalTempResids(InterestFluid,ReferenceFluid,alpha_j,Z_j);
3393  std::vector<double> x0 = std::vector<double>(2,0);
3394  x0[0] = T0;
3395  x0[1] = rho0;
3396 
3397  // Check whether the starting guess is already pretty good
3398  error = root_sum_square(CTR.call(x0));
3399 
3400  // Make a copy so that if the calculations fail, we can return the original values
3401  std::vector<double> x0_initial = x0;
3402 
3403  try{
3404  // First naively try to just use the Newton-Raphson solver without any
3405  // special checking of the values.
3406  x0=NDNewtonRaphson_Jacobian(&CTR,x0_initial,1e-10,30,errstring);
3407  error = root_sum_square(CTR.call(x0));
3408  if (fabs(error)>1e-2 || x0[0]<0.0 || x0[1]<0.0 || !ValidNumber(x0[0]) || !ValidNumber(x0[1])){
3409  throw ValueError("Error calculating the conformal state for ECS");
3410  }
3411  // convert to STL vector to avoid Eigen library in header
3412  std::vector<double> xout(2,x0[0]);
3413  xout[1] = x0[1];
3414  return xout;
3415  }
3416  catch(std::exception &){}
3417  // Ok, that didn't work, so we need to try something more interesting
3418  // Local Newton-Raphson solver with bounds checking on the step values
3419  error=999;
3420  iter=0;
3421  x0 = x0_initial; // Start back at unity shape factors
3422  while (iter<30 && fabs(error)>1e-6)
3423  {
3424  T = x0[0];
3425  rho = x0[1];
3426  tau = reduce.T/T;
3427  delta = rho/reduce.rho;
3428  dp_drho=R()*T*(1+2*delta*dphir_dDelta(tau,delta)+delta*delta*d2phir_dDelta2(tau,delta));
3429 
3430  //if (dp_drho<0)
3431  //{
3432  // if (rho > ReferenceFluid->reduce.rho)
3433  // x0(1)*=1.04;
3434  // else
3435  // x0(0)*=0.96;
3436  //}
3437  //Try to take a step
3438  f0 = CTR.call(x0);
3439  J = CTR.Jacobian(x0);
3440 
3441  // Negate f0
3442  negative_f0 = f0;
3443  for (unsigned int i = 0; i<f0.size(); i++){ negative_f0[i] *= -1;}
3444 
3445  v = linsolve(J,negative_f0);
3446  v0 = v[0];
3447  v1 = v[1];
3448  if (x0[0]-v[0]>0 && x0[1]-v[1]>0)
3449  {
3450  x0[0] -= v[0];
3451  x0[1] -= v[1];
3452  }
3453  else
3454  {
3455  x0[0] -= 1.05*x0[0];
3456  x0[1] -= 1.05*x0[1];
3457  }
3458  error = root_sum_square(f0);
3459  iter += 1;
3460  if (iter>29)
3461  {
3462  *errstring = std::string("ConformalTemperature reached maximum number of steps without reaching solution");
3463  return x0_initial;
3464  }
3465  }
3466  // convert to STL vector
3467  std::vector<double> xout(2,x0[0]);
3468  xout[1] = x0[1];
3469  return xout;
3470 };
3471 
3472 double Fluid::viscosity_ECS_Trho(double T, double rho, Fluid * ReferenceFluid)
3473 {
3474  /*
3475  Implements the method of
3476  Marcia L. Huber, Arno Laesecke, and Richard A. Perkins
3477  "Model for the Viscosity and Thermal Conductivity of Refrigerants,
3478  Including a New Correlation for the Viscosity of R134a"
3479  Ind. Eng. Chem. Res. 2003, 42, 3163-3178
3480  */
3481  double e_k,sigma,e0_k, sigma0, Tc0,rhoc0,T0,rho0,rhoc,Tc,
3482  eta_dilute,theta,phi,f,h,eta_resid,M,M0,F_eta,eta,psi,
3483  rhoc0bar,rhobar,rhocbar,rho0bar;
3484  std::vector<double> x0;
3485 
3486  Tc0=ReferenceFluid->reduce.T;
3487  rhoc0=ReferenceFluid->reduce.rho;
3488  M0=ReferenceFluid->params.molemass;
3489  rhoc0bar = rhoc0/M0;
3490  Tc = reduce.T;
3491  rhoc = reduce.rho;
3492  M = params.molemass;
3493  rhocbar=rhoc/M;
3494  rhobar=rho/M;
3495 
3496  try{
3497  // Get the ECS params for the fluid if it has them
3498  ECSParams(&e_k,&sigma);
3499  }
3500  catch (const NotImplementedError &){
3501  try{
3502  // Get the ECS parameters from the reference fluid
3503  ReferenceFluid->ECSParams(&e0_k,&sigma0);
3504  }
3505  catch (const NotImplementedError &){
3506  // Doesn't have e_k and sigma for reference fluid
3507  throw NotImplementedError(format("Your reference fluid for ECS [%s] does not have an implementation of ECSParams",(char *)ReferenceFluid->get_name().c_str()));
3508  }
3509  //Estimate the ECS parameters from Huber and Ely, 2003
3510  e_k = e0_k*Tc/Tc0;
3511  sigma = sigma0*pow(rhoc/rhoc0,1.0/3.0);
3512  }
3513 
3514  // The dilute portion is for the fluid of interest, not for the reference fluid
3515  // It is the viscosity in the limit of zero density
3516  eta_dilute = viscosity_dilute(T,e_k,sigma); //[uPa-s]
3517 
3518  if (1)//(T>reduce.T)
3519  {
3520  // Get the conformal temperature. To start out here, assume that the shape factors are unity
3521  theta=1;
3522  phi=1;
3523  }
3524  else
3525  {
3526  /* Use the method from
3527  Isabel M. Marruchoa, James F. Ely, "Extended corresponding states for pure polar
3528  and non-polar fluids: an improved method for component shape factor prediction",
3529  Fluid Phase Equilibria 150–151 1998 215–223
3530 
3531  Reynes and Thodos,
3532  APPLICATION OF A REDUCED VAPOR
3533  PRESSURE EQUATION TO
3534  NONHYDROROCARBON SUBSTANCES
3535  beta = 5/9*gamma-40/27
3536  gamma = 9/5*beta + 9/5*40/27
3537  gamma = 9/5*beta + 8/3
3538  */
3539  double Bstar,Bstar0,Cstar,Cstar0,DELTABstar,DELTACstar,Zc,Zc0;
3540  double omega = params.accentricfactor;
3541  double omega0 = ReferenceFluid->params.accentricfactor;
3542  double Tstar = T/reduce.T;
3543  Zc = reduce.p.Pa/(reduce.rho*R()*reduce.T);
3544  Zc0 = ReferenceFluid->reduce.p.Pa/(ReferenceFluid->reduce.rho*ReferenceFluid->R()*ReferenceFluid->reduce.T);
3545  Bstar = -6.207612-15.37641*omega-0.574946*pow(10,-omega);
3546  Bstar0 = -6.207612-15.37641*omega0-0.574946*pow(10,-omega0);
3547  Cstar = 8/3+9*Bstar/5.0/log(10.0);
3548  Cstar0 = 8/3+9*Bstar0/5.0/log(10.0);
3549  DELTABstar = Bstar-Bstar0;
3550  DELTACstar = Cstar-Cstar0;
3551  theta = (1-Cstar0+2*pow(1-Tstar,2.0/7.0)*log(Zc/Zc0)-DELTABstar+DELTACstar*log(Tstar)+Bstar/Tstar)/(1-Cstar0+Bstar0/Tstar);
3552  phi = pow(Zc,pow(1-Tstar,2.0/7.0))/pow(Zc0,pow(1-Tstar/theta,2.0/7.0));
3553  }
3554 
3555  psi = ECS_psi_viscosity(rho/reduce.rho);
3556 
3557  f=Tc/Tc0*theta;
3558  //Must be the ratio of MOLAR densities!!
3559  h=rhoc0bar/rhocbar*phi;
3560  T0=T/f;
3561  rho0bar=rhobar*h;
3562  //Get the mass density for the reference fluid [kg/m3]
3563  rho0=rho0bar*M0;
3564  std::string errstring;
3565  // First check whether you should use this code in the first place.
3566  // Implementing the method of TRNECS from REFPROP
3567  double delta = rho/reduce.rho;
3568  double tau = reduce.T/T;
3569  double Z = 1+delta*dphir_dDelta(tau,delta);
3570  double p0 = Z*R()*T0*rho0;
3571  if (Z<0.3 || p0>1.1*ReferenceFluid->reduce.p.Pa || rho0>ReferenceFluid->reduce.rho){
3572  // Use the code to calculate the conformal state
3573  x0=ConformalTemperature(this,ReferenceFluid,T,rho,T0,rho0,&errstring);
3574  T0=x0[0];
3575  rho0=x0[1];
3576  }
3577  rho0bar = rho0/M0;
3578  h = rho0bar/rhobar;
3579  f = T/T0;
3580 
3581  eta_resid = ReferenceFluid->viscosity_background(T0,rho0*psi);
3582  F_eta = sqrt(f)*pow(h,-2.0/3.0)*sqrt(M/M0);
3583  eta = eta_dilute+eta_resid*F_eta;
3584  return eta;
3585 }
3586 
3587 double Fluid::conductivity_ECS_Trho(double T, double rho, Fluid * ReferenceFluid)
3588 {
3589  /*
3590  Implements the method of
3591  Marcia L. Huber, Arno Laesecke, and Richard A. Perkins
3592  "Model for the Viscosity and Thermal Conductivity of Refrigerants,
3593  Including a New Correlation for the Viscosity of R134a"
3594  Ind. Eng. Chem. Res. 2003, 42, 3163-3178
3595  */
3596  double e_k,sigma,e0_k, sigma0, Tc0,rhoc0,T0,rho0,rhoc,Tc,
3597  eta_dilute,theta,phi,f,h,lambda_resid,M,M0,F_lambda,lambda,chi,
3598  f_int,lambda_int,lambda_crit,lambda_star,rhoc0bar,rhobar,rhocbar,rho0bar;
3599  std::vector<double> x0;
3600 
3601  // Properties for the reference fluid
3602  Tc0=ReferenceFluid->reduce.T;
3603  rhoc0=ReferenceFluid->reduce.rho;
3604  M0=ReferenceFluid->params.molemass;
3605  rhoc0bar = rhoc0/M0;
3606 
3607  // Properties for the given fluid
3608  Tc = reduce.T;
3609  rhoc = reduce.rho;
3610  M = params.molemass;
3611  rhocbar=rhoc/M;
3612  rhobar=rho/M;
3613 
3614  try{
3615  // Get the ECS params for the fluid if it has them
3616  ECSParams(&e_k,&sigma);
3617  }
3618  catch(NotImplementedError &){
3619  try{
3620  //Estimate the ECS parameters from Huber and Ely, 2003
3621  ReferenceFluid->ECSParams(&e0_k,&sigma0);
3622  }
3623  catch (NotImplementedError &){
3624  // Doesn't have e_k and sigma for reference fluid
3625  throw NotImplementedError(format("Your reference fluid for ECS [%s] does not have an implementation of ECSParams",(char *)ReferenceFluid->get_name().c_str()));
3626  }
3627  e_k = e0_k*Tc/Tc0;
3628  sigma = sigma0*pow(rhoc/rhoc0,1.0/3.0);
3629  }
3630 
3631  // The dilute portion is for the fluid of interest, not for the reference fluid
3632  // It is the viscosity in the limit of zero density
3633  // It has units of Pa-s here
3634  eta_dilute = viscosity_dilute(T,e_k,sigma); //[Pa-s]
3635 
3636  chi = ECS_chi_conductivity(rho/reduce.rho);
3637  f_int = ECS_f_int(T);
3638 
3639  // Get the conformal temperature. To start out here, assume that the shape factors are unity
3640  theta=1.0;
3641  phi=1.0;
3642  f=Tc/Tc0*theta;
3643  h=rhoc0bar/rhocbar*phi;
3644  T0=T/f;
3645  rho0bar=rhobar*h;
3646  //Get the mass density for the reference fluid [kg/m3]
3647  rho0=rho0bar*M0;
3648 
3649  std::string errstring;
3650  // First check whether you should use this code in the first place.
3651  // Implementing the method of TRNECS from REFPROP
3652  double delta = rho/reduce.rho;
3653  double tau = reduce.T/T;
3654  double Z = 1+delta*dphir_dDelta(tau,delta);
3655  double p0 = Z*R()*T0*rho0; //[Pa]
3656  if (Z<0.3 || p0 > 1.1*ReferenceFluid->reduce.p.Pa || rho0 > ReferenceFluid->reduce.rho){
3657  // Use the code to calculate the conformal state
3658  x0=ConformalTemperature(this,ReferenceFluid,T,rho,T0,rho0,&errstring);
3659  T0=x0[0];
3660  rho0=x0[1];
3661  }
3662 
3663  rho0bar = rho0/M0;
3664  h=rho0bar/rhobar;
3665  f=T/T0;
3666 
3667  // Ideal-gas specific heat in the limit of zero density
3668  double cpstar = specific_heat_p_ideal_Trho(T); //[J/kg/K]
3669  lambda_star = 15e-3*R()*(eta_dilute*1e3)/4.0; //[W/m/K]
3670  lambda_int = f_int*(eta_dilute*1e3)*(cpstar-5.0/2.0*R() ); //[W/m/K]
3671  F_lambda = sqrt(f)*pow(h,-2.0/3.0)*sqrt(M0/M); //[-]
3672 
3673  //Get the background conductivity from the reference fluid
3674  lambda_resid = ReferenceFluid->conductivity_background(T0,rho0*chi);//[W/m/K]
3675 
3676  lambda_crit = conductivity_critical(T,rho); //[W/m/K]
3677  lambda = lambda_int+lambda_star+lambda_resid*F_lambda+lambda_crit; //[W/m/K]
3678  return lambda; //[W/m/K]
3679 }
3680 
3681 bool Fluid::build_TTSE_LUT(bool force_build)
3682 {
3683  if (!built_TTSE_LUT || force_build)
3684  {
3685  // No value has been set for the parameters
3686  if (pmin_TTSE>1e100 && pmax_TTSE > 1e100 && hmin_TTSE > 1e00 && hmax_TTSE > 1e100)
3687  {
3688  double psatL,psatV,rhoL,rhoV;
3689 
3690  this->disable_TTSE_LUT(); // Disable TTSE to do these calculations
3691  saturation_T(limits.Tmin, false, psatL, psatV, rhoL, rhoV);
3692  double hL = enthalpy_Trho(limits.Tmin,rhoL);
3693  double hV = enthalpy_Trho(limits.Tmin,rhoV);
3694  hmin_TTSE = hL;
3695  hmax_TTSE = hL+(hV-hL)*2;
3696  pmin_TTSE = std::min(psatL, psatV);
3697  pmax_TTSE = 2*reduce.p.Pa;
3698  }
3699 
3700  // Turn off the use of LUT while you are building it,
3701  // otherwise you get an infinite recursion
3702  enabled_TTSE_LUT = false;
3703 
3704  TTSESatL = TTSETwoPhaseTableClass(this,0);
3706  TTSESatV = TTSETwoPhaseTableClass(this,1);
3709 
3713  // T,rho will mirror the size and range of h,p
3721 
3722  // If we can read the LUT from file, we are done and don't need to rebuild
3724  {
3725  // Build all the tables
3727  TTSESinglePhase.build_Trho(-1, -1, -1, -1, &TTSESatL, &TTSESatV);// Allow method to figure out the range using h,p since -1 passed for T and rho limits
3728 
3729  // Write all the matrices and arrays to files
3732  }
3733  }
3734 
3735  built_TTSE_LUT = true;
3736  enabled_TTSE_LUT = true;
3737  }
3738  return true;
3739 }
3740 
3748 
3763 void Fluid::set_TTSESat_LUT_size(int Nsat){Nsat_TTSE = Nsat;};
3765 void Fluid::set_TTSESinglePhase_LUT_size(int Np, int Nh){Np_TTSE = Np; Nh_TTSE = Nh;};
3767 void Fluid::set_TTSESinglePhase_LUT_range(double hmin, double hmax, double pmin, double pmax){hmin_TTSE = hmin; hmax_TTSE = hmax; pmin_TTSE = pmin; pmax_TTSE = pmax;};
3769 void Fluid::get_TTSESinglePhase_LUT_range(double *hmin, double *hmax, double *pmin, double *pmax){*hmin = hmin_TTSE; *hmax = hmax_TTSE; *pmin = pmin_TTSE; *pmax = pmax_TTSE;};
3770 
3771 void AncillaryCurveClass::update(Fluid *_pFluid, std::string Output)
3772 {
3773  pFluid = _pFluid;
3774  iOutput = get_param_index(Output);
3775 }
3777 {
3778  double T,rhoV,rhoL;
3779 
3780  double Tmin = pFluid->params.Ttriple+1e-6;
3781  if (Tmin<pFluid->limits.Tmin)
3782  Tmin = pFluid->limits.Tmin+1e-6;
3783  double Tmax = pFluid->reduce.T-10*DBL_EPSILON;
3784  for(int i = 0; i<N; i++)
3785  {
3786  T = Tmin+(Tmax-Tmin)/(N-1)*i;
3787  xL.push_back(T);
3788  xV.push_back(T);
3789  rhoL = pFluid->rhosatL(T);
3790  rhoV = pFluid->rhosatV(T);
3791  // IProps will always return value in standard units, convert to SI
3792  if (iOutput==iH)
3793  {
3794  yL.push_back(pFluid->enthalpy_Trho(T,rhoL));
3795  yV.push_back(pFluid->enthalpy_Trho(T,rhoV));
3796  }
3797  else if (iOutput == iS)
3798  {
3799  yL.push_back(pFluid->entropy_Trho(T,rhoL));
3800  yV.push_back(pFluid->entropy_Trho(T,rhoV));
3801  }
3802  else
3803  {
3804  throw ValueError(format("iOutput [%d] in build is invalid", iOutput).c_str());
3805  }
3806  }
3807  built = true;
3808  return 1;
3809 }
3810 
3812  return interp1d(&xL,&yL,T);
3813 }
3814 
3816  return interp1d(&xV,&yV,T);
3817 }
3818 
3820  return interp1d(&yL,&xL,y);
3821 }
3822 
3824  return interp1d(&yV,&xV,y);
3825 }
3826 
3827 double CriticalSplineStruct_T::interpolate_rho(Fluid *pFluid, int phase, double T)
3828 {
3829  // Use the spline interpolation since you are very close to the critical point
3830  // R = rho/rhoc-1 => we are solving for R for liquid and vapor using a spline
3831  // since we know that dtaudR|crit = 0, tau|crit = 1, R|crit = 0, and we know
3832  // rho and drhodT_sat at Tend, the end of the normal Akasaka solving method
3833  //
3834  // Essentially you have tau = aR^3+bR^2+cR+d, where c = 0 and d = 1 from constraints
3835  // at critical point
3836  // Can check cubic solution from http://www.akiti.ca/Quad3Deg.html
3837  // Also, wikipedia has good docs on cubics: http://en.wikipedia.org/wiki/Cubic_function, especially the "Trigonometric (and hyperbolic) method" section
3838 
3839  double rhoc = pFluid->reduce.rho;
3840  double Tc = pFluid->reduce.T;
3841  double tauend = Tc/Tend, tau = Tc/T;
3842 
3843  double k1,k2,R,Rend,a,b,c,d,rhoend;
3844  // If you are within a microKelvin of critical point, return critical point
3845  if (fabs(T-Tc)<1e-6)
3846  {
3847  return rhoc;
3848  }
3849  if (phase == 0)
3850  {
3851  k1 = -tauend/Tend*rhoc/drhoLdT_sat; // k1 = dtaudR|sat at Tend
3852  k2 = tauend;
3853  Rend = rhoendL/rhoc-1; // R at Tend
3854  rhoend = rhoendL;
3855  }
3856  else if (phase == 1)
3857  {
3858  k1 = -tauend/Tend*rhoc/drhoVdT_sat; // k1 = dtaudR|sat at Tend
3859  k2 = tauend;
3860  Rend = rhoendV/rhoc-1; // R at Tend
3861  rhoend = rhoendV;
3862  }
3863  else
3864  {
3865  throw ValueError();
3866  }
3867 
3868  // Linear system to find constants a,b for the spline cubic
3869  /*
3870  k2 = aR^3+bR^2+1
3871  k1 = 3aR^2+2bR
3872  */
3873  double a11 = Rend*Rend*Rend;
3874  double a12 = Rend*Rend;
3875  double b1 = k2-1;
3876  double a21 = 3*Rend*Rend;
3877  double a22 = 2*Rend;
3878  double b2 = k1;
3879 
3880  // Cramer's rule to find a,b
3881  double det = a11*a22-a21*a12;
3882  a = (b1*a22-a12*b2)/det;
3883  b = (b2*a11-a21*b1)/det;
3884 
3885  // Constants from critical point
3886  c = 0;
3887  d = 1-tau;
3888 
3889  // Discriminant
3890  double DELTA = 18*a*b*c*d-4*b*b*b*d+b*b*c*c-4*a*c*c*c-27*a*a*d*d;
3891 
3892  // Coefficients for the depressed cubic t^3+p*t+q = 0
3893  double p = (3*a*c-b*b)/(3*a*a);
3894  double q = (2*b*b*b-9*a*b*c+27*a*a*d)/(27*a*a*a);
3895 
3896  if (DELTA<0)
3897  {
3898  // One real root
3899  double t0;
3900  if (4*p*p*p+27*q*q>0 && p<0)
3901  {
3902  t0 = -2.0*fabs(q)/q*sqrt(-p/3.0)*cosh(1.0/3.0*acosh(-3.0*fabs(q)/(2.0*p)*sqrt(-3.0/p)));
3903  }
3904  else
3905  {
3906  t0 = -2.0*sqrt(p/3.0)*sinh(1.0/3.0*asinh(3.0*q/(2.0*p)*sqrt(3.0/p)));
3907  }
3908  R = t0-b/(3*a);
3909  }
3910  else //(DELTA>0)
3911  {
3912  // Three real roots
3913  double t0 = 2.0*sqrt(-p/3.0)*cos(1.0/3.0*acos(3.0*q/(2.0*p)*sqrt(-3.0/p))-0*2.0*M_PI/3.0);
3914  double t1 = 2.0*sqrt(-p/3.0)*cos(1.0/3.0*acos(3.0*q/(2.0*p)*sqrt(-3.0/p))-1*2.0*M_PI/3.0);
3915  double t2 = 2.0*sqrt(-p/3.0)*cos(1.0/3.0*acos(3.0*q/(2.0*p)*sqrt(-3.0/p))-2*2.0*M_PI/3.0);
3916 
3917  double R0 = t0-b/(3*a);
3918  double R1 = t1-b/(3*a);
3919  double R2 = t2-b/(3*a);
3920 
3921  // The solution for R must be bounded between Rend and 0
3922  if (R0*Rend > 0 && fabs(R0)<=fabs(Rend))
3923  {
3924  R = R0;
3925  }
3926  else if (R1*Rend > 0 && fabs(R1)<=fabs(Rend))
3927  {
3928  R = R1;
3929  }
3930  else if (R2*Rend > 0 && fabs(R2)<=fabs(Rend))
3931  {
3932  R = R2;
3933  }
3934  else
3935  {
3936  throw ValueError(format("No solution found for R"));
3937  }
3938  }
3939 
3940  return rhoc*(1+R);
3941 }
3942 
3943 
3944 
3946 {
3947  UseCriticalSpline = false;
3949  std::vector<std::string> fluid_names = strsplit(Fluids.FluidList(),',');
3950 
3951  double rhoL=0, rhoV=0, drhodTL=0, drhodTV=0;
3952 
3953  FILE *fp;
3954  fp = fopen("CriticalSplineConstants_T.h","w");
3955  for (unsigned int i = 0; i < fluid_names.size(); i++)
3956  {
3957  std::cout << format("%s:\n",fluid_names[i].c_str()).c_str();
3958  CoolPropStateClass CPS(fluid_names[i]);
3959  double Tc = CPS.pFluid->reduce.T;
3960  double Tt = CPS.pFluid->params.Ttriple;
3961  if (!CPS.pFluid->pure())
3962  {
3963  // Skip pseudo-pure fluids
3964  continue;
3965  }
3966  double good;
3967  try{
3968  if (Tc-5>Tt)
3969  {
3970  CPS.update(iT,Tc-5,iQ,1);
3971  rhoV = CPS.rhoV(); rhoL = CPS.rhoL();
3972  drhodTV = CPS.drhodT_along_sat_vapor();
3973  drhodTL = CPS.drhodT_along_sat_liquid();
3974  if (!ValidNumber(drhodTV) || !ValidNumber(drhodTL)){throw ValueError();}
3975  good = 5;
3976  }
3977  else
3978  {
3979  CPS.update(iT,Tc-1,iQ,1);
3980  rhoV = CPS.rhoV(); rhoL = CPS.rhoL();
3981  drhodTV = CPS.drhodT_along_sat_vapor();
3982  drhodTL = CPS.drhodT_along_sat_liquid();
3983  if (!ValidNumber(drhodTV) || !ValidNumber(drhodTL)){throw ValueError();}
3984  good = 1;
3985  }
3986  }
3987  catch(std::exception &)
3988  {
3989  if (CPS.pFluid->reduce.T > 100)
3990  {
3991  std::cout << format("%s : failed at 20 K \n",fluid_names[i].c_str()).c_str();
3992  continue;
3993  }
3994  else
3995  {
3996  std::cout << format("%s : failed at 1 K \n",fluid_names[i].c_str()).c_str();
3997  continue;
3998  }
3999  }
4000  bool valid = false;
4001 
4002  for (double step = 0.5; step > 1e-10; step /= 100.0)
4003  {
4004  valid = true;
4005  for (double b = good; b>0; b -= step)
4006  {
4007  try{
4008  CPS.update(iT, Tc - b, iQ, 1);
4009  rhoV = CPS.rhoV(); rhoL = CPS.rhoL();
4010  drhodTV = CPS.drhodT_along_sat_vapor();
4011  drhodTL = CPS.drhodT_along_sat_liquid();
4012  }
4013  catch(std::exception &){
4014  valid = false;
4015  break;
4016  }
4017  if (!ValidNumber(drhodTV) || !ValidNumber(drhodTL) || drhodTV*drhodTL > 0){
4018  //std::cout << format("%0.20g",good) << std::endl;
4019  valid = false;
4020  break;
4021  }
4022  else
4023  {
4024  good = b;
4025  }
4026  }
4027  if (!valid){
4028  break;
4029  }
4030  }
4031  CoolPropStateClass CPS2(fluid_names[i]);
4032  CPS2.update(iT,Tc-good,iQ,1.0);
4033  rhoV = CPS2.rhoV(); rhoL = CPS2.rhoL();
4034  drhodTV = CPS2.drhodT_along_sat_vapor();
4035  drhodTL = CPS2.drhodT_along_sat_liquid();
4036  std::cout << format("%0.20g",good).c_str() << std::endl;
4037  fprintf(fp,"\tstd::make_pair(std::string(\"%s\"),CriticalSplineStruct_T(%0.12e,%0.12e,%0.12e,%0.12e,%0.12e) ),\n",fluid_names[i].c_str(),Tc-good,rhoL,rhoV,drhodTL,drhodTV);
4038  }
4039  fclose(fp);
4040  UseCriticalSpline = true;
4041 }
4042 
4043 std::string Fluid::to_json()
4044 {
4046  dd.SetObject();
4047 
4048  // Fluid name
4050  _name.SetString(get_name().c_str(),dd.GetAllocator());
4051  dd.AddMember("NAME", _name, dd.GetAllocator());
4052 
4053  // CAS code
4055  _cas.SetString(params.CAS.c_str(),dd.GetAllocator());
4056  dd.AddMember("CAS", _cas, dd.GetAllocator());
4057 
4058  // Aliases
4060  std::vector<std::string> aliasesv = get_aliases();
4061  for (std::vector<std::string>::iterator it = aliasesv.begin(); it != aliasesv.end(); it++)
4062  {
4064  _aliases.SetString((*it).c_str(),dd.GetAllocator());
4065  aliases.PushBack(_aliases,dd.GetAllocator());
4066  }
4067  dd.AddMember("ALIASES", aliases, dd.GetAllocator());
4068 
4069  // The equation(s) of state
4070  rapidjson::Value reducing_state(rapidjson::kObjectType);
4073  the_EOS.AddMember("gas_constant",params.R_u,dd.GetAllocator());
4074  the_EOS.AddMember("gas_constant_units","J/mol/K",dd.GetAllocator());
4075  the_EOS.AddMember("molar_mass",params.molemass/1000,dd.GetAllocator());
4076  the_EOS.AddMember("molar_mass_units","kg/mol",dd.GetAllocator());
4077  the_EOS.AddMember("accentric",params.accentricfactor,dd.GetAllocator());
4078  the_EOS.AddMember("accentric_units","-",dd.GetAllocator());
4079  the_EOS.AddMember("ptriple",params.ptriple*1000,dd.GetAllocator());
4080  the_EOS.AddMember("ptriple_units","Pa",dd.GetAllocator());
4081  the_EOS.AddMember("Ttriple",params.Ttriple,dd.GetAllocator());
4082  the_EOS.AddMember("Ttriple_units","K",dd.GetAllocator());
4083 
4084  double rhoL = PropsSI("D","T",params.Ttriple+1e-3,"Q",0,name.c_str());
4085  double rhoV = PropsSI("D","T",params.Ttriple+1e-3,"Q",1,name.c_str());
4086  if (!ValidNumber(rhoL)) rhoL = 1e99;
4087  if (!ValidNumber(rhoV)) rhoV = 1e99;
4088 
4089  the_EOS.AddMember("rhoLtriple",rhoL/params.molemass*1000,dd.GetAllocator());
4090  the_EOS.AddMember("rhoLtriple_units","mol/m^3",dd.GetAllocator());
4091  the_EOS.AddMember("rhoVtriple",rhoV/params.molemass*1000,dd.GetAllocator());
4092  the_EOS.AddMember("rhoVtriple_units","mol/m^3",dd.GetAllocator());
4093 
4094  the_EOS.AddMember("BibTeX_EOS",BibTeXKeys.EOS.c_str(),dd.GetAllocator());
4095  the_EOS.AddMember("BibTeX_CP0",BibTeXKeys.CP0.c_str(),dd.GetAllocator());
4096 
4097  the_EOS.AddMember("pseudo_pure", !pure(), dd.GetAllocator());
4098 
4099  // The reducing state
4100  reducing_state.AddMember("T", reduce.T, dd.GetAllocator());
4101  reducing_state.AddMember("T_units", "K", dd.GetAllocator());
4102  reducing_state.AddMember("rhomolar", reduce.rho/params.molemass*1000, dd.GetAllocator());
4103  reducing_state.AddMember("rhomolar_units", "mol/m^3", dd.GetAllocator());
4104  reducing_state.AddMember("p", reduce.p.Pa, dd.GetAllocator());
4105  reducing_state.AddMember("p_units", "Pa", dd.GetAllocator());
4106 
4107  the_EOS.AddMember("reducing_state",reducing_state,dd.GetAllocator());
4108  the_EOS.AddMember("T_max", limits.Tmax, dd.GetAllocator());
4109  the_EOS.AddMember("T_max_units", "K", dd.GetAllocator());
4110  the_EOS.AddMember("p_max", limits.pmax, dd.GetAllocator());
4111  the_EOS.AddMember("p_max_units", "Pa", dd.GetAllocator());
4112 
4114  for (std::vector<phi_BC*>::const_iterator it = phirlist.begin(); it != phirlist.end(); it++)
4115  {
4117  (*it)->to_json(entry, dd);
4118  alphar.PushBack(entry,dd.GetAllocator());
4119  }
4120  the_EOS.AddMember("alphar",alphar,dd.GetAllocator());
4121 
4123  for (std::vector<phi_BC*>::const_iterator it = phi0list.begin(); it != phi0list.end(); it++)
4124  {
4126  (*it)->to_json(entry, dd);
4127  alpha0.PushBack(entry,dd.GetAllocator());
4128  }
4129  the_EOS.AddMember("alpha0",alpha0,dd.GetAllocator());
4130 
4131  EOS.PushBack(the_EOS,dd.GetAllocator());
4132 
4133  dd.AddMember("EOS",EOS,dd.GetAllocator());
4134 
4135  rapidjson::StringBuffer buffer;
4137 
4138  dd.Accept(writer);
4139  std::string json0 = buffer.GetString();
4140  //std::cout << json0 << std::endl;
4141 
4142  FILE *fp;
4143  fp = fopen((get_name()+".json").c_str(),"w");
4144  fprintf(fp,"%s",json0.c_str());
4145  fclose(fp);
4146 
4147  return std::string();
4148 
4149 }
double JSON_lookup_double(rapidjson::Document &root, std::string FluidName, std::string key)
Definition: FluidClass.cpp:49
HSSatFuncClass(double h, double s, Fluid *pFluid)
std::vector< std::vector< double > > Jacobian(std::vector< double > x)
struct HSContainer HS
The point that is used to reduce the T and rho for EOS.
Definition: FluidClass.h:223
std::string root_path
Definition: TTSE.h:105
Fluid * get_fluid(long iFluid)
Definition: CoolProp.cpp:236
void rhosatPure(double T, double &rhoLout, double &rhoVout, double &pout, double omega, bool use_guesses)
DensityTpResids(Fluid *pFluid, double T, double p)
Definition: FluidClass.cpp:135
void MatInv_2(double A[2][2], double B[2][2])
double BoundedSecant(FuncWrapper1D *f, double x0, double xmin, double xmax, double dx, double tol, int maxiter, std::string *errstring)
Definition: Solvers.cpp:179
std::vector< double > a_hs_satL
Definition: FluidClass.h:38
double root_sum_square(std::vector< double > x)
double drhoVdT_sat
Derivative of density w.r.t. temperature along the saturated vapor curve.
Definition: FluidClass.h:114
virtual double d3phir_dDelta_dTau2(double tau, double delta)
Definition: FluidClass.cpp:362
std::vector< phi_BC * > phirlist
Definition: FluidClass.h:178
double R()
Returns the mass-specific gas constant for the fluid in the desired units.
virtual double d2phi0_dTau2(double tau, double delta)
Definition: FluidClass.cpp:411
FluidCacheElement dphir_dDelta
Definition: FluidClass.h:143
#define M_PI
Definition: CoolPropTools.h:58
double interpolateV(double T)
void rebuild_CriticalSplineConstants_T()
Rebuild the constants.
int GetInt() const
Definition: document.h:422
AncillaryCurveClass * h_ancillary
True if it is a pure fluid, false otherwise.
Definition: FluidClass.h:162
int set_reference_stateP(Fluid *pFluid, std::string reference_state)
Definition: CoolProp.cpp:1049
double hV(void)
Definition: CPState.cpp:1341
virtual double d2phir_dDelta_dTau(double tau, double delta)
Definition: FluidClass.cpp:329
Saturation_p_IterateSaturationT_Resids(Fluid *pFluid, double p)
virtual void temperature_ph(double p, double h, double &Tout, double &rhoout, double &rhoL, double &rhoV, double &TsatLout, double &TsatVout, double T0=-1, double rho0=-1)
long get_Fluid_index(std::string FluidName)
Definition: CoolProp.cpp:239
double build(double pmin, double pmax, TTSETwoPhaseTableClass *other=NULL)
Definition: TTSE.cpp:2590
double reverseinterpolateL(double y)
bool build_TTSE_LUT(bool force=false)
Build of the TTSE LUT.
double rhoL(void)
Definition: CPState.h:265
virtual void temperature_ps(double p, double s, double &Tout, double &rhoout, double &rhoL, double &rhoV, double &TsatLout, double &TsatVout)
struct FluidLimits limits
Definition: FluidClass.h:219
virtual double d3phir_dDelta3(double tau, double delta)
Definition: FluidClass.cpp:353
std::string EOS
Definition: FluidClass.h:120
virtual double density_Tp(double T, double p)
Definition: FluidClass.cpp:719
double reverseinterpolateV(double y)
double rhoendV
Saturated vapor density at the last temperature for which the conventional methods can be used...
Definition: FluidClass.h:110
virtual void temperature_hs(double h, double s, double &Tout, double &rhoout, double &rhoL, double &rhoV, double &TsatLout, double &TsatVout)
rhosatPure_BrentrhoVResidClass(double T, Fluid *pFluid)
void enable_TTSE_LUT_writing(void)
Enable the writing of TTSE tables to file.
unsigned int Nsat_TTSE
Definition: FluidClass.h:172
virtual void saturation_s(double s, int Q, double &Tsatout, double &rhoout, double &TsatLout, double &TsatVout, double &rhoLout, double &rhoVout)
Definition: FluidClass.cpp:803
std::vector< std::vector< double > > Jacobian(std::vector< double > x)
double Tsat_anc(double p, double Q)
bool isenabled_TTSE_LUT_writing(void)
Check if the writing of TTSE tables to file is enabled.
virtual double d3phi0_dTau3(double tau, double delta)
Definition: FluidClass.cpp:419
double psatV_anc(double T)
Definition: FluidClass.h:380
virtual void saturation_h(double h, double Tmin, double Tmax, int Q, double &Tsatout, double &rhoout, double &TsatLout, double &TsatVout, double &rhoLout, double &rhoVout)
Definition: FluidClass.cpp:875
bool enabled_EXTTP
Definition: FluidClass.h:584
GenericValue & SetObject()
Set this value as an empty object.
Definition: document.h:229
double hmin_TTSE
Definition: FluidClass.h:171
double interpolateL(double T)
std::vector< std::string > get_aliases()
Definition: FluidClass.h:241
double specific_heat_p_ideal_Trho(double T)
Definition: FluidClass.cpp:497
virtual double viscosity_background(double T, double rho)
Definition: FluidClass.h:477
PressureUnit p
Definition: FluidClass.h:50
virtual double d3phir_dTau3(double tau, double delta)
Definition: FluidClass.cpp:346
std::string ECSReferenceFluid
A list of aliases of names for the Fluid, each element is a std::string instance. ...
Definition: FluidClass.h:154
const char * GetString() const
Definition: stringbuffer.h:25
FluidCacheElement d2phir_dDelta2
Definition: FluidClass.h:143
SaturationPressureGivenResids(Fluid *pFluid, double p)
std::string name
A container to hold the cache for residual Helmholtz derivatives.
Definition: FluidClass.h:151
AncillaryCurveClass * s_ancillary
Definition: FluidClass.h:163
unsigned SizeType
Use 32-bit array/string indices even for 64-bit platform, instead of using size_t.
Definition: rapidjson.h:67
std::vector< double > call(std::vector< double > x)
virtual double phi0(double tau, double delta)
Definition: FluidClass.cpp:381
EXPORT_CODE int CONVENTION get_debug_level()
const GenericValue & Accept(Handler &handler) const
Generate events of this value to a Handler.
Definition: document.h:494
FluidCacheElement d2phir_dTau2
Definition: FluidClass.h:143
void set_TTSESinglePhase_LUT_range(double hmin, double hmax, double pmin, double pmax)
Over-ride the default range of the single-phase LUT.
virtual double ECS_psi_viscosity(double rhor)
Definition: FluidClass.h:410
double drhodT_along_sat_liquid(void)
Definition: CPState.h:688
std::string to_json()
Export this fluid as a JSON file;.
double viscosity_ECS_Trho(double T, double rho, Fluid *ReferenceFluid)
FluidCache cache
Definition: FluidClass.h:150
void enable_EXTTP(void)
double Pa
Definition: Units.h:22
double hL_Tmin
Definition: FluidClass.h:37
double gibbs_Trho(double T, double rho)
Definition: FluidClass.cpp:513
virtual double psat(double T)
Definition: FluidClass.h:359
double rhoendL
Saturated liquid density at the last temperature for which the conventional methods can be used...
Definition: FluidClass.h:108
std::string CP0
Definition: FluidClass.h:121
double pressure_Trho(double T, double rho)
Definition: FluidClass.cpp:449
Writer with indentation and spacing.
Definition: prettywriter.h:15
void disable_EXTTP(void)
Disable the TTSE.
double TL(void)
Definition: CPState.h:269
ConformalTempResids(Fluid *InterestFluid, Fluid *ReferenceFluid, double alpha_j, double Z_j)
double molemass
Definition: FluidClass.h:43
void update(long iInput1, double Value1, long iInput2, double Value2, double T0=-1, double rho0=-1)
Definition: CPState.cpp:213
A stub class to do the density(T,p) calculations for near the critical point using Brent solver...
Definition: FluidClass.cpp:129
std::vector< double > yL
Definition: FluidClass.h:65
long phase_prho_indices(double p, double rho, double &T, double &TL, double &TV, double &rhoL, double &rhoV)
double conductivity_critical(double T, double rho, double qd=2e9, double GAMMA=0.0496, double zeta0=1.94e-10)
double psatL_anc(double T)
Definition: FluidClass.h:374
std::vector< std::string > aliases
The REFPROP-compliant name if REFPROP-"name" is not a compatible fluid name. If not included...
Definition: FluidClass.h:153
void set_size_Trho(unsigned int NT=100, unsigned int Nrho=100)
Set the sizes of the matrices with Trho as inputs.
Definition: TTSE.cpp:214
std::vector< std::string > strsplit(std::string s, char del)
double call(double T)
double rho_hmax
Definition: FluidClass.h:37
virtual double dphi0_dTau(double tau, double delta)
Definition: FluidClass.cpp:403
bool double_equal(double a, double b)
double entropy_Trho(double T, double rho)
Definition: FluidClass.cpp:473
double _get_rho_guess(double T, double p)
double hmax_TTSE
Definition: FluidClass.h:171
FluidsContainer Fluids
Definition: CoolProp.cpp:189
FluidCacheElement phir
Definition: FluidClass.h:143
TTSETwoPhaseTableClass * SatL
Definition: TTSE.h:103
double density_Tp_Soave(double T, double p, int iValue=0)
Get the density using the Soave EOS.
Definition: FluidClass.cpp:543
#define DBL_EPSILON
Definition: Helmholtz.cpp:10
struct CriticalStruct reduce
A pointer to the point that is used to reduce the T and rho for EOS.
Definition: FluidClass.h:222
std::vector< double > JSON_lookup_dblvector(rapidjson::Document &root, std::string FluidName, std::string key)
Definition: FluidClass.cpp:82
double internal_energy_Trho(double T, double rho)
Definition: FluidClass.cpp:465
Represents an in-memory output stream.
Definition: stringbuffer.h:16
long phase_Trho_indices(double T, double rho, double &pL, double &pV, double &rhoL, double &rhoV)
Return the phase using the phase flags from phase enum in CoolProp.h.
double sL(void)
Definition: CPState.cpp:1352
void set_TTSESinglePhase_LUT_size(int Np, int Nh)
Over-ride the default size of the single-phase LUT.
void update(Fluid *pFluid, std::string Output)
std::string get_name()
Returns a std::string with the name of the fluid.
Definition: FluidClass.h:235
double Tmax
Definition: FluidClass.h:54
double GetDouble() const
Definition: document.h:427
double Ttriple
Definition: FluidClass.h:43
void rhosatPure_BrentrhoV(double T, double &rhoLout, double &rhoVout, double &pout)
virtual double surface_tension_T(double T)
virtual void ECSParams(double *e_k, double *sigma)
virtual double rhosatV(double T)
Definition: FluidClass.h:371
std::string format(const char *fmt,...)
double speed_sound_Trho(double T, double rho)
Definition: FluidClass.cpp:503
Fluid * pFluid
A pointer to a CoolProp fluid.
Definition: CPState.h:195
virtual double viscosity_dilute(double T, double e_k, double sigma)
TTSETwoPhaseTableClass TTSESatV
Definition: FluidClass.h:227
Fluid is the abstract base class that is employed by all the other fluids.
Definition: FluidClass.h:147
double drhodT_along_sat_vapor(void)
Definition: CPState.h:687
GenericValue & SetString(const Ch *s, SizeType length)
Set this value as a string without copying source string.
Definition: document.h:460
FluidCacheElement dphir_dTau
Definition: FluidClass.h:143
bool IsDouble() const
Definition: document.h:205
virtual double dphir_dTau(double tau, double delta)
Definition: FluidClass.cpp:295
double PropsSI(std::string Output, std::string Name1, double Prop1, std::string Name2, double Prop2, std::string Ref)
Definition: CoolProp.cpp:886
virtual double dphi0_dDelta(double tau, double delta)
Definition: FluidClass.cpp:389
EnvironmentalFactorsStruct environment
Definition: FluidClass.h:176
SizeType Size() const
Get the number of elements in array.
Definition: document.h:333
struct CriticalStruct * preduce
Definition: FluidClass.h:221
bool IsArray() const
Definition: document.h:199
long phase_Tp_indices(double T, double p, double &pL, double &pV, double &rhoL, double &rhoV)
Return the phase using the phase flags from phase enum in CoolProp.h.
void enable_TTSE_LUT(void)
GenericValue & AddMember(GenericValue &name, GenericValue &value, Allocator &allocator)
Add a member (name-value pair) to the object.
Definition: document.h:258
void set_TTSESat_LUT_size(int Nsat)
Over-ride the default size of both of the saturation LUT.
double QuadInterp(double x0, double x1, double x2, double f0, double f1, double f2, double x)
double hsatV_anc(double T)
double build_ph(double hmin, double hmax, double pmin, double pmax, TTSETwoPhaseTableClass *SatL=NULL, TTSETwoPhaseTableClass *SatV=NULL)
Definition: TTSE.cpp:834
std::vector< double > xL
Definition: FluidClass.h:65
TTSESinglePhaseTableClass TTSESinglePhase
Definition: FluidClass.h:228
std::vector< double > x(ncmax, 0)
std::string REFPROPname
The name of the fluid.
Definition: FluidClass.h:152
bool IsString() const
Definition: document.h:206
bool isenabled_EXTTP(void)
Check if TTSE is enabled.
FluidCacheElement d2phir_dDelta_dTau
Definition: FluidClass.h:143
double call(double rho)
Definition: FluidClass.cpp:138
double rhoV(void)
Definition: CPState.h:266
std::vector< std::vector< double > > linsolve(std::vector< std::vector< double > > const &A, std::vector< std::vector< double > > const &B)
Definition: MatrixMath.cpp:185
struct OtherParameters params
Definition: FluidClass.h:220
bool isPure
A std::string that contains a reference for the transport properties of the fluid.
Definition: FluidClass.h:159
std::string FluidList()
Returns a std::string of a comma-separated list of the CoolProp names of all the fluids that are load...
Definition: AllFluids.cpp:313
GenericValue & PushBack(GenericValue &value, Allocator &allocator)
Append a value at the end of the array.
Definition: document.h:396
double hsatL_anc(double T)
double hL(void)
Definition: CPState.cpp:1330
std::string JSON_lookup_string(rapidjson::Document &root, std::string FluidName, std::string key)
Definition: FluidClass.cpp:62
std::vector< int > n_hs_satL
Definition: FluidClass.h:39
double specific_heat_v_Trho(double T, double rho)
Definition: FluidClass.cpp:480
void rhosatPure_Brent(double T, double &rhoLout, double &rhoVout, double &pout)
A document for parsing JSON text as DOM.
Definition: document.h:691
bool IsInt() const
Definition: document.h:201
CriticalSplineStruct_T CriticalSpline_T
Definition: FluidClass.h:231
double pmin_TTSE
Definition: FluidClass.h:171
void set_size(unsigned int N)
Definition: TTSE.cpp:2564
double Tsat(double p, double Q, double T_guess)
bool isBetween(double x1, double x2, double x)
virtual double psatV(double T)
Definition: FluidClass.h:365
void rhosatPure_Akasaka(double T, double &rhoLout, double &rhoVout, double &pout, double omega, bool use_guesses=false)
std::vector< double > call(std::vector< double > x)
double pmax
Definition: FluidClass.h:54
virtual ~Fluid()
Definition: FluidClass.cpp:147
void disable_TTSE_LUT_writing(void)
Disable the writing of TTSE tables to file.
double density_Tp_PengRobinson(double T, double p, int solution)
Definition: FluidClass.cpp:607
double evaluate(long iParam, double p)
Definition: TTSE.cpp:2699
params
double Brent(FuncWrapper1D *f, double a, double b, double macheps, double t, int maxiter, std::string *errstr)
Definition: Solvers.cpp:235
virtual double d2phir_dDelta2(double tau, double delta)
Definition: FluidClass.cpp:276
double sV_Tmin
Definition: FluidClass.h:37
double drhodT_p_Trho(double T, double rho)
Definition: FluidClass.cpp:537
struct CriticalStruct crit
Definition: FluidClass.h:218
double hV_Tmin
Definition: FluidClass.h:37
double p(void)
Definition: CPState.h:291
std::vector< double > ConformalTemperature(Fluid *InterestFluid, Fluid *ReferenceFluid, double T, double rho, double T0, double rho0, std::string *errstring)
double sV(void)
Definition: CPState.cpp:1363
Allocator & GetAllocator()
Get the allocator of this document.
Definition: document.h:758
TTSETwoPhaseTableClass * SatV
Definition: TTSE.h:103
double T_hmax
Definition: FluidClass.h:37
double pmax_TTSE
Definition: FluidClass.h:171
BibTeXKeysStruct BibTeXKeys
Definition: FluidClass.h:175
void write_all_to_file(std::string root_path=std::string())
Definition: TTSE.cpp:745
double sL_Tmin
Definition: FluidClass.h:37
double dpdrho_Trho(double T, double rho)
Definition: FluidClass.cpp:529
virtual double phir(double tau, double delta)
Definition: FluidClass.cpp:237
bool enabled_TTSE_LUT
Parameters for the Tabular Taylor Series Expansion (TTSE) Method.
Definition: FluidClass.h:584
double temperature_prho(double p, double rho, double T0)
double DerivTerms(long iTerm, double T, double rho, Fluid *pFluid)
Definition: CoolProp.cpp:1001
virtual double ECS_f_int(double T)
Definition: FluidClass.h:420
unsigned int Np_TTSE
Definition: FluidClass.h:172
void calculate_parameters(double deltaV, double deltaL, double tau)
virtual double conductivity_background(double T, double rho)
Definition: FluidClass.h:443
virtual double d2phi0_dDelta2(double tau, double delta)
Definition: FluidClass.cpp:396
std::string phase_Tp(double T, double p, double &pL, double &pV, double &rhoL, double &rhoV)
Return the phase given the temperature and pressure.
void get_TTSESinglePhase_LUT_range(double *hmin, double *hmax, double *pmin, double *pmax)
Get the current range of the single-phase LUT.
long get_param_index(std::string param)
Definition: CoolProp.cpp:288
std::vector< phi_BC * > phi0list
A vector of instances of the phi_BC classes for the residual Helmholtz energy contribution.
Definition: FluidClass.h:179
void update(long iInput1, double Value1, long iInput2, double Value2)
Definition: CPState.cpp:2818
double build_Trho(double Tmin, double Tmax, double rhomin, double rhomax, TTSETwoPhaseTableClass *SatL, TTSETwoPhaseTableClass *SatV)
Definition: TTSE.cpp:1051
double dpdT_Trho(double T, double rho)
Definition: FluidClass.cpp:521
double specific_heat_p_Trho(double T, double rho)
Definition: FluidClass.cpp:487
bool read_all_from_file(std::string root_path)
Definition: TTSE.cpp:588
std::vector< double > NDNewtonRaphson_Jacobian(FuncWrapperND *f, std::vector< double > x0, double tol, int maxiter, std::string *errstring)
Definition: Solvers.cpp:47
double s_hmax
Definition: FluidClass.h:37
double Secant(FuncWrapper1D *f, double x0, double dx, double tol, int maxiter, std::string *errstring)
Definition: Solvers.cpp:123
void post_load(rapidjson::Document &JSON, rapidjson::Document &JSON_CAS)
Definition: FluidClass.cpp:162
bool enable_writing_tables_to_files
Definition: FluidClass.h:584
bool HasMember(const Ch *name) const
Check whether a member exists in the object.
Definition: document.h:249
bool isAlias(std::string name)
Definition: FluidClass.cpp:435
virtual double viscosity_Trho(double T, double rho)
Definition: FluidClass.cpp:788
virtual double ECS_chi_conductivity(double rhor)
Definition: FluidClass.h:415
double rhobar
Definition: FluidClass.h:49
double hmax
Definition: FluidClass.h:37
void solve_cubic(double a, double b, double c, double d, double *x0, double *x1, double *x2)
virtual double d2phi0_dDelta_dTau(double tau, double delta)
Definition: FluidClass.cpp:427
bool isenabled_TTSE_LUT(void)
Check if TTSE is enabled.
void set_size_ph(unsigned int Np=100, unsigned int Nh=100)
Set the sizes of the matrices with h,p as inputs.
Definition: TTSE.cpp:169
std::vector< double > yV
Definition: FluidClass.h:65
double conductivity_ECS_Trho(double T, double rho, Fluid *ReferenceFluid)
std::string JSON_lookup_CAS(rapidjson::Document &root, std::string FluidName)
Definition: FluidClass.cpp:72
unsigned int Nh_TTSE
Definition: FluidClass.h:172
bool pure()
Returns true if the fluid is pure, false if pseudo-pure or a mixture.
Definition: FluidClass.h:243
void saturation_VdW(double T, double &rhoL, double &rhoV, double &p, double s0=-1)
virtual double d2phir_dTau2(double tau, double delta)
Definition: FluidClass.cpp:312
virtual double psatL(double T)
Definition: FluidClass.h:362
double temperature_prho_PengRobinson(double p, double rho)
Definition: FluidClass.cpp:687
TTSETwoPhaseTableClass TTSESatL
Definition: FluidClass.h:226
double interpolate_rho(Fluid *pFluid, int phase, double T)
virtual double dphir_dDelta(double tau, double delta)
Definition: FluidClass.cpp:257
Represents a JSON value. Use Value for UTF8 encoding and default allocator.
Definition: document.h:30
std::vector< int > JSON_lookup_intvector(rapidjson::Document &root, std::string FluidName, std::string key)
Definition: FluidClass.cpp:101
virtual double rhosatL(double T)
Definition: FluidClass.h:368
virtual void saturation_p(double p, bool UseLUT, double &TsatLout, double &TsatVout, double &rhoLout, double &rhoVout)
double ssatL_anc(double T)
virtual double d3phir_dDelta2_dTau(double tau, double delta)
Definition: FluidClass.cpp:370
std::vector< double > xV
Definition: FluidClass.h:65
bool enable_writing_tables_to_files
Definition: TTSE.h:109
virtual double conductivity_Trho(double T, double rho)
Definition: FluidClass.cpp:795
double enthalpy_Trho(double T, double rho)
Definition: FluidClass.cpp:457
const Ch * GetString() const
Definition: document.h:447
SaturationFunctionOfPressureResids(Fluid *pFluid, double p, double R, double rhoc, double Tc)
void disable_TTSE_LUT(void)
Disable the TTSE.
double temperature_prho_VanDerWaals(double p, double rho)
Definition: FluidClass.cpp:710
virtual void density_Ts(double T, double s, double &rhoout, double &pout, double &rhoLout, double &rhoVout, double &psatLout, double &psatVout)
virtual void saturation_T(double T, bool UseLUT, double &psatLout, double &psatVout, double &rhoLout, double &rhoVout)
Definition: FluidClass.cpp:948
double Tmin
Definition: FluidClass.h:54
double drhoLdT_sat
Derivative of density w.r.t. temperature along the saturated liquid curve.
Definition: FluidClass.h:112
bool ValidNumber(double x)
double accentricfactor
Definition: FluidClass.h:43
bool built_TTSE_LUT
Definition: FluidClass.h:584
double Tend
The last temperature for which the conventional methods can be used.
Definition: FluidClass.h:106
double ssatV_anc(double T)
std::string phase_Trho(double T, double rho, double &pL, double &pV, double &rhoL, double &rhoV)
Return the phase given the temperature and the density.
double dpdT_constrho(void)
Definition: CPState.cpp:2118
double TV(void)
Definition: CPState.h:270
double interp1d(std::vector< double > *x, std::vector< double > *y, double x0)