@@ -3,14 +3,13 @@ use crate::functional::{HelmholtzEnergyFunctional, DFT};
33use crate :: geometry:: Grid ;
44use crate :: solver:: { DFTSolver , DFTSolverLog } ;
55use crate :: weight_functions:: WeightFunctionInfo ;
6- use feos_core:: { Contributions , EosError , EosResult , EosUnit , EquationOfState , State } ;
6+ use feos_core:: { Contributions , EosError , EosResult , EosUnit , EquationOfState , State , Verbosity } ;
77use ndarray:: {
88 Array , Array1 , ArrayBase , Axis as Axis_nd , Data , Dimension , Ix1 , Ix2 , Ix3 , RemoveAxis ,
99} ;
1010use num_dual:: Dual64 ;
11- use quantity:: si:: { SIArray , SIArray1 , SINumber , SIUnit } ;
11+ use quantity:: si:: { SIArray , SIArray1 , SIArray2 , SINumber , SIUnit } ;
1212use quantity:: Quantity ;
13- // use quantity::{Quantity, QuantityArray, SIArray1, SINumber};
1413use std:: ops:: MulAssign ;
1514use std:: sync:: Arc ;
1615
@@ -531,4 +530,156 @@ where
531530 ) ? * SIUnit :: reference_pressure ( ) ;
532531 Ok ( self . integrate ( & internal_energy_density) )
533532 }
533+
534+ pub fn drho_dmu ( & self ) -> EosResult < SIArray < <D :: Larger as Dimension >:: Larger > > {
535+ let rho = self . density . to_reduced ( SIUnit :: reference_density ( ) ) ?;
536+ let second_partial_derivatives = self . second_partial_derivatives ( & rho) ?;
537+
538+ let shape = rho. shape ( ) ;
539+ let shape: Vec < _ > = std:: iter:: once ( & shape[ 0 ] ) . chain ( shape) . copied ( ) . collect ( ) ;
540+ let mut drho_dmu = Array :: zeros ( shape) . into_dimensionality ( ) . unwrap ( ) ;
541+ let rhs = |x : & _ | {
542+ let delta_functional_derivative =
543+ self . delta_functional_derivative ( x, & second_partial_derivatives) ;
544+ let mut xm = x. clone ( ) ;
545+ xm. outer_iter_mut ( )
546+ . zip ( self . dft . m ( ) . iter ( ) )
547+ . for_each ( |( mut x, & m) | x *= m) ;
548+ // let delta_i = self.delta_bond_integrals(&exp_dfdrho, &delta_functional_derivative);
549+ // x + (delta_functional_derivative - delta_i) * rho
550+ xm + delta_functional_derivative * & rho
551+ } ;
552+ let mut log = DFTSolverLog :: new ( Verbosity :: None ) ;
553+ for ( k, mut d) in drho_dmu. outer_iter_mut ( ) . enumerate ( ) {
554+ let mut lhs = rho. clone ( ) ;
555+ for ( i, mut l) in lhs. outer_iter_mut ( ) . enumerate ( ) {
556+ if i != k {
557+ l. fill ( 0.0 ) ;
558+ }
559+ }
560+ d. assign ( & Self :: gmres ( rhs, & lhs, 200 , 1e-13 , & mut log) ?) ;
561+ }
562+ Ok ( drho_dmu
563+ * ( SIUnit :: reference_density ( ) / SIUnit :: reference_molar_entropy ( ) / self . temperature ) )
564+ }
565+
566+ pub fn dn_dmu ( & self ) -> EosResult < SIArray2 > {
567+ let drho_dmu = self . drho_dmu ( ) ?;
568+ let n = drho_dmu. shape ( ) [ 0 ] ;
569+ let dn_dmu = SIArray2 :: from_shape_fn ( [ n; 2 ] , |( i, j) | {
570+ self . integrate ( & drho_dmu. index_axis ( Axis_nd ( 0 ) , i) . index_axis ( Axis_nd ( 0 ) , j) )
571+ } ) ;
572+ Ok ( dn_dmu)
573+ }
574+
575+ pub fn drho_dp ( & self ) -> EosResult < SIArray < D :: Larger > > {
576+ let rho = self . density . to_reduced ( SIUnit :: reference_density ( ) ) ?;
577+ let partial_density = self
578+ . bulk
579+ . partial_density
580+ . to_reduced ( SIUnit :: reference_density ( ) ) ?;
581+ let rho_bulk = self . dft . component_index ( ) . mapv ( |i| partial_density[ i] ) ;
582+
583+ let second_partial_derivatives = self . second_partial_derivatives ( & rho) ?;
584+ let ( _, _, _, exp_dfdrho, _) = self . euler_lagrange_equation ( & rho, & rho_bulk, false ) ?;
585+
586+ let rhs = |x : & _ | {
587+ let delta_functional_derivative =
588+ self . delta_functional_derivative ( x, & second_partial_derivatives) ;
589+ let mut xm = x. clone ( ) ;
590+ xm. outer_iter_mut ( )
591+ . zip ( self . dft . m ( ) . iter ( ) )
592+ . for_each ( |( mut x, & m) | x *= m) ;
593+ let delta_i = self . delta_bond_integrals ( & exp_dfdrho, & delta_functional_derivative) ;
594+ xm + ( delta_functional_derivative - delta_i) * & rho
595+ } ;
596+ let mut lhs = rho. clone ( ) ;
597+ let v = self
598+ . bulk
599+ . partial_molar_volume ( Contributions :: Total )
600+ . to_reduced ( SIUnit :: reference_volume ( ) / SIUnit :: reference_moles ( ) ) ?;
601+ for ( mut l, & c) in lhs. outer_iter_mut ( ) . zip ( self . dft . component_index ( ) . iter ( ) ) {
602+ l *= v[ c] ;
603+ }
604+ let mut log = DFTSolverLog :: new ( Verbosity :: None ) ;
605+ Self :: gmres ( rhs, & lhs, 200 , 1e-13 , & mut log)
606+ . map ( |x| x / ( SIUnit :: reference_molar_entropy ( ) * self . temperature ) )
607+ }
608+
609+ pub fn dn_dp ( & self ) -> EosResult < SIArray1 > {
610+ let dn_dp = self . integrate_comp ( & self . drho_dp ( ) ?) ;
611+ let mut dn_dp_comp = Array1 :: zeros ( self . dft . components ( ) ) * SIUnit :: reference_moles ( )
612+ / SIUnit :: reference_pressure ( ) ;
613+ for ( i, & j) in self . dft . component_index ( ) . iter ( ) . enumerate ( ) {
614+ dn_dp_comp. try_set ( j, dn_dp. get ( i) ) . unwrap ( ) ;
615+ }
616+ Ok ( dn_dp_comp)
617+ }
618+
619+ pub fn drho_dt ( & self ) -> EosResult < SIArray < D :: Larger > > {
620+ let rho = self . density . to_reduced ( SIUnit :: reference_density ( ) ) ?;
621+ let t = self
622+ . temperature
623+ . to_reduced ( SIUnit :: reference_temperature ( ) ) ?;
624+
625+ let second_partial_derivatives = self . second_partial_derivatives ( & rho) ?;
626+
627+ // define rhs
628+ let rhs = |x : & _ | {
629+ let delta_functional_derivative =
630+ self . delta_functional_derivative ( x, & second_partial_derivatives) ;
631+ let mut xm = x. clone ( ) ;
632+ xm. outer_iter_mut ( )
633+ . zip ( self . dft . m ( ) . iter ( ) )
634+ . for_each ( |( mut x, & m) | x *= m) ;
635+ // let delta_i = self.delta_bond_integrals(&exp_dfdrho, &delta_functional_derivative);
636+ // (xm + (delta_functional_derivative - delta_i) * &rho) * t
637+ ( xm + delta_functional_derivative * & rho) * t
638+ } ;
639+
640+ // calculate temperature derivative of functional derivative
641+ let functional_contributions = self . dft . contributions ( ) ;
642+ let weight_functions: Vec < WeightFunctionInfo < Dual64 > > = functional_contributions
643+ . iter ( )
644+ . map ( |c| c. weight_functions ( Dual64 :: from ( t) . derive ( ) ) )
645+ . collect ( ) ;
646+ let convolver: Arc < dyn Convolver < _ , D > > =
647+ ConvolverFFT :: plan ( & self . grid , & weight_functions, None ) ;
648+ let ( _, dfdrhodt) = self . dft . functional_derivative_dual ( t, & rho, & convolver) ?;
649+
650+ // calculate temperature derivative of bulk functional derivative
651+ let partial_density = self
652+ . bulk
653+ . partial_density
654+ . to_reduced ( SIUnit :: reference_density ( ) ) ?;
655+ let rho_bulk = self . dft . component_index ( ) . mapv ( |i| partial_density[ i] ) ;
656+ let bulk_convolver = BulkConvolver :: new ( weight_functions) ;
657+ let ( _, dfdrhodt_bulk) =
658+ self . dft
659+ . functional_derivative_dual ( t, & rho_bulk, & bulk_convolver) ?;
660+
661+ // solve for drho_dt
662+ let x = ( self . bulk . dp_dni ( Contributions :: Total ) * self . bulk . dp_dt ( Contributions :: Total )
663+ / self . bulk . dp_dv ( Contributions :: Total ) )
664+ . to_reduced ( SIUnit :: reference_molar_entropy ( ) ) ?;
665+ let mut lhs = dfdrhodt. mapv ( |d| d. eps [ 0 ] ) ;
666+ lhs. outer_iter_mut ( )
667+ . zip ( dfdrhodt_bulk. into_iter ( ) )
668+ . zip ( x. into_iter ( ) )
669+ . for_each ( |( ( mut lhs, d) , x) | lhs -= d. eps [ 0 ] + x) ;
670+ lhs. outer_iter_mut ( )
671+ . zip ( rho. outer_iter ( ) )
672+ . zip ( rho_bulk. into_iter ( ) )
673+ . zip ( self . dft . m ( ) . iter ( ) )
674+ . for_each ( |( ( ( mut lhs, rho) , rho_b) , & m) | lhs += & ( ( & rho / rho_b) . mapv ( f64:: ln) * m) ) ;
675+
676+ lhs *= & ( -& rho) ;
677+ let mut log = DFTSolverLog :: new ( Verbosity :: None ) ;
678+ let drho_dt = Self :: gmres ( rhs, & lhs, 200 , 1e-13 , & mut log) ?;
679+ Ok ( drho_dt * ( SIUnit :: reference_density ( ) / SIUnit :: reference_temperature ( ) ) )
680+ }
681+
682+ pub fn dn_dt ( & self ) -> EosResult < SIArray1 > {
683+ Ok ( self . integrate_comp ( & self . drho_dt ( ) ?) )
684+ }
534685}
0 commit comments