@@ -4,7 +4,7 @@ use crate::{FeosResult, PhaseEquilibrium, ReferenceSystem, Residual};
44use nalgebra:: { Const , SVector , U1 , U2 } ;
55#[ cfg( feature = "rayon" ) ]
66use ndarray:: { Array1 , Array2 , ArrayView2 , Zip } ;
7- use num_dual:: { Derivative , DualSVec , DualStruct } ;
7+ use num_dual:: { Derivative , DualNum , DualSVec , DualStruct , first_derivative , partial2 } ;
88use quantity:: { Density , Pressure , Temperature } ;
99#[ cfg( feature = "rayon" ) ]
1010use quantity:: { KELVIN , KILO , METER , MOL , PASCAL } ;
@@ -66,6 +66,50 @@ pub trait PropertiesAD {
6666 Ok ( Pressure :: from_reduced ( p) )
6767 }
6868
69+ fn boiling_temperature < const P : usize > (
70+ & self ,
71+ pressure : Pressure ,
72+ ) -> FeosResult < Temperature < Gradient < P > > >
73+ where
74+ Self : Residual < U1 , Gradient < P > > ,
75+ {
76+ let eos_f64 = self . re ( ) ;
77+ let ( temperature, [ vapor_density, liquid_density] ) =
78+ PhaseEquilibrium :: pure_p ( & eos_f64, pressure, None , Default :: default ( ) ) ?;
79+
80+ // implicit differentiation is implemented here instead of just calling pure_t with dual
81+ // numbers, because for the first derivative, we can avoid calculating density derivatives.
82+ let t = temperature. into_reduced ( ) ;
83+ let v1 = 1.0 / liquid_density. to_reduced ( ) ;
84+ let v2 = 1.0 / vapor_density. to_reduced ( ) ;
85+ let p = pressure. into_reduced ( ) ;
86+ let t = Gradient :: from ( t) ;
87+ let t = t + {
88+ let v1 = Gradient :: from ( v1) ;
89+ let v2 = Gradient :: from ( v2) ;
90+ let p = Gradient :: from ( p) ;
91+ let x = Self :: pure_molefracs ( ) ;
92+
93+ let residual_entropy = |v| {
94+ let ( a, s) = first_derivative (
95+ partial2 (
96+ |t, & v, x| self . lift ( ) . residual_molar_helmholtz_energy ( t, v, x) ,
97+ & v,
98+ & x,
99+ ) ,
100+ t,
101+ ) ;
102+ ( a, -s)
103+ } ;
104+ let ( a1, s1) = residual_entropy ( v1) ;
105+ let ( a2, s2) = residual_entropy ( v2) ;
106+
107+ let ln_rho = ( v1 / v2) . ln ( ) ;
108+ ( p * ( v2 - v1) + ( a2 - a1 + t * ln_rho) ) / ( s2 - s1 - ln_rho)
109+ } ;
110+ Ok ( Temperature :: from_reduced ( t) )
111+ }
112+
69113 fn equilibrium_liquid_density < const P : usize > (
70114 & self ,
71115 temperature : Temperature ,
@@ -111,6 +155,26 @@ pub trait PropertiesAD {
111155 )
112156 }
113157
158+ #[ cfg( feature = "rayon" ) ]
159+ fn boiling_temperature_parallel < const P : usize > (
160+ parameter_names : [ String ; P ] ,
161+ parameters : ArrayView2 < f64 > ,
162+ input : ArrayView2 < f64 > ,
163+ ) -> ( Array1 < f64 > , Array2 < f64 > , Array1 < bool > )
164+ where
165+ Self : ParametersAD < 1 > ,
166+ {
167+ parallelize :: < _ , Self , _ , _ > (
168+ parameter_names,
169+ parameters,
170+ input,
171+ |eos : & Self :: Lifted < Gradient < P > > , inp| {
172+ eos. boiling_temperature ( inp[ 0 ] * PASCAL )
173+ . map ( |p| p. convert_into ( KELVIN ) )
174+ } ,
175+ )
176+ }
177+
114178 #[ cfg( feature = "rayon" ) ]
115179 fn liquid_density_parallel < const P : usize > (
116180 parameter_names : [ String ; P ] ,
0 commit comments