1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
use super::Components;
use crate::StateHD;
use ndarray::Array1;
use num_dual::DualNum;
use num_dual::*;
use std::fmt;

/// Ideal gas Helmholtz energy contribution.
pub trait IdealGas: Components + Sync + Send {
    // Return a reference to the implementation of the de Broglie wavelength.
    fn ideal_gas_model(&self) -> &dyn DeBroglieWavelength;

    /// Evaluate the ideal gas Helmholtz energy contribution for a given state.
    ///
    /// In some cases it could be advantageous to overwrite this
    /// implementation instead of implementing the de Broglie
    /// wavelength.
    fn evaluate_ideal_gas<D: DualNum<f64> + Copy>(&self, state: &StateHD<D>) -> D
    where
        for<'a> dyn DeBroglieWavelength + 'a: DeBroglieWavelengthDual<D>,
    {
        let ln_lambda3 = self.ideal_gas_model().ln_lambda3(state.temperature);
        ((ln_lambda3
            + state.partial_density.mapv(|x| {
                if x.re() == 0.0 {
                    D::from(0.0)
                } else {
                    x.ln() - 1.0
                }
            }))
            * &state.moles)
            .sum()
    }
}

/// Implementation of an ideal gas model in terms of the
/// logarithm of the cubic thermal de Broglie wavelength
/// in units ln(A³).
///
/// This trait needs to be implemented generically or for
/// the specific types in the supertraits of [DeBroglieWavelength]
/// so that the implementor can be used as an ideal gas
/// contribution in the equation of state.
pub trait DeBroglieWavelengthDual<D: DualNum<f64>> {
    fn ln_lambda3(&self, temperature: D) -> Array1<D>;
}

/// Object safe version of the [DeBroglieWavelengthDual] trait.
///
/// The trait is implemented automatically for every struct that implements
/// the supertraits.
pub trait DeBroglieWavelength:
    DeBroglieWavelengthDual<f64>
    + DeBroglieWavelengthDual<Dual64>
    + DeBroglieWavelengthDual<Dual<DualSVec64<3>, f64>>
    + DeBroglieWavelengthDual<HyperDual64>
    + DeBroglieWavelengthDual<Dual2_64>
    + DeBroglieWavelengthDual<Dual3_64>
    + DeBroglieWavelengthDual<HyperDual<Dual64, f64>>
    + DeBroglieWavelengthDual<HyperDual<DualSVec64<2>, f64>>
    + DeBroglieWavelengthDual<HyperDual<DualSVec64<3>, f64>>
    + DeBroglieWavelengthDual<Dual2<Dual64, f64>>
    + DeBroglieWavelengthDual<Dual3<Dual64, f64>>
    + DeBroglieWavelengthDual<Dual3<DualSVec64<2>, f64>>
    + DeBroglieWavelengthDual<Dual3<DualSVec64<3>, f64>>
    + fmt::Display
    + Send
    + Sync
{
}

impl<T> DeBroglieWavelength for T where
    T: DeBroglieWavelengthDual<f64>
        + DeBroglieWavelengthDual<Dual64>
        + DeBroglieWavelengthDual<Dual<DualSVec64<3>, f64>>
        + DeBroglieWavelengthDual<HyperDual64>
        + DeBroglieWavelengthDual<Dual2_64>
        + DeBroglieWavelengthDual<Dual3_64>
        + DeBroglieWavelengthDual<HyperDual<Dual64, f64>>
        + DeBroglieWavelengthDual<HyperDual<DualSVec64<2>, f64>>
        + DeBroglieWavelengthDual<HyperDual<DualSVec64<3>, f64>>
        + DeBroglieWavelengthDual<Dual2<Dual64, f64>>
        + DeBroglieWavelengthDual<Dual3<Dual64, f64>>
        + DeBroglieWavelengthDual<Dual3<DualSVec64<2>, f64>>
        + DeBroglieWavelengthDual<Dual3<DualSVec64<3>, f64>>
        + fmt::Display
        + Send
        + Sync
{
}