use crate::Real;
use crate::error::KrigingError;
use crate::variogram::models::VariogramModel;
#[derive(Debug, Clone)]
pub struct NestedVariogram {
components: Vec<VariogramModel>,
}
impl NestedVariogram {
pub fn new(components: Vec<VariogramModel>) -> Result<Self, KrigingError> {
if components.is_empty() {
return Err(KrigingError::InvalidInput(
"nested variogram requires at least one component".to_string(),
));
}
Ok(Self { components })
}
pub fn components(&self) -> &[VariogramModel] {
&self.components
}
pub fn semivariance(&self, distance: Real) -> Real {
self.components
.iter()
.map(|m| m.semivariance(distance))
.sum()
}
pub fn covariance(&self, distance: Real) -> Real {
self.components.iter().map(|m| m.covariance(distance)).sum()
}
pub fn total_sill(&self) -> Real {
self.components
.iter()
.map(|m| m.covariance(0.0) + m.semivariance(0.0))
.sum()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::variogram::models::VariogramType;
#[test]
fn new_rejects_empty_components() {
assert!(NestedVariogram::new(vec![]).is_err());
}
#[test]
fn semivariance_is_sum_of_components() {
let a = VariogramModel::new(0.1, 1.0, 5.0, VariogramType::Exponential).unwrap();
let b = VariogramModel::new(0.0, 2.0, 30.0, VariogramType::Gaussian).unwrap();
let nested = NestedVariogram::new(vec![a, b]).unwrap();
let h = 7.0;
let sum = a.semivariance(h) + b.semivariance(h);
assert!((nested.semivariance(h) - sum).abs() < 1e-5);
}
#[test]
fn covariance_complements_semivariance() {
let a = VariogramModel::new(0.1, 1.0, 5.0, VariogramType::Exponential).unwrap();
let b = VariogramModel::new(0.0, 2.0, 30.0, VariogramType::Gaussian).unwrap();
let nested = NestedVariogram::new(vec![a, b]).unwrap();
let h = 4.0;
let total = nested.total_sill();
assert!((nested.covariance(h) + nested.semivariance(h) - total).abs() < 1e-4);
}
}