use crate::weight_functions::WeightFunctionInfo;
use feos_core::{EosResult, HelmholtzEnergyDual, StateHD};
use ndarray::prelude::*;
use ndarray::RemoveAxis;
use num_dual::*;
use num_traits::Zero;
use std::fmt::Display;
macro_rules! impl_helmholtz_energy {
($number:ty) => {
impl HelmholtzEnergyDual<$number> for Box<dyn FunctionalContribution> {
fn helmholtz_energy(&self, state: &StateHD<$number>) -> $number {
let weight_functions = self.weight_functions(state.temperature);
let density = weight_functions
.component_index
.mapv(|c| state.partial_density[c]);
let weight_constants = weight_functions.weight_constants(Zero::zero(), 0);
let weighted_densities = weight_constants.dot(&density).insert_axis(Axis(1));
self.calculate_helmholtz_energy_density(
state.temperature,
weighted_densities.view(),
)
.unwrap()[0]
* state.volume
}
}
};
}
impl_helmholtz_energy!(f64);
impl_helmholtz_energy!(Dual64);
impl_helmholtz_energy!(Dual<DualVec64<3>, f64>);
impl_helmholtz_energy!(HyperDual64);
impl_helmholtz_energy!(Dual2_64);
impl_helmholtz_energy!(Dual3_64);
impl_helmholtz_energy!(HyperDual<Dual64, f64>);
impl_helmholtz_energy!(HyperDual<DualVec64<2>, f64>);
impl_helmholtz_energy!(HyperDual<DualVec64<3>, f64>);
impl_helmholtz_energy!(Dual3<Dual64, f64>);
impl_helmholtz_energy!(Dual3<DualVec64<2>, f64>);
impl_helmholtz_energy!(Dual3<DualVec64<3>, f64>);
pub trait FunctionalContributionDual<N: DualNum<f64>>: Display {
fn weight_functions(&self, temperature: N) -> WeightFunctionInfo<N>;
fn weight_functions_pdgt(&self, temperature: N) -> WeightFunctionInfo<N> {
self.weight_functions(temperature)
}
fn calculate_helmholtz_energy_density(
&self,
temperature: N,
weighted_densities: ArrayView2<N>,
) -> EosResult<Array1<N>>;
}
pub trait FunctionalContribution:
FunctionalContributionDual<f64>
+ FunctionalContributionDual<Dual64>
+ FunctionalContributionDual<Dual<DualVec64<3>, f64>>
+ FunctionalContributionDual<HyperDual64>
+ FunctionalContributionDual<Dual2_64>
+ FunctionalContributionDual<Dual3_64>
+ FunctionalContributionDual<HyperDual<Dual64, f64>>
+ FunctionalContributionDual<HyperDual<DualVec64<2>, f64>>
+ FunctionalContributionDual<HyperDual<DualVec64<3>, f64>>
+ FunctionalContributionDual<Dual3<Dual64, f64>>
+ FunctionalContributionDual<Dual3<DualVec64<2>, f64>>
+ FunctionalContributionDual<Dual3<DualVec64<3>, f64>>
+ Display
+ Sync
+ Send
{
fn first_partial_derivatives(
&self,
temperature: f64,
weighted_densities: Array2<f64>,
mut helmholtz_energy_density: ArrayViewMut1<f64>,
mut first_partial_derivative: ArrayViewMut2<f64>,
) -> EosResult<()> {
let mut wd = weighted_densities.mapv(Dual64::from);
let t = Dual64::from(temperature);
let mut phi = Array::zeros(weighted_densities.raw_dim().remove_axis(Axis(0)));
for i in 0..wd.shape()[0] {
wd.index_axis_mut(Axis(0), i)
.map_inplace(|x| x.eps[0] = 1.0);
phi = self.calculate_helmholtz_energy_density(t, wd.view())?;
first_partial_derivative
.index_axis_mut(Axis(0), i)
.assign(&phi.mapv(|p| p.eps[0]));
wd.index_axis_mut(Axis(0), i)
.map_inplace(|x| x.eps[0] = 0.0);
}
helmholtz_energy_density.assign(&phi.mapv(|p| p.re));
Ok(())
}
fn second_partial_derivatives(
&self,
temperature: f64,
weighted_densities: ArrayView2<f64>,
mut helmholtz_energy_density: ArrayViewMut1<f64>,
mut first_partial_derivative: ArrayViewMut2<f64>,
mut second_partial_derivative: ArrayViewMut3<f64>,
) -> EosResult<()> {
let mut wd = weighted_densities.mapv(HyperDual64::from);
let t = HyperDual64::from(temperature);
let mut phi = Array::zeros(weighted_densities.raw_dim().remove_axis(Axis(0)));
for i in 0..wd.shape()[0] {
wd.index_axis_mut(Axis(0), i)
.map_inplace(|x| x.eps1[0] = 1.0);
for j in 0..=i {
wd.index_axis_mut(Axis(0), j)
.map_inplace(|x| x.eps2[0] = 1.0);
phi = self.calculate_helmholtz_energy_density(t, wd.view())?;
let p = phi.mapv(|p| p.eps1eps2[(0, 0)]);
second_partial_derivative
.index_axis_mut(Axis(0), i)
.index_axis_mut(Axis(0), j)
.assign(&p);
if i != j {
second_partial_derivative
.index_axis_mut(Axis(0), j)
.index_axis_mut(Axis(0), i)
.assign(&p);
}
wd.index_axis_mut(Axis(0), j)
.map_inplace(|x| x.eps2[0] = 0.0);
}
first_partial_derivative
.index_axis_mut(Axis(0), i)
.assign(&phi.mapv(|p| p.eps1[0]));
wd.index_axis_mut(Axis(0), i)
.map_inplace(|x| x.eps1[0] = 0.0);
}
helmholtz_energy_density.assign(&phi.mapv(|p| p.re));
Ok(())
}
}
impl<T> FunctionalContribution for T where
T: FunctionalContributionDual<f64>
+ FunctionalContributionDual<Dual64>
+ FunctionalContributionDual<Dual<DualVec64<3>, f64>>
+ FunctionalContributionDual<HyperDual64>
+ FunctionalContributionDual<Dual2_64>
+ FunctionalContributionDual<Dual3_64>
+ FunctionalContributionDual<HyperDual<Dual64, f64>>
+ FunctionalContributionDual<HyperDual<DualVec64<2>, f64>>
+ FunctionalContributionDual<HyperDual<DualVec64<3>, f64>>
+ FunctionalContributionDual<Dual3<Dual64, f64>>
+ FunctionalContributionDual<Dual3<DualVec64<2>, f64>>
+ FunctionalContributionDual<Dual3<DualVec64<3>, f64>>
+ Display
+ Sync
+ Send
{
}