use bader::analysis::calculate_bader_density;
use bader::arguments::App;
use bader::critical::{
bond_adjacency, bond_pruning, cage_pruning, critical_point_merge,
nuclei_ordering, ring_pruning,
};
use bader::errors::ArgumentError;
use bader::io::{self, FileFormat, WriteType};
use bader::methods::{maxima_finder, minima_finder, weight};
use bader::utils::index_generator;
use bader::voxel_map::{BlockingVoxelMap, VoxelMap};
fn main() {
let app = App::new();
let env_args = std::env::args().collect::<Vec<String>>();
let args =
match app.parse_args(env_args.iter().map(|s| s.as_str()).collect()) {
Ok(a) => a,
Err(e) => match e {
ArgumentError::ShortHelp(_)
| ArgumentError::LongHelp(_)
| ArgumentError::NoFile(_) => {
println!("{}", e);
return;
}
_ => panic!("{}", e),
},
};
let splash = format!(
"{}: v{}\nRunning on {} threads.",
env!("CARGO_PKG_DESCRIPTION"),
env!("CARGO_PKG_VERSION"),
args.threads
);
if !args.silent {
println!("{}", splash);
}
let (densities, rho, atoms, grid, voxel_origin) =
args.file_type.init(&args);
let reference = if rho.is_empty() { &densities[0] } else { &rho };
let voxel_map =
BlockingVoxelMap::new(grid, atoms.lattice.to_cartesian, voxel_origin);
let index = match index_generator(reference, args.vacuum_tolerance) {
Ok(i) => i,
Err(e) => panic!("{}", e),
};
let mut nuclei = match maxima_finder(
&index,
reference,
&voxel_map,
&args.maximum_distance,
&atoms,
args.threads,
!args.silent,
) {
Ok(v) => v,
Err(e) => panic!(
"\nBader maximum at {:#?}\n is too far away from nearest atom: {} with a distance of {} Ang.",
args.file_type.coordinate_format(e.maximum),
e.atom + 1,
e.distance,
),
};
let ordered_nuclei = nuclei_ordering(
&mut nuclei,
reference,
&atoms,
&voxel_map.grid,
!args.silent,
);
nuclei.iter().for_each(|maximum| {
voxel_map.maxima_store(maximum.position, maximum.atoms[0].0 as isize);
});
let (bonds, rings) = weight(
reference,
&voxel_map,
&index,
args.weight_tolerance,
!args.silent,
args.threads,
);
let voxel_map = VoxelMap::from_blocking_voxel_map(voxel_map);
let cages = minima_finder(
&index,
reference,
&voxel_map,
args.threads,
!args.silent,
);
let (atoms_density, atoms_volume, atoms_radius, atoms_error) =
calculate_bader_density(
&densities,
&voxel_map,
&atoms,
args.threads,
!args.silent,
);
let bonds = bond_pruning(&bonds, reference, &args);
let bond_adjacency = bond_adjacency(&bonds, atoms.positions.len());
let rings = critical_point_merge(ring_pruning(
&rings,
&bond_adjacency,
reference,
&args,
));
let cages = critical_point_merge(cage_pruning(
&cages,
&ordered_nuclei,
reference,
&atoms,
voxel_map.grid_get(),
&args,
));
let partition_table = io::output::PartitionTable::new(
&atoms_density,
&atoms_volume,
&atoms_radius,
&atoms_error,
voxel_map.weight_len(),
reference.len(),
);
let critical_points = (ordered_nuclei, bonds, rings, cages);
let critical_point_output = io::output::CriticalPointOutput::new(
atoms.positions.clone(),
critical_points,
reference,
&voxel_map.grid,
args.file_type,
);
if io::output::write(
format!(
"# {}\n\n{}\n\n{}",
splash, partition_table, critical_point_output
),
String::from("bader_charge_analysis.md"),
)
.is_err()
{
panic!("Error in writing bader_charge_analysis.md")
}
let filename = match densities.len().cmp(&2) {
std::cmp::Ordering::Less => vec![String::from("charge")],
std::cmp::Ordering::Equal => {
vec![String::from("charge"), String::from("spin")]
}
std::cmp::Ordering::Greater => vec![
String::from("charge"),
String::from("spin_x"),
String::from("spin_y"),
String::from("spin_z"),
],
};
let write_map: Box<dyn Iterator<Item = (isize, Vec<Option<f64>>)>> = {
if let WriteType::Atom(a) = args.output {
let atom_iter = if a.is_empty() {
(0..atoms.positions.len() as isize).collect()
} else {
a
};
Box::new(atom_iter.into_iter().map(|atom_number| {
let map = voxel_map.volume_map(atom_number);
(atom_number, map)
}))
} else {
Box::new(Vec::with_capacity(0).into_iter())
}
};
write_map.for_each(|(id, weight_map)| {
densities.iter().zip(&filename).for_each(|(rho, flnm)| {
if args
.file_type
.write(
&atoms,
weight_map
.iter()
.zip(rho)
.map(|(weight, charge)| weight.map(|w| w * charge))
.collect(),
format!("{}_{}", id + 1, flnm),
!args.silent,
)
.is_err()
{
panic!("Error in writing {}", flnm)
}
});
});
}