@@ -17,6 +17,7 @@ use num_dual::DualNum;
1717use quantity:: si:: { SIArray1 , SIUnit } ;
1818use serde:: { Deserialize , Serialize } ;
1919use std:: f64:: consts:: SQRT_2 ;
20+ use std:: fmt;
2021use std:: rc:: Rc ;
2122
2223const KB_A3 : f64 = 13806490.0 ;
@@ -165,12 +166,51 @@ impl Parameter for PengRobinsonParameters {
165166 }
166167}
167168
169+ struct PengRobinsonContribution {
170+ parameters : Rc < PengRobinsonParameters > ,
171+ }
172+
173+ impl < D : DualNum < f64 > > HelmholtzEnergyDual < D > for PengRobinsonContribution {
174+ fn helmholtz_energy ( & self , state : & StateHD < D > ) -> D {
175+ // temperature dependent a parameter
176+ let p = & self . parameters ;
177+ let x = & state. molefracs ;
178+ let ak = ( & p. tc . mapv ( |tc| ( D :: one ( ) - ( state. temperature / tc) . sqrt ( ) ) ) * & p. kappa + 1.0 )
179+ . mapv ( |x| x. powi ( 2 ) )
180+ * & p. a ;
181+
182+ // Mixing rules
183+ let mut ak_mix = D :: zero ( ) ;
184+ for i in 0 ..ak. len ( ) {
185+ for j in 0 ..ak. len ( ) {
186+ ak_mix += ( ak[ i] * ak[ j] ) . sqrt ( ) * ( x[ i] * x[ j] * ( 1.0 - p. k_ij [ ( i, j) ] ) ) ;
187+ }
188+ }
189+ let b = ( x * & p. b ) . sum ( ) ;
190+
191+ // Helmholtz energy
192+ let n = state. moles . sum ( ) ;
193+ let v = state. volume ;
194+ n * ( ( v / ( v - b * n) ) . ln ( )
195+ - ak_mix / ( b * SQRT_2 * 2.0 * state. temperature )
196+ * ( ( v * ( SQRT_2 - 1.0 ) + b * n) / ( v * ( SQRT_2 + 1.0 ) - b * n) ) . ln ( ) )
197+ }
198+ }
199+
200+ impl fmt:: Display for PengRobinsonContribution {
201+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
202+ write ! ( f, "Peng Robinson" )
203+ }
204+ }
205+
168206/// A simple version of the Peng-Robinson equation of state.
169207pub struct PengRobinson {
170208 /// Parameters
171209 parameters : Rc < PengRobinsonParameters > ,
172210 /// Ideal gas contributions to the Helmholtz energy
173211 ideal_gas : Joback ,
212+ /// Non-ideal contributions to the Helmholtz energy
213+ contributions : Vec < Box < dyn HelmholtzEnergy > > ,
174214}
175215
176216impl PengRobinson {
@@ -180,9 +220,14 @@ impl PengRobinson {
180220 || Joback :: default ( parameters. tc . len ( ) ) ,
181221 |j| Joback :: new ( j. clone ( ) ) ,
182222 ) ;
223+ let contributions: Vec < Box < dyn HelmholtzEnergy > > =
224+ vec ! [ Box :: new( PengRobinsonContribution {
225+ parameters: parameters. clone( ) ,
226+ } ) ] ;
183227 Self {
184228 parameters,
185229 ideal_gas,
230+ contributions,
186231 }
187232 }
188233}
@@ -202,42 +247,7 @@ impl EquationOfState for PengRobinson {
202247 }
203248
204249 fn residual ( & self ) -> & [ Box < dyn HelmholtzEnergy > ] {
205- unreachable ! ( )
206- }
207-
208- fn evaluate_residual < D : DualNum < f64 > > ( & self , state : & StateHD < D > ) -> D {
209- // temperature dependent a parameter
210- let p = & self . parameters ;
211- let x = & state. molefracs ;
212- let ak = ( & p. tc . mapv ( |tc| ( D :: one ( ) - ( state. temperature / tc) . sqrt ( ) ) ) * & p. kappa + 1.0 )
213- . mapv ( |x| x. powi ( 2 ) )
214- * & p. a ;
215-
216- // Mixing rules
217- let mut ak_mix = D :: zero ( ) ;
218- for i in 0 ..ak. len ( ) {
219- for j in 0 ..ak. len ( ) {
220- ak_mix += ( ak[ i] * ak[ j] ) . sqrt ( ) * ( x[ i] * x[ j] * ( 1.0 - p. k_ij [ ( i, j) ] ) ) ;
221- }
222- }
223- let b = ( x * & p. b ) . sum ( ) ;
224-
225- // Helmholtz energy
226- let n = state. moles . sum ( ) ;
227- let v = state. volume ;
228- n * ( ( v / ( v - b * n) ) . ln ( )
229- - ak_mix / ( b * SQRT_2 * 2.0 * state. temperature )
230- * ( ( v * ( SQRT_2 - 1.0 ) + b * n) / ( v * ( SQRT_2 + 1.0 ) - b * n) ) . ln ( ) )
231- }
232-
233- fn evaluate_residual_contributions < D : DualNum < f64 > > (
234- & self ,
235- state : & StateHD < D > ,
236- ) -> Vec < ( String , D ) >
237- where
238- dyn HelmholtzEnergy : HelmholtzEnergyDual < D > ,
239- {
240- vec ! [ ( "Peng-Robinson" . into( ) , self . evaluate_residual( state) ) ]
250+ & self . contributions
241251 }
242252
243253 fn ideal_gas ( & self ) -> & dyn IdealGasContribution {
0 commit comments