mod collocated;
mod scratch;
mod session;
mod sweep;
use crate::foundation::{AlgoError, Lattice, Result};
use crate::geostat::nscore::NormalScore;
use crate::gridding::kriging::SpatialVariogram;
use ndarray::Array2;
use collocated::standardize;
use scratch::SgsScratch;
use sweep::{simulate_scores, snap_fixed};
pub use session::SgsSession;
#[derive(Debug, Clone)]
pub struct SgsParams {
pub variogram: SpatialVariogram,
pub max_neighbours: usize,
pub radius: f64,
pub seed: u64,
pub collocated: Option<(Array2<f64>, f64)>,
}
impl SgsParams {
pub fn new(
variogram: impl Into<SpatialVariogram>,
max_neighbours: usize,
radius: f64,
seed: u64,
) -> Result<SgsParams> {
if max_neighbours == 0 || !radius.is_finite() || radius <= 0.0 {
return Err(AlgoError::InvalidArgument(
"SgsParams: need max_neighbours >= 1 and radius > 0".to_string(),
));
}
Ok(SgsParams {
variogram: variogram.into(),
max_neighbours,
radius,
seed,
collocated: None,
})
}
}
pub fn sgs(coords: &[[f64; 3]], lattice: &Lattice, params: &SgsParams) -> Result<Array2<f64>> {
sgs_seeded(coords, lattice, params, params.seed)
}
pub fn sgs_seeded(
coords: &[[f64; 3]],
lattice: &Lattice,
params: &SgsParams,
seed: u64,
) -> Result<Array2<f64>> {
if coords.is_empty() {
return Err(AlgoError::EmptyInput("sgs: no conditioning data"));
}
if params.max_neighbours == 0 || !params.radius.is_finite() || params.radius <= 0.0 {
return Err(AlgoError::InvalidArgument(
"sgs: need max_neighbours >= 1 and radius > 0".to_string(),
));
}
let (ncol, nrow) = (lattice.ncol, lattice.nrow);
if let Some((sec, _)) = ¶ms.collocated {
if sec.dim() != (ncol, nrow) {
return Err(AlgoError::InvalidArgument(
"sgs: collocated secondary shape must match the lattice (ncol, nrow)".to_string(),
));
}
}
let secondary = params
.collocated
.as_ref()
.map(|(f, rho)| (standardize(f), *rho));
let zvals: Vec<f64> = coords.iter().map(|c| c[2]).collect();
let ns = NormalScore::fit(&zvals)?;
let fixed = snap_fixed(coords, lattice, &ns);
let mut scratch = SgsScratch::default();
let sim = simulate_scores(
lattice,
¶ms.variogram,
params.max_neighbours,
params.radius,
secondary.as_ref(),
seed,
&fixed,
&mut scratch,
);
Ok(sim.mapv(|s| ns.back(s)))
}
pub fn sgs_unconditional<V>(
lattice: &Lattice,
mean: f64,
variance: f64,
variogram: &V,
max_neighbours: usize,
radius: f64,
seed: u64,
) -> Result<Array2<f64>>
where
for<'a> SpatialVariogram: From<&'a V>,
{
if max_neighbours == 0 || !radius.is_finite() || radius <= 0.0 {
return Err(AlgoError::InvalidArgument(
"sgs_unconditional: need max_neighbours >= 1 and radius > 0".to_string(),
));
}
if !mean.is_finite() || !variance.is_finite() || variance < 0.0 {
return Err(AlgoError::InvalidArgument(
"sgs_unconditional: need finite mean and variance >= 0".to_string(),
));
}
if variance == 0.0 {
return Ok(Array2::from_elem((lattice.ncol, lattice.nrow), mean));
}
let variogram = SpatialVariogram::from(variogram);
let mut scratch = SgsScratch::default();
let sim = simulate_scores(
lattice,
&variogram,
max_neighbours,
radius,
None,
seed,
&[],
&mut scratch,
);
let sd = variance.sqrt();
Ok(sim.mapv(|s| mean + sd * s))
}
#[cfg(test)]
mod tests;