use indexmap::{indexmap, IndexMap};
use serde_json::json;
use gchemol_core::{Atom, Molecule};
use gut::prelude::*;
mod hbs;
mod jinja;
mod tera;
#[derive(Debug, Serialize)]
struct AtomData {
index: usize,
element_index: usize,
symbol: String,
number: usize,
freezing: [bool; 3],
x: f64,
y: f64,
z: f64,
fx: f64,
fy: f64,
fz: f64,
vx: f64,
vy: f64,
vz: f64,
}
impl Default for AtomData {
fn default() -> Self {
AtomData {
index: 0,
element_index: 0,
symbol: "C".into(),
number: 6,
freezing: [false; 3],
x: 0.0,
y: 0.0,
z: 0.0,
fx: 0.0,
fy: 0.0,
fz: 0.0,
vx: 0.0,
vy: 0.0,
vz: 0.0,
}
}
}
#[derive(Debug, Serialize)]
struct BondData {
i: usize,
j: usize,
order: f64,
}
#[derive(Debug, Serialize)]
struct UnitCell {
a: f64,
b: f64,
c: f64,
alpha: f64,
beta: f64,
gamma: f64,
va: [f64; 3],
vb: [f64; 3],
vc: [f64; 3],
}
#[derive(Debug, Serialize)]
struct SpeciesData {
index: usize,
element_symbol: String,
element_number: usize,
number_of_atoms: usize,
}
#[derive(Debug, Serialize)]
struct MoleculeData {
title: String,
unit_cell: Option<UnitCell>,
number_of_atoms: usize,
number_of_bonds: usize,
number_of_species: usize,
atoms: Vec<AtomData>,
bonds: Vec<BondData>,
element_types: Vec<(String, usize)>,
species: Vec<SpeciesData>,
}
pub(self) fn renderable(mol: &Molecule) -> serde_json::Value {
let unit_cell = if let Some(lat) = mol.lattice {
let [va, vb, vc] = lat.vectors();
let [a, b, c] = lat.lengths();
let [alpha, beta, gamma] = lat.angles();
let cell = UnitCell {
a,
b,
c,
alpha,
beta,
gamma,
va: va.into(),
vb: vb.into(),
vc: vc.into(),
};
Some(cell)
} else {
None
};
let mut element_types: IndexMap<String, usize> = indexmap! {};
for (_, a) in mol.atoms() {
let k = a.symbol().into();
let c = element_types.entry(k).or_insert(0);
*c += 1;
}
let mut atoms = vec![];
for (i, a) in mol.atoms() {
let [x, y, z] = a.position();
let index = i;
let number = a.number();
let symbol = a.symbol().to_string();
let [fx, fy, fz] = mol.lattice.map(|lat| lat.to_frac([x, y, z]).into()).unwrap_or([0.0; 3]);
let element_index = {
let (x, _, _) = element_types.get_full(a.symbol()).expect("element type index");
x + 1
};
let [vx, vy, vz] = a.velocity();
let freezing = a.freezing();
atoms.push(AtomData {
index,
element_index,
symbol,
number,
freezing,
x,
y,
z,
fx,
fy,
fz,
vx,
vy,
vz,
})
}
let element_types: Vec<(_, _)> = element_types.into_iter().collect();
let n = element_types.len();
let species: Vec<_> = element_types
.iter()
.enumerate()
.map(|(i, (s, n))| SpeciesData {
index: i + 1,
element_symbol: s.clone(),
element_number: Atom::new(s.as_str(), [0.0; 3]).number(),
number_of_atoms: *n,
})
.collect();
let bonds = vec![];
let md = MoleculeData {
title: mol.title(),
number_of_atoms: mol.natoms(),
number_of_bonds: mol.nbonds(),
number_of_species: n,
unit_cell,
atoms,
bonds,
element_types,
species,
};
json!({
"molecule": md,
})
}
pub trait TemplateRendering {
fn render_with(&self, f: &std::path::Path) -> Result<String>;
}
impl TemplateRendering for Molecule {
fn render_with(&self, path: &std::path::Path) -> Result<String> {
let template = gut::fs::read_file(path)?;
match path.extension().and_then(|x| x.to_str()) {
Some("hbs") => self::hbs::render_molecule_with(&self, &template),
Some("tera") => self::tera::render_molecule_with(&self, &template),
_ => self::jinja::render_molecule_with(&self, &template),
}
}
}
pub fn to_json(mol: &Molecule) -> Result<String> {
let data = renderable(mol);
let serialized = serde_json::to_string_pretty(&data)?;
Ok(serialized)
}
pub fn to_json_value(mol: &Molecule) -> serde_json::Value {
renderable(mol)
}
pub use self::jinja::Template;