gchemol_readwrite/
template.rs

1// [[file:../gchemol-readwrite.note::43d6e881][43d6e881]]
2//! Template rendering for molecule
3
4use indexmap::{indexmap, IndexMap};
5use serde_json::json;
6
7use gchemol_core::{Atom, Molecule};
8use gut::prelude::*;
9// 43d6e881 ends here
10
11// [[file:../gchemol-readwrite.note::925f7269][925f7269]]
12mod hbs;
13mod jinja;
14mod tera;
15// 925f7269 ends here
16
17// [[file:../gchemol-readwrite.note::*core][core:1]]
18#[derive(Debug, Serialize)]
19struct AtomData {
20    index: usize,
21    element_index: usize,
22    symbol: String,
23    number: usize,
24    freezing: [bool; 3],
25    x: f64,
26    y: f64,
27    z: f64,
28    fx: f64,
29    fy: f64,
30    fz: f64,
31    vx: f64,
32    vy: f64,
33    vz: f64,
34}
35
36impl Default for AtomData {
37    fn default() -> Self {
38        AtomData {
39            index: 0,
40            element_index: 0,
41            symbol: "C".into(),
42            number: 6,
43            freezing: [false; 3],
44            x: 0.0,
45            y: 0.0,
46            z: 0.0,
47            fx: 0.0,
48            fy: 0.0,
49            fz: 0.0,
50            vx: 0.0,
51            vy: 0.0,
52            vz: 0.0,
53        }
54    }
55}
56
57#[derive(Debug, Serialize)]
58struct BondData {
59    i: usize,
60    j: usize,
61    order: f64,
62}
63
64#[derive(Debug, Serialize)]
65struct UnitCell {
66    a: f64,
67    b: f64,
68    c: f64,
69    alpha: f64,
70    beta: f64,
71    gamma: f64,
72    va: [f64; 3],
73    vb: [f64; 3],
74    vc: [f64; 3],
75}
76
77#[derive(Debug, Serialize)]
78struct SpeciesData {
79    index: usize,
80    element_symbol: String,
81    element_number: usize,
82    number_of_atoms: usize,
83}
84
85#[derive(Debug, Serialize)]
86struct MoleculeData {
87    title: String,
88    unit_cell: Option<UnitCell>,
89    number_of_atoms: usize,
90    number_of_bonds: usize,
91    number_of_species: usize,
92    atoms: Vec<AtomData>,
93    bonds: Vec<BondData>,
94
95    // mapping element type and numbers as in VASP POSCAR
96    // O C H  # element symbol
97    // 1 2 3  # element count
98    element_types: Vec<(String, usize)>,
99    species: Vec<SpeciesData>,
100}
101
102/// construct a shallow representation of molecule for templating
103pub(self) fn renderable(mol: &Molecule) -> serde_json::Value {
104    // unit cell data
105    let unit_cell = if let Some(lat) = mol.lattice {
106        let [va, vb, vc] = lat.vectors();
107        let [a, b, c] = lat.lengths();
108        let [alpha, beta, gamma] = lat.angles();
109
110        let cell = UnitCell {
111            a,
112            b,
113            c,
114            alpha,
115            beta,
116            gamma,
117            va: va.into(),
118            vb: vb.into(),
119            vc: vc.into(),
120        };
121
122        Some(cell)
123    } else {
124        None
125    };
126
127    let mut element_types: IndexMap<String, usize> = indexmap! {};
128    for (_, a) in mol.atoms() {
129        let k = a.symbol().into();
130        let c = element_types.entry(k).or_insert(0);
131        *c += 1;
132    }
133
134    // atoms data
135    let mut atoms = vec![];
136    for (i, a) in mol.atoms() {
137        let [x, y, z] = a.position();
138        let index = i;
139        let number = a.number();
140        let symbol = a.symbol().to_string();
141        let [fx, fy, fz] = mol.lattice.map(|lat| lat.to_frac([x, y, z]).into()).unwrap_or([0.0; 3]);
142
143        let element_index = {
144            let (x, _, _) = element_types.get_full(a.symbol()).expect("element type index");
145            x + 1
146        };
147
148        let [vx, vy, vz] = a.velocity();
149        let freezing = a.freezing();
150        atoms.push(AtomData {
151            index,
152            element_index,
153            symbol,
154            number,
155            freezing,
156            x,
157            y,
158            z,
159            fx,
160            fy,
161            fz,
162            vx,
163            vy,
164            vz,
165        })
166    }
167
168    // convert indexmap to plain list
169    let element_types: Vec<(_, _)> = element_types.into_iter().collect();
170
171    let n = element_types.len();
172    let species: Vec<_> = element_types
173        .iter()
174        .enumerate()
175        .map(|(i, (s, n))| SpeciesData {
176            index: i + 1,
177            element_symbol: s.clone(),
178            // FIXME: dirty
179            element_number: Atom::new(s.as_str(), [0.0; 3]).number(),
180            number_of_atoms: *n,
181        })
182        .collect();
183
184    let bonds = vec![];
185    let md = MoleculeData {
186        title: mol.title(),
187        number_of_atoms: mol.natoms(),
188        number_of_bonds: mol.nbonds(),
189        number_of_species: n,
190        unit_cell,
191        atoms,
192        bonds,
193        element_types,
194        species,
195    };
196
197    json!({
198        "molecule": md,
199    })
200}
201// core:1 ends here
202
203// [[file:../gchemol-readwrite.note::3582ce8f][3582ce8f]]
204/// Render molecule in user defined format
205pub trait TemplateRendering {
206    /// Render with input template file.
207    fn render_with(&self, f: &std::path::Path) -> Result<String>;
208}
209
210impl TemplateRendering for Molecule {
211    fn render_with(&self, path: &std::path::Path) -> Result<String> {
212        let template = gut::fs::read_file(path)?;
213
214        // possible extension in lowercase only
215        match path.extension().and_then(|x| x.to_str()) {
216            Some("hbs") => self::hbs::render_molecule_with(&self, &template),
217            Some("tera") => self::tera::render_molecule_with(&self, &template),
218            _ => self::jinja::render_molecule_with(&self, &template),
219        }
220    }
221}
222// 3582ce8f ends here
223
224// [[file:../gchemol-readwrite.note::e22b38a2][e22b38a2]]
225/// Convert molecule to json string ready for template rendering
226pub fn to_json(mol: &Molecule) -> Result<String> {
227    let data = renderable(mol);
228    let serialized = serde_json::to_string_pretty(&data)?;
229    Ok(serialized)
230}
231
232/// Convert molecule to json Value data structure for template rendering
233pub fn to_json_value(mol: &Molecule) -> serde_json::Value {
234    renderable(mol)
235}
236// e22b38a2 ends here
237
238// [[file:../gchemol-readwrite.note::0f1f521b][0f1f521b]]
239pub use self::jinja::Template;
240// 0f1f521b ends here