use super::*;
use std::ptr::null;
pub struct XtbEnvironment {
env: xtb_TEnvironment,
}
impl XtbEnvironment {
pub fn new() -> Self {
unsafe { assert_eq!(XTB_API_VERSION, xtb_getAPIVersion() as u32) };
Self {
env: unsafe { xtb_newEnvironment() },
}
}
pub fn check_error(&self) -> Result<()> {
let ret = unsafe { xtb_checkEnvironment(self.env) };
if ret != 0 {
unsafe { xtb_showEnvironment(self.env, null()) };
bail!("Error occured in the API with return code {}!", ret);
}
Ok(())
}
fn set_verbosity(&self, verbosity: u32) -> Result<()> {
unsafe {
xtb_setVerbosity(self.env, verbosity as i32);
}
self.check_error()?;
Ok(())
}
pub fn set_output_verbose(&self) -> Result<()> {
self.set_verbosity(XTB_VERBOSITY_FULL)
}
pub fn set_output_minimal(&self) -> Result<()> {
self.set_verbosity(XTB_VERBOSITY_MINIMAL)
}
pub fn set_output_muted(&self) -> Result<()> {
self.set_verbosity(XTB_VERBOSITY_MUTED)
}
}
pub struct XtbMolecule {
mol: xtb_TMolecule,
}
impl XtbMolecule {
pub fn create<'a>(
env: &XtbEnvironment,
attyp: &[i32],
coord: &[f64],
charge: f64,
uhf: i32,
lattice: impl Into<Option<&'a [f64; 9]>>,
periodic: impl Into<Option<&'a [bool; 3]>>,
) -> Result<Self> {
let mol = unsafe {
let natoms = attyp.len() as i32;
let env = env.env;
let attyp = attyp.as_ptr();
let coord = coord.as_ptr();
let lattice = lattice.into().map_or(null(), |x| x.as_ptr());
let periodic = periodic.into().map_or(null(), |x| x.as_ptr());
xtb_newMolecule(env, &natoms, attyp, coord, &charge, &uhf, lattice, periodic)
};
env.check_error()?;
let mol = Self { mol };
Ok(mol)
}
pub fn update(&self, env: &XtbEnvironment, coord: &[f64], lattice: Option<&[f64; 9]>) -> Result<()> {
unsafe {
let env = env.env;
let mol = self.mol;
let coord = coord.as_ptr();
let lattice = lattice.map_or(null(), |x| x.as_ptr());
xtb_updateMolecule(env, mol, coord, lattice);
}
env.check_error()?;
Ok(())
}
}
#[derive(Clone, Debug, Copy, PartialEq)]
pub enum XtbMethod {
GFN2xTB,
GFN1xTB,
GFN0xTB,
GFNFF,
}
pub struct XtbCalculator {
calc: xtb_TCalculator,
}
impl XtbCalculator {
pub fn new() -> Self {
Self {
calc: unsafe { xtb_newCalculator() },
}
}
pub fn load_parametrization(&self, mol: &XtbMolecule, env: &XtbEnvironment, method: XtbMethod) -> Result<()> {
unsafe {
let calc = self.calc;
let mol = mol.mol;
let env = env.env;
match method {
XtbMethod::GFNFF => xtb_loadGFNFF(env, mol, calc, std::ptr::null_mut()),
XtbMethod::GFN0xTB => xtb_loadGFN0xTB(env, mol, calc, std::ptr::null_mut()),
XtbMethod::GFN1xTB => xtb_loadGFN1xTB(env, mol, calc, std::ptr::null_mut()),
XtbMethod::GFN2xTB => xtb_loadGFN2xTB(env, mol, calc, std::ptr::null_mut()),
_ => unimplemented!(),
}
}
env.check_error()?;
Ok(())
}
pub fn set_max_iterations(&self, env: &XtbEnvironment, n: usize) {
unsafe {
xtb_setMaxIter(env.env, self.calc, n as i32);
}
}
pub fn set_electronic_temperature(&self, env: &XtbEnvironment, temp: f64) {
unsafe {
xtb_setElectronicTemp(env.env, self.calc, temp);
}
}
pub fn set_accuracy(&self, env: &XtbEnvironment, acc: f64) {
unsafe {
xtb_setAccuracy(env.env, self.calc, acc);
}
}
pub fn single_point(&self, mol: &XtbMolecule, env: &XtbEnvironment) -> Result<XtbResults> {
let mut res = XtbResults::new();
unsafe {
let calc = self.calc;
let mol = mol.mol;
let res = res.res;
let env = env.env;
xtb_singlepoint(env, mol, calc, res);
}
env.check_error()?;
Ok(res)
}
}
pub struct XtbResults {
res: xtb_TResults,
}
impl XtbResults {
fn new() -> Self {
Self {
res: unsafe { xtb_newResults() },
}
}
pub fn get_energy(&self, env: &XtbEnvironment) -> Result<f64> {
let mut energy = std::f64::NAN;
unsafe {
xtb_getEnergy(env.env, self.res, &mut energy);
}
env.check_error()?;
Ok(energy)
}
pub fn get_dipole(&self, env: &XtbEnvironment) -> Result<[f64; 3]> {
let mut dipole = [std::f64::NAN; 3];
unsafe {
xtb_getDipole(env.env, self.res, dipole.as_mut_ptr());
}
env.check_error()?;
Ok(dipole)
}
pub fn get_gradient(&self, env: &XtbEnvironment, gradient: &mut [f64]) -> Result<()> {
unsafe {
xtb_getGradient(env.env, self.res, gradient.as_mut_ptr());
}
env.check_error()?;
Ok(())
}
pub fn get_bond_orders(&self, env: &XtbEnvironment, bond_orders: &mut [f64]) -> Result<()> {
unsafe {
xtb_getBondOrders(env.env, self.res, bond_orders.as_mut_ptr());
}
env.check_error()?;
Ok(())
}
pub fn get_charges(&self, env: &XtbEnvironment, charges: &mut [f64]) -> Result<()> {
unsafe {
xtb_getCharges(env.env, self.res, charges.as_mut_ptr());
}
env.check_error()?;
Ok(())
}
pub fn get_virial(&self, env: &XtbEnvironment, virial: &mut [f64]) -> Result<()> {
unsafe {
xtb_getVirial(env.env, self.res, virial.as_mut_ptr());
}
env.check_error()?;
Ok(())
}
pub fn get_nao(&self, env: &XtbEnvironment) -> Result<usize> {
let mut nao = 0;
unsafe {
xtb_getNao(env.env, self.res, &mut nao);
}
env.check_error()?;
Ok(nao as usize)
}
pub fn get_orbital_eigenvalues(&self, env: &XtbEnvironment, emo: &mut [f64]) -> Result<()> {
unsafe {
xtb_getOrbitalEigenvalues(env.env, self.res, emo.as_mut_ptr());
}
env.check_error()?;
Ok(())
}
pub fn get_orbital_occupations(&self, env: &XtbEnvironment, focc: &mut [f64]) -> Result<()> {
unsafe {
xtb_getOrbitalOccupations(env.env, self.res, focc.as_mut_ptr());
}
env.check_error()?;
Ok(())
}
pub fn get_orbital_coefficients(&self, env: &XtbEnvironment, forb: &mut [f64]) -> Result<()> {
unsafe {
xtb_getOrbitalCoefficients(env.env, self.res, forb.as_mut_ptr());
}
env.check_error()?;
Ok(())
}
}
macro_rules! impl_xtb_drop {
($obj:ident, $xtb_del:ident, $res:ident) => {
impl Drop for $obj {
fn drop(&mut self) {
if !self.$res.is_null() {
unsafe { $xtb_del(&mut self.$res) }
}
assert!(self.$res.is_null());
}
}
};
}
impl_xtb_drop!(XtbEnvironment, xtb_delEnvironment, env);
impl_xtb_drop!(XtbMolecule, xtb_delMolecule, mol);
impl_xtb_drop!(XtbResults, xtb_delResults, res);
impl_xtb_drop!(XtbCalculator, xtb_delCalculator, calc);