///|
/// Common typed failures; numerical inputs never silently clamp invalid cells.
pub(all) enum Problem {
Invalid(String)
Budget(String)
} derive(Eq, Debug)
///|
/// Reciprocal metric in inverse square angstroms (no 2*pi factor).
pub struct Metric {
aa : Double
bb : Double
cc : Double
ab : Double
ac : Double
bc : Double
a : Double
b : Double
c : Double
} derive(Debug)
///|
fn finite(x : Double) -> Bool {
x == x && x.abs() < 1.0e300
}
///|
/// Positive lengths in angstroms; angles in degrees. Reject ill-conditioned cells.
pub fn reciprocal(
a : Double,
b : Double,
c : Double,
alpha : Double,
beta : Double,
gamma : Double,
) -> Result[Metric, Problem] {
for x in [a, b, c] {
if !finite(x) || x < 1.0e-6 || x > 1.0e6 {
return Err(Invalid("length outside [1e-6,1e6] angstrom"))
}
}
for x in [alpha, beta, gamma] {
if !finite(x) || x <= 0.0 || x >= 180.0 {
return Err(Invalid("angle outside (0,180) degrees"))
}
}
let ca = @math.cos(alpha * @math.PI / 180.0)
let cb = @math.cos(beta * @math.PI / 180.0)
let cg = @math.cos(gamma * @math.PI / 180.0)
let det = 1.0 + 2.0 * ca * cb * cg - ca * ca - cb * cb - cg * cg
if det <= 1.0e-10 {
return Err(Invalid("singular or ill-conditioned cell"))
}
Ok({
aa: (1.0 - ca * ca) / (a * a * det),
bb: (1.0 - cb * cb) / (b * b * det),
cc: (1.0 - cg * cg) / (c * c * det),
ab: (cb * ca - cg) / (a * b * det),
ac: (cg * ca - cb) / (a * c * det),
bc: (cg * cb - ca) / (b * c * det),
a,
b,
c,
})
}