Repository navigation
Closed
Labels
corerelated to features in feos-corerelated to features in feos-coreenhancementNew feature or requestNew feature or request
Description
At the moment, we calculate all properties of a State that use second derivatives via HyperDual64. Second derivatives w.r.t a single property, i.e. non-mixed derivatives, can be calculated using Dual2_64 which are a bit more efficient.
Should we add Dual2_64 to the PartialDerivative enum and to the cache? E.g. we could do
// in state/mod.rs
#[derive(Clone, Copy, Eq, Hash, PartialEq, Debug)]
pub(crate) enum PartialDerivative {
Zeroth,
First(Derivative),
Second(Derivative),
SecondMixed(Derivative, Derivative),
Third(Derivative),
}
/// Creates a [StateHD] taking the first and second (non-mixed) derivatives.
pub fn derive2(&self, derivative: Derivative) -> StateHD<Dual2_64> {
let mut t = Dual2_64::from(self.reduced_temperature);
let mut v = Dual2_64::from(self.reduced_volume);
let mut n = self.reduced_moles.mapv(Dual2_64::from);
match derivative {
Derivative::DT => t = t.derive(),
Derivative::DV => v = v.derive(),
Derivative::DN(i) => n[i] = n[i].derive(),
}
StateHD::new(t, v, n)
}
/// Creates a [StateHD] taking the first and second mixed partial derivatives.
pub fn derive2_mixed(
&self,
derivative1: Derivative,
derivative2: Derivative,
) -> StateHD<HyperDual64> {
let mut t = HyperDual64::from(self.reduced_temperature);
let mut v = HyperDual64::from(self.reduced_volume);
let mut n = self.reduced_moles.mapv(HyperDual64::from);
match derivative1 {
Derivative::DT => t.eps1[0] = 1.0,
Derivative::DV => v.eps1[0] = 1.0,
Derivative::DN(i) => n[i].eps1[0] = 1.0,
}
match derivative2 {
Derivative::DT => t.eps2[0] = 1.0,
Derivative::DV => v.eps2[0] = 1.0,
Derivative::DN(i) => n[i].eps2[0] = 1.0,
}
StateHD::new(t, v, n)
}
// in state/cache.rs
pub fn get_or_insert_with_d2_64<F: FnOnce() -> Dual2_64>(
&mut self,
derivative: Derivative,
f: F,
) -> f64 {
if let Some(&value) = self.map.get(&PartialDerivative::Second(derivative)) {
self.hit += 1;
value
} else {
self.miss += 1;
let value = f();
self.map.insert(PartialDerivative::Zeroth, value.re);
self.map
.insert(PartialDerivative::First(derivative), value.v1[0]);
self.map
.insert(PartialDerivative::Second(derivative), value.v2[0]);
// also fill PartialDerivtave::SecondMixed(derivative, derivative)?
value.v2[0]
}
}
// in state/properties.rs
fn dp_dv_(&self, evaluate: Evaluate) -> QuantityScalar<U> {
-self.get_or_compute_derivative(PartialDerivative::Second(DV), evaluate)
}
fn dp_dt_(&self, evaluate: Evaluate) -> QuantityScalar<U> {
-self.get_or_compute_derivative(PartialDerivative::SecondMixed(DV, DT), evaluate)
}Background: This will speed up algorithms that use those non-mixed, second derivatives, e.g. the density iteration. First implementation shows speedup of ~9 % for density iteration and 6 % for tp_flash.
Metadata
Metadata
Assignees
Labels
corerelated to features in feos-corerelated to features in feos-coreenhancementNew feature or requestNew feature or request
Activity
prehner commentedon Dec 20, 2022
Sounds excellent! Do we need separate
SecondandSecondMixedentries for the cache specifically? Feels like we could just use theSecondMixedvariant for that.g-bauer commentedon Dec 20, 2022
The cache stores derivatives as
HashMap<PartialDerivative, f64>, so theSecondvariant is a possible key. But we could ignore that when filling the map, i.e. we can writeThis just uses
SecondMixedand would lead to a cache hit in case one computes the second derivative using the less efficientHyperDual64.