use std::path::{Path, PathBuf};
use crate::{
AbovePlaneRestraint, Angle, AtomRestraint, BelowPlaneRestraint, CenteringMode,
InsideBoxRestraint, InsideCubeRestraint, InsideCylinderRestraint, InsideEllipsoidRestraint,
InsideSphereRestraint, Molpack, OutsideBoxRestraint, OutsideCubeRestraint,
OutsideCylinderRestraint, OutsideEllipsoidRestraint, OutsideSphereRestraint, Target,
};
use super::error::ScriptError;
use super::parser::{AtomGroup, RestraintSpec, Script, Structure};
pub struct ScriptPlan {
pub packer: Molpack,
pub structures: Vec<StructurePlan>,
pub output: PathBuf,
pub nloop: usize,
pub filetype: Option<String>,
}
pub struct StructurePlan {
pub filepath: PathBuf,
pub number: usize,
pub mol_restraints: Vec<RestraintSpec>,
pub atom_groups: Vec<AtomGroup>,
pub center: bool,
pub fixed: Option<([f64; 3], [f64; 3])>,
}
impl Script {
pub fn lower(&self, base_dir: &Path) -> Result<ScriptPlan, ScriptError> {
if self.structures.is_empty() {
return Err(ScriptError::NoStructures);
}
let mut packer = Molpack::new()
.with_tolerance(self.tolerance)
.with_avoid_overlap(self.avoid_overlap);
if let Some(seed) = self.seed {
packer = packer.with_seed(seed);
}
if let Some(pbc) = self.pbc {
packer = packer.with_periodic_box(pbc.min, pbc.max);
}
let structures: Vec<StructurePlan> = self
.structures
.iter()
.map(|s| StructurePlan::from_structure(s, base_dir))
.collect();
Ok(ScriptPlan {
packer,
structures,
output: resolve(base_dir, &self.output),
nloop: self.nloop,
filetype: self.filetype.clone(),
})
}
}
impl StructurePlan {
fn from_structure(s: &Structure, base_dir: &Path) -> Self {
Self {
filepath: resolve(base_dir, &s.filepath),
number: s.number,
mol_restraints: s.mol_restraints.clone(),
atom_groups: s.atom_groups.clone(),
center: s.center,
fixed: s.fixed,
}
}
pub fn apply(&self, mut target: Target) -> Target {
for r in &self.mol_restraints {
target = apply_mol_restraint(target, r);
}
for group in &self.atom_groups {
target = apply_atom_group(target, group);
}
if self.center {
target = target.with_centering(CenteringMode::Center);
}
if let Some((pos, euler)) = self.fixed {
target = target.fixed_at(pos).with_orientation([
Angle::from_radians(euler[0]),
Angle::from_radians(euler[1]),
Angle::from_radians(euler[2]),
]);
}
target
}
}
fn resolve(base: &Path, path: &Path) -> PathBuf {
if path.is_absolute() {
path.to_path_buf()
} else {
base.join(path)
}
}
fn restraint_from_spec(r: &RestraintSpec) -> Box<dyn AtomRestraint> {
match *r {
RestraintSpec::InsideBox { min, max } => {
Box::new(InsideBoxRestraint::new(min, max, [false; 3]))
}
RestraintSpec::OutsideBox { min, max } => Box::new(OutsideBoxRestraint::new(min, max)),
RestraintSpec::InsideCube { origin, side } => {
Box::new(InsideCubeRestraint::new(origin, side))
}
RestraintSpec::OutsideCube { origin, side } => {
Box::new(OutsideCubeRestraint::new(origin, side))
}
RestraintSpec::InsideSphere { center, radius } => {
Box::new(InsideSphereRestraint::new(center, radius))
}
RestraintSpec::OutsideSphere { center, radius } => {
Box::new(OutsideSphereRestraint::new(center, radius))
}
RestraintSpec::InsideEllipsoid {
center,
axes,
exponent,
} => Box::new(InsideEllipsoidRestraint::new(center, axes, exponent)),
RestraintSpec::OutsideEllipsoid {
center,
axes,
exponent,
} => Box::new(OutsideEllipsoidRestraint::new(center, axes, exponent)),
RestraintSpec::InsideCylinder {
center,
axis,
radius,
length,
} => Box::new(InsideCylinderRestraint::new(center, axis, radius, length)),
RestraintSpec::OutsideCylinder {
center,
axis,
radius,
length,
} => Box::new(OutsideCylinderRestraint::new(center, axis, radius, length)),
RestraintSpec::AbovePlane { normal, distance } => {
Box::new(AbovePlaneRestraint::new(normal, distance))
}
RestraintSpec::BelowPlane { normal, distance } => {
Box::new(BelowPlaneRestraint::new(normal, distance))
}
}
}
fn apply_mol_restraint(target: Target, r: &RestraintSpec) -> Target {
target.with_restraint(restraint_from_spec(r))
}
fn apply_atom_group(mut target: Target, group: &AtomGroup) -> Target {
let zero_indexed: Vec<usize> = group
.atom_indices
.iter()
.map(|&i| i.saturating_sub(1))
.collect();
let indices = zero_indexed.as_slice();
for r in &group.restraints {
target = target.with_atom_restraint(indices, restraint_from_spec(r));
}
target
}
#[cfg(feature = "io")]
pub struct BuildResult {
pub packer: Molpack,
pub targets: Vec<Target>,
pub output: PathBuf,
pub nloop: usize,
}
#[cfg(feature = "io")]
impl Script {
pub fn build(&self, base_dir: &Path) -> Result<BuildResult, ScriptError> {
let plan = self.lower(base_dir)?;
let filetype = plan.filetype.as_deref();
let targets: Vec<Target> = plan
.structures
.iter()
.map(|sp| -> Result<Target, ScriptError> {
let frame = super::io::read_frame(&sp.filepath, filetype)?;
Ok(sp.apply(Target::new(frame, sp.number)))
})
.collect::<Result<_, _>>()?;
Ok(BuildResult {
packer: plan.packer,
targets,
output: plan.output,
nloop: plan.nloop,
})
}
}