#![warn(missing_docs)]
pub mod kde;
pub mod magic;
pub mod sampling;
pub mod windows;
pub use kde::{Bandwidth, KdeSampler};
pub use magic::{magic, magic_with, MagicOutput, MagicParams, MagicSelection};
pub use sampling::{AliasTable, MeshSourceSampler, Mode, SampledVoxel};
pub use windows::{
emit_openmc_weight_windows, emit_serpent_wwin, OpenMcOptions, OpenMcWeightWindows, SerpentWwin,
};
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum Error {
EmptyTally,
LengthMismatch {
expected: usize,
got: usize,
},
ZeroMaxFlux {
energy_group: usize,
},
EmptyPdf,
NegativePdf {
index: usize,
value: f64,
},
NonFinitePdf {
index: usize,
},
ZeroSumPdf,
NegativeTally {
index: usize,
value: f64,
},
NonFiniteTally {
field: &'static str,
index: usize,
},
ZeroVarianceDim {
dim: usize,
},
BadDraw {
value: f64,
},
NegativeWindow {
index: usize,
value: f64,
},
NonFiniteWindow {
index: usize,
},
BadEnergyBounds {
index: usize,
},
BadMeshBounds {
axis: usize,
index: usize,
},
BadEmissionOption {
option: &'static str,
value: String,
detail: &'static str,
},
Wwinp(String),
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::EmptyTally => write!(f, "tally contains no volume elements"),
Error::LengthMismatch { expected, got } => {
write!(f, "length mismatch: expected {expected}, got {got}")
}
Error::ZeroMaxFlux { energy_group } => write!(
f,
"energy group {energy_group} has no positive flux; \
weight-window normalization would divide by zero"
),
Error::EmptyPdf => write!(f, "pdf must contain at least one value"),
Error::NegativePdf { index, value } => {
write!(f, "pdf[{index}] = {value} is negative")
}
Error::NonFinitePdf { index } => write!(f, "pdf[{index}] is not finite"),
Error::ZeroSumPdf => write!(f, "pdf sums to zero; cannot normalize"),
Error::NegativeTally { index, value } => write!(
f,
"tally/density value at index {index} = {value} is negative"
),
Error::NonFiniteTally { field, index } => {
write!(f, "{field}[{index}] is not finite")
}
Error::ZeroVarianceDim { dim } => {
write!(
f,
"kde dimension {dim} has zero variance; use a fixed bandwidth"
)
}
Error::BadDraw { value } => {
write!(f, "kde draw uniform {value} is outside [0, 1)")
}
Error::NegativeWindow { index, value } => {
write!(
f,
"weight-window lower bound at index {index} = {value} is negative"
)
}
Error::NonFiniteWindow { index } => {
write!(
f,
"weight-window lower bound at index {index} is not finite"
)
}
Error::BadEnergyBounds { index } => {
write!(
f,
"energy upper bound at index {index} is not positive and increasing"
)
}
Error::BadMeshBounds { axis, index } => {
write!(
f,
"mesh bound at axis {axis} index {index} is not finite and increasing"
)
}
Error::BadEmissionOption {
option,
value,
detail,
} => write!(f, "emission option {option} = {value}: {detail}"),
Error::Wwinp(m) => write!(f, "wwinp writer error: {m}"),
}
}
}
impl std::error::Error for Error {}
#[cfg(test)]
mod tests {
use super::Error;
#[test]
fn error_display_covers_all_variants() {
let cases: Vec<(Error, &str)> = vec![
(Error::EmptyTally, "no volume elements"),
(
Error::LengthMismatch {
expected: 3,
got: 5,
},
"expected 3, got 5",
),
(Error::ZeroMaxFlux { energy_group: 2 }, "energy group 2"),
(Error::EmptyPdf, "at least one value"),
(
Error::NegativePdf {
index: 1,
value: -0.5,
},
"pdf[1]",
),
(Error::NonFinitePdf { index: 0 }, "pdf[0]"),
(Error::ZeroSumPdf, "sums to zero"),
(
Error::NegativeTally {
index: 4,
value: -1.0,
},
"index 4",
),
(
Error::NonFiniteTally {
field: "flux",
index: 7,
},
"flux[7]",
),
(Error::ZeroVarianceDim { dim: 1 }, "dimension 1"),
(Error::BadDraw { value: 1.5 }, "outside [0, 1)"),
(
Error::NegativeWindow {
index: 2,
value: -0.1,
},
"index 2",
),
(Error::NonFiniteWindow { index: 3 }, "index 3"),
(Error::BadEnergyBounds { index: 1 }, "index 1"),
(Error::BadMeshBounds { axis: 2, index: 4 }, "axis 2"),
(
Error::BadEmissionOption {
option: "survival_ratio",
value: "1".to_string(),
detail: "must be greater than 1",
},
"survival_ratio",
),
(Error::Wwinp("disk".into()), "wwinp writer error"),
];
for (err, needle) in cases {
assert!(
format!("{err}").contains(needle),
"display of {err:?} should contain {needle:?}"
);
let _: &dyn std::error::Error = &err;
}
}
}