use bio_files::DensityMap;
use lin_alg::f64::Vec3;
pub const DENSITY_CELL_MARGIN: f64 = 3.0;
pub const DENSITY_MAX_DIST: f64 = 4.;
#[derive(Clone, Copy, PartialEq, Debug, Default)]
pub enum MapStatus {
Observed,
FreeSet,
SystematicallyAbsent,
OutsideHighResLimit,
HigherThanResCutoff,
LowerThanResCutoff,
#[default]
UnreliableMeasurement,
}
impl MapStatus {
pub fn from_str(val: &str) -> Option<MapStatus> {
match val.to_lowercase().as_ref() {
"o" => Some(MapStatus::Observed),
"f" => Some(MapStatus::FreeSet),
"-" => Some(MapStatus::SystematicallyAbsent),
"<" => Some(MapStatus::OutsideHighResLimit),
"h" => Some(MapStatus::HigherThanResCutoff),
"l" => Some(MapStatus::LowerThanResCutoff),
"x" => Some(MapStatus::UnreliableMeasurement),
_ => {
eprintln!("Fallthrough on map type: {val}");
None
}
}
}
}
#[allow(unused)]
#[derive(Clone, Default, Debug)]
pub struct Reflection {
pub h: i32,
pub k: i32,
pub l: i32,
pub status: MapStatus,
pub amp: f64,
pub amp_uncertainty: f64,
pub amp_weighted: Option<f64>,
pub phase_weighted: Option<f64>,
pub phase_figure_of_merit: Option<f64>,
pub delta_amp_weighted: Option<f64>,
pub delta_phase_weighted: Option<f64>,
pub delta_figure_of_merit: Option<f64>,
}
#[derive(Clone, Debug, Default)]
pub struct ReflectionsData {
pub space_group: String,
pub cell_len_a: f32,
pub cell_len_b: f32,
pub cell_len_c: f32,
pub cell_angle_alpha: f32,
pub cell_angle_beta: f32,
pub cell_angle_gamma: f32,
pub points: Vec<Reflection>,
}
#[derive(Clone, Debug)]
pub struct DensityPt {
pub coords: Vec3,
pub density: f64,
}
#[derive(Clone, Debug)]
pub struct DensityRect {
pub origin_cart: Vec3,
pub step: [f64; 3],
pub dims: [usize; 3],
pub data: Vec<f32>,
}
impl DensityRect {
pub fn new(atom_posits: &[Vec3], map: &DensityMap, margin: f64) -> Self {
let hdr = &map.hdr;
let inner = &hdr.inner;
let cell = &inner.cell;
let mut min_r = Vec3::new(f64::INFINITY, f64::INFINITY, f64::INFINITY);
let mut max_r = Vec3::new(f64::NEG_INFINITY, f64::NEG_INFINITY, f64::NEG_INFINITY);
for p in atom_posits {
let mut f = cell.cartesian_to_fractional(*p);
f -= map.origin_frac;
min_r = Vec3::new(min_r.x.min(f.x), min_r.y.min(f.y), min_r.z.min(f.z));
max_r = Vec3::new(max_r.x.max(f.x), max_r.y.max(f.y), max_r.z.max(f.z));
}
let margin_r = Vec3::new(margin / cell.a, margin / cell.b, margin / cell.c);
min_r -= margin_r;
max_r += margin_r;
let to_idx = |fr: f64, n: i32| -> isize { (fr * n as f64 - 0.5).floor() as isize };
let lo_i = [
to_idx(min_r.x, inner.mx),
to_idx(min_r.y, inner.my),
to_idx(min_r.z, inner.mz),
];
let hi_i = [
to_idx(max_r.x, inner.mx),
to_idx(max_r.y, inner.my),
to_idx(max_r.z, inner.mz),
];
let dims = [
(hi_i[0] - lo_i[0] + 1) as usize,
(hi_i[1] - lo_i[1] + 1) as usize,
(hi_i[2] - lo_i[2] + 1) as usize,
];
let lo_frac = Vec3::new(
(lo_i[0] as f64 + 0.5) / inner.mx as f64,
(lo_i[1] as f64 + 0.5) / inner.my as f64,
(lo_i[2] as f64 + 0.5) / inner.mz as f64,
) + map.origin_frac;
let origin_cart = cell.fractional_to_cartesian(lo_frac);
let step = [
cell.a / inner.mx as f64,
cell.b / inner.my as f64,
cell.c / inner.mz as f64,
];
let mut data = Vec::with_capacity(dims[0] * dims[1] * dims[2]);
for kz in 0..dims[2] {
for ky in 0..dims[1] {
for kx in 0..dims[0] {
let idx_c = [
lo_i[0] + kx as isize,
lo_i[1] + ky as isize,
lo_i[2] + kz as isize,
];
let frac = map.origin_frac
+ Vec3::new(
(idx_c[0] as f64 + 0.5) / inner.mx as f64,
(idx_c[1] as f64 + 0.5) / inner.my as f64,
(idx_c[2] as f64 + 0.5) / inner.mz as f64,
);
let cart = cell.fractional_to_cartesian(frac);
let density = map.density_at_point_trilinear(cart);
let dens_sig = map.density_to_sig(density);
data.push(dens_sig);
}
}
}
Self {
origin_cart,
step,
dims,
data,
}
}
}