use crate::ffi;
use derive_builder::{Builder, UninitializedFieldError};
use duplicate::duplicate_item;
use std::ffi::{c_char, c_int, CStr};
use std::ptr::{null, null_mut};
use std::result::Result;
pub fn dftd3_get_api_version() -> String {
let version = unsafe { ffi::dftd3_get_version() };
format!("{}.{}.{}", version / 10000, version / 100 % 100, version % 100)
}
pub fn dftd3_get_api_version_compact() -> [usize; 3] {
let version = unsafe { ffi::dftd3_get_version() } as usize;
[version / 10000, version / 100 % 100, version % 100]
}
pub enum DFTD3Error {
C(ffi::dftd3_error),
Rust(String),
BuilderError(UninitializedFieldError),
}
impl From<UninitializedFieldError> for DFTD3Error {
fn from(ufe: UninitializedFieldError) -> DFTD3Error {
DFTD3Error::BuilderError(ufe)
}
}
impl Drop for DFTD3Error {
fn drop(&mut self) {
if let DFTD3Error::C(ptr) = self {
unsafe { ffi::dftd3_delete_error(&mut ptr.clone()) }
}
}
}
impl Default for DFTD3Error {
fn default() -> Self {
DFTD3Error::new()
}
}
impl std::error::Error for DFTD3Error {}
impl DFTD3Error {
pub fn new() -> Self {
let ptr = unsafe { ffi::dftd3_new_error() };
DFTD3Error::C(ptr)
}
pub fn check(&self) -> bool {
match self {
DFTD3Error::C(ptr) => unsafe { ffi::dftd3_check_error(*ptr) != 0 },
_ => true,
}
}
pub fn get_c_ptr(&mut self) -> ffi::dftd3_error {
match self {
DFTD3Error::C(ptr) => *ptr,
_ => std::ptr::null_mut(),
}
}
pub fn get_message(&self) -> String {
match self {
DFTD3Error::C(ptr) => {
const LEN_BUFFER: usize = 512;
let buffer = [0u8; LEN_BUFFER];
let raw = buffer.as_ptr() as *mut c_char;
let msg = unsafe {
ffi::dftd3_get_error(*ptr, raw, &(LEN_BUFFER as c_int));
CStr::from_ptr(raw)
};
msg.to_string_lossy().to_string()
},
DFTD3Error::Rust(msg) => msg.clone(),
DFTD3Error::BuilderError(ufe) => {
format!("Builder error: {:?}", ufe)
},
}
}
}
impl std::fmt::Debug for DFTD3Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if self.check() {
write!(f, "DFTD3Error: {}", self.get_message())
} else {
write!(f, "DFTD3Error: No error")
}
}
}
impl std::fmt::Display for DFTD3Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if self.check() {
write!(f, "DFTD3Error: {}", self.get_message())
} else {
write!(f, "")
}
}
}
pub struct DFTD3Structure {
pub(crate) ptr: ffi::dftd3_structure,
natoms: usize,
}
impl Drop for DFTD3Structure {
fn drop(&mut self) {
unsafe { ffi::dftd3_delete_structure(&mut self.ptr) };
}
}
impl DFTD3Structure {
pub fn new(
numbers: &[usize],
positions: &[f64],
lattice: Option<&[f64]>,
periodic: Option<&[bool]>,
) -> Self {
Self::new_f(numbers, positions, lattice, periodic).unwrap()
}
pub fn update(&mut self, positions: &[f64], lattice: Option<&[f64]>) {
self.update_f(positions, lattice).unwrap()
}
pub fn get_natoms(&self) -> usize {
self.natoms
}
pub fn new_f(
numbers: &[usize],
positions: &[f64],
lattice: Option<&[f64]>,
periodic: Option<&[bool]>,
) -> Result<Self, DFTD3Error> {
let natoms = numbers.len();
if positions.len() != 3 * natoms {
return Err(DFTD3Error::Rust(format!(
"Invalid dimension for positions, expected {}, got {}",
3 * natoms,
positions.len()
)));
}
if lattice.is_some_and(|lattice| lattice.len() != 9) {
return Err(DFTD3Error::Rust(format!(
"Invalid dimension for lattice, expected 9, got {}",
lattice.unwrap().len()
)));
}
if periodic.is_some_and(|periodic| periodic.len() != 3) {
return Err(DFTD3Error::Rust(format!(
"Invalid dimension for periodic, expected 3, got {}",
periodic.unwrap().len()
)));
}
let lattice_ptr = lattice.map_or(null(), |x| x.as_ptr());
let periodic_ptr = periodic.map_or(null(), |x| x.as_ptr());
let natoms_c_int = natoms as c_int;
let atomic_numbers = numbers.iter().map(|&x| x as c_int).collect::<Vec<c_int>>();
let mut error = DFTD3Error::new();
let ptr = unsafe {
ffi::dftd3_new_structure(
error.get_c_ptr(),
natoms_c_int,
atomic_numbers.as_ptr(),
positions.as_ptr(),
lattice_ptr,
periodic_ptr,
)
};
match error.check() {
true => Err(error),
false => Ok(Self { ptr, natoms }),
}
}
pub fn update_f(
&mut self,
positions: &[f64],
lattice: Option<&[f64]>,
) -> Result<(), DFTD3Error> {
if positions.len() != 3 * self.natoms {
return Err(DFTD3Error::Rust(format!(
"Invalid dimension for positions, expected {}, got {}",
3 * self.natoms,
positions.len()
)));
}
if lattice.is_some_and(|lattice| lattice.len() != 9) {
return Err(DFTD3Error::Rust(format!(
"Invalid dimension for lattice, expected 9, got {}",
lattice.unwrap().len()
)));
}
let lattice_ptr = lattice.map_or(null(), |x| x.as_ptr());
let mut error = DFTD3Error::new();
unsafe {
ffi::dftd3_update_structure(
error.get_c_ptr(),
self.ptr,
positions.as_ptr(),
lattice_ptr,
)
};
match error.check() {
true => Err(error),
false => Ok(()),
}
}
}
pub struct DFTD3Param {
ptr: ffi::dftd3_param,
}
impl Drop for DFTD3Param {
fn drop(&mut self) {
unsafe { ffi::dftd3_delete_param(&mut self.ptr) };
}
}
impl DFTD3Param {
pub fn new_zero_damping_f(
s6: f64,
s8: f64,
s9: f64,
rs6: f64,
rs8: f64,
alp: f64,
) -> Result<Self, DFTD3Error> {
let mut error = DFTD3Error::new();
let ptr =
unsafe { ffi::dftd3_new_zero_damping(error.get_c_ptr(), s6, s8, s9, rs6, rs8, alp) };
match error.check() {
true => Err(error),
false => Ok(Self { ptr }),
}
}
pub fn new_zero_damping(s6: f64, s8: f64, s9: f64, rs6: f64, rs8: f64, alp: f64) -> Self {
Self::new_zero_damping_f(s6, s8, s9, rs6, rs8, alp).unwrap()
}
pub fn load_zero_damping_f(method: &str, atm: bool) -> Result<Self, DFTD3Error> {
let mut error = DFTD3Error::new();
let token = std::ffi::CString::new(method).unwrap();
let ptr = unsafe { ffi::dftd3_load_zero_damping(error.get_c_ptr(), token.into_raw(), atm) };
match error.check() {
true => Err(error),
false => Ok(Self { ptr }),
}
}
pub fn load_zero_damping(method: &str, atm: bool) -> Self {
Self::load_zero_damping_f(method, atm).unwrap()
}
pub fn new_rational_damping_f(
s6: f64,
s8: f64,
s9: f64,
a1: f64,
a2: f64,
alp: f64,
) -> Result<Self, DFTD3Error> {
let mut error = DFTD3Error::new();
let ptr =
unsafe { ffi::dftd3_new_rational_damping(error.get_c_ptr(), s6, s8, s9, a1, a2, alp) };
match error.check() {
true => Err(error),
false => Ok(Self { ptr }),
}
}
pub fn new_rational_damping(s6: f64, s8: f64, s9: f64, a1: f64, a2: f64, alp: f64) -> Self {
Self::new_rational_damping_f(s6, s8, s9, a1, a2, alp).unwrap()
}
pub fn load_rational_damping_f(method: &str, atm: bool) -> Result<Self, DFTD3Error> {
let mut error = DFTD3Error::new();
let token = std::ffi::CString::new(method).unwrap();
let ptr =
unsafe { ffi::dftd3_load_rational_damping(error.get_c_ptr(), token.into_raw(), atm) };
match error.check() {
true => Err(error),
false => Ok(Self { ptr }),
}
}
pub fn load_rational_damping(method: &str, atm: bool) -> Self {
Self::load_rational_damping_f(method, atm).unwrap()
}
pub fn new_mzero_damping_f(
s6: f64,
s8: f64,
s9: f64,
rs6: f64,
rs8: f64,
alp: f64,
bet: f64,
) -> Result<Self, DFTD3Error> {
let mut error = DFTD3Error::new();
let ptr = unsafe {
ffi::dftd3_new_mzero_damping(error.get_c_ptr(), s6, s8, s9, rs6, rs8, alp, bet)
};
match error.check() {
true => Err(error),
false => Ok(Self { ptr }),
}
}
pub fn new_mzero_damping(
s6: f64,
s8: f64,
s9: f64,
rs6: f64,
rs8: f64,
alp: f64,
bet: f64,
) -> Self {
Self::new_mzero_damping_f(s6, s8, s9, rs6, rs8, alp, bet).unwrap()
}
pub fn load_mzero_damping_f(method: &str, atm: bool) -> Result<Self, DFTD3Error> {
let mut error = DFTD3Error::new();
let token = std::ffi::CString::new(method).unwrap();
let ptr =
unsafe { ffi::dftd3_load_mzero_damping(error.get_c_ptr(), token.into_raw(), atm) };
match error.check() {
true => Err(error),
false => Ok(Self { ptr }),
}
}
pub fn load_mzero_damping(method: &str, atm: bool) -> Self {
Self::load_mzero_damping_f(method, atm).unwrap()
}
pub fn new_mrational_damping_f(
s6: f64,
s8: f64,
s9: f64,
a1: f64,
a2: f64,
alp: f64,
) -> Result<Self, DFTD3Error> {
let mut error = DFTD3Error::new();
let ptr =
unsafe { ffi::dftd3_new_mrational_damping(error.get_c_ptr(), s6, s8, s9, a1, a2, alp) };
match error.check() {
true => Err(error),
false => Ok(Self { ptr }),
}
}
pub fn new_mrational_damping(s6: f64, s8: f64, s9: f64, a1: f64, a2: f64, alp: f64) -> Self {
Self::new_mrational_damping_f(s6, s8, s9, a1, a2, alp).unwrap()
}
pub fn load_mrational_damping_f(method: &str, atm: bool) -> Result<Self, DFTD3Error> {
let mut error = DFTD3Error::new();
let token = std::ffi::CString::new(method).unwrap();
let ptr =
unsafe { ffi::dftd3_load_mrational_damping(error.get_c_ptr(), token.into_raw(), atm) };
match error.check() {
true => Err(error),
false => Ok(Self { ptr }),
}
}
pub fn load_mrational_damping(method: &str, atm: bool) -> Self {
Self::load_mrational_damping_f(method, atm).unwrap()
}
pub fn new_optimizedpower_damping_f(
s6: f64,
s8: f64,
s9: f64,
a1: f64,
a2: f64,
alp: f64,
bet: f64,
) -> Result<Self, DFTD3Error> {
let mut error = DFTD3Error::new();
let ptr = unsafe {
ffi::dftd3_new_optimizedpower_damping(error.get_c_ptr(), s6, s8, s9, a1, a2, alp, bet)
};
match error.check() {
true => Err(error),
false => Ok(Self { ptr }),
}
}
pub fn new_optimizedpower_damping(
s6: f64,
s8: f64,
s9: f64,
a1: f64,
a2: f64,
alp: f64,
bet: f64,
) -> Self {
Self::new_optimizedpower_damping_f(s6, s8, s9, a1, a2, alp, bet).unwrap()
}
pub fn load_optimizedpower_damping_f(method: &str, atm: bool) -> Result<Self, DFTD3Error> {
let mut error = DFTD3Error::new();
let token = std::ffi::CString::new(method).unwrap();
let ptr = unsafe {
ffi::dftd3_load_optimizedpower_damping(error.get_c_ptr(), token.into_raw(), atm)
};
match error.check() {
true => Err(error),
false => Ok(Self { ptr }),
}
}
pub fn load_optimizedpower_damping(method: &str, atm: bool) -> Self {
Self::load_optimizedpower_damping_f(method, atm).unwrap()
}
}
pub fn dftd3_load_param(version: &str, method: &str, atm: bool) -> DFTD3Param {
dftd3_load_param_f(version, method, atm).unwrap()
}
pub fn dftd3_load_param_f(
version: &str,
method: &str,
atm: bool,
) -> Result<DFTD3Param, DFTD3Error> {
let version = version.to_lowercase().replace(['-', '_', ' '], "");
match version.as_str() {
"d3bj" | "bj" => DFTD3Param::load_rational_damping_f(method, atm),
"d3zero" | "zero" => DFTD3Param::load_zero_damping_f(method, atm),
"d3bjm" | "d3mbj" | "bjm" | "mbj" => DFTD3Param::load_mrational_damping_f(method, atm),
"d3zerom" | "d3mzero" | "zerom" | "mzero" => DFTD3Param::load_mzero_damping_f(method, atm),
"d3op" | "op" => DFTD3Param::load_optimizedpower_damping_f(method, atm),
_ => Err(DFTD3Error::Rust(format!("Unknown DFTD3 version: {}", version))),
}
}
pub trait DFTD3ParamAPI {
fn new_param_f(self) -> Result<DFTD3Param, DFTD3Error>;
fn new_param(self) -> DFTD3Param
where
Self: Sized,
{
self.new_param_f().unwrap()
}
}
pub trait DFTD3LoadParamAPI {
fn load_param_f(method: &str, atm: bool) -> Result<DFTD3Param, DFTD3Error>;
fn load_param(method: &str, atm: bool) -> DFTD3Param {
Self::load_param_f(method, atm).unwrap()
}
}
#[doc = include_str!("damping_param_usage.md")]
#[derive(Builder, Debug, Clone)]
#[builder(pattern = "owned", build_fn(error = "DFTD3Error"))]
pub struct DFTD3RationalDampingParam {
#[builder(default = 1.0)]
#[doc = r"optional, default 1.0"]
pub s6: f64,
pub s8: f64,
#[builder(default = 1.0)]
#[doc = r"optional, default 1.0"]
pub s9: f64,
pub a1: f64,
pub a2: f64,
#[builder(default = 14.0)]
#[doc = r"optional, default 14.0"]
pub alp: f64,
}
impl DFTD3ParamAPI for DFTD3RationalDampingParam {
fn new_param_f(self) -> Result<DFTD3Param, DFTD3Error> {
let Self { s6, s8, s9, a1, a2, alp } = self;
DFTD3Param::new_rational_damping_f(s6, s8, s9, a1, a2, alp)
}
}
#[doc = include_str!("damping_param_usage.md")]
#[derive(Builder, Debug, Clone)]
#[builder(pattern = "owned", build_fn(error = "DFTD3Error"))]
pub struct DFTD3ZeroDampingParam {
#[builder(default = 1.0)]
#[doc = r"optional, default 1.0"]
pub s6: f64,
pub s8: f64,
#[builder(default = 1.0)]
#[doc = r"optional, default 1.0"]
pub s9: f64,
pub rs6: f64,
#[builder(default = 1.0)]
#[doc = r"optional, default 1.0"]
pub rs8: f64,
#[builder(default = 14.0)]
#[doc = r"optional, default 14.0"]
pub alp: f64,
}
impl DFTD3ParamAPI for DFTD3ZeroDampingParam {
fn new_param_f(self) -> Result<DFTD3Param, DFTD3Error> {
let Self { s6, s8, s9, rs6, rs8, alp } = self;
DFTD3Param::new_zero_damping_f(s6, s8, s9, rs6, rs8, alp)
}
}
#[doc = include_str!("damping_param_usage.md")]
#[derive(Builder, Debug, Clone)]
#[builder(pattern = "owned", build_fn(error = "DFTD3Error"))]
pub struct DFTD3ModifiedRationalDampingParam {
#[builder(default = 1.0)]
#[doc = r"optional, default 1.0"]
pub s6: f64,
pub s8: f64,
#[builder(default = 1.0)]
#[doc = r"optional, default 1.0"]
pub s9: f64,
pub a1: f64,
pub a2: f64,
#[builder(default = 14.0)]
#[doc = r"optional, default 14.0"]
pub alp: f64,
}
impl DFTD3ParamAPI for DFTD3ModifiedRationalDampingParam {
fn new_param_f(self) -> Result<DFTD3Param, DFTD3Error> {
let Self { s6, s8, s9, a1, a2, alp } = self;
DFTD3Param::new_mrational_damping_f(s6, s8, s9, a1, a2, alp)
}
}
#[doc = include_str!("damping_param_usage.md")]
#[derive(Builder, Debug, Clone)]
#[builder(pattern = "owned", build_fn(error = "DFTD3Error"))]
pub struct DFTD3ModifiedZeroDampingParam {
#[builder(default = 1.0)]
#[doc = r"optional, default 1.0"]
pub s6: f64,
pub s8: f64,
#[builder(default = 1.0)]
#[doc = r"optional, default 1.0"]
pub s9: f64,
pub rs6: f64,
#[builder(default = 1.0)]
#[doc = r"optional, default 1.0"]
pub rs8: f64,
#[builder(default = 14.0)]
#[doc = r"optional, default 14.0"]
pub alp: f64,
pub bet: f64,
}
impl DFTD3ParamAPI for DFTD3ModifiedZeroDampingParam {
fn new_param_f(self) -> Result<DFTD3Param, DFTD3Error> {
let Self { s6, s8, s9, rs6, rs8, alp, bet } = self;
DFTD3Param::new_mzero_damping_f(s6, s8, s9, rs6, rs8, alp, bet)
}
}
#[doc = include_str!("damping_param_usage.md")]
#[derive(Builder, Debug, Clone)]
#[builder(pattern = "owned", build_fn(error = "DFTD3Error"))]
pub struct DFTD3OptimizedPowerDampingParam {
#[builder(default = 1.0)]
#[doc = r"optional, default 1.0"]
pub s6: f64,
pub s8: f64,
#[builder(default = 1.0)]
#[doc = r"optional, default 1.0"]
pub s9: f64,
pub a1: f64,
pub a2: f64,
#[builder(default = 14.0)]
#[doc = r"optional, default 14.0"]
pub alp: f64,
pub bet: f64,
}
impl DFTD3ParamAPI for DFTD3OptimizedPowerDampingParam {
fn new_param_f(self) -> Result<DFTD3Param, DFTD3Error> {
let Self { s6, s8, s9, a1, a2, alp, bet } = self;
DFTD3Param::new_optimizedpower_damping_f(s6, s8, s9, a1, a2, alp, bet)
}
}
#[duplicate_item(
DampingParam load_damping_f ;
[DFTD3RationalDampingParam ] [load_rational_damping_f ];
[DFTD3ZeroDampingParam ] [load_zero_damping_f ];
[DFTD3ModifiedRationalDampingParam] [load_mrational_damping_f ];
[DFTD3ModifiedZeroDampingParam ] [load_mzero_damping_f ];
[DFTD3OptimizedPowerDampingParam ] [load_optimizedpower_damping_f];
)]
impl DFTD3LoadParamAPI for DampingParam {
fn load_param_f(method: &str, atm: bool) -> Result<DFTD3Param, DFTD3Error> {
DFTD3Param::load_damping_f(method, atm)
}
}
#[duplicate_item(
DampingParamBuilder;
[DFTD3RationalDampingParamBuilder];
[DFTD3ZeroDampingParamBuilder];
[DFTD3ModifiedRationalDampingParamBuilder];
[DFTD3ModifiedZeroDampingParamBuilder];
[DFTD3OptimizedPowerDampingParamBuilder];
)]
impl DampingParamBuilder {
pub fn init(self) -> DFTD3Param {
self.init_f().unwrap()
}
pub fn init_f(self) -> Result<DFTD3Param, DFTD3Error> {
self.build()?.new_param_f()
}
}
pub struct DFTD3Output {
pub energy: f64,
pub grad: Option<Vec<f64>>,
pub sigma: Option<Vec<f64>>,
}
impl From<DFTD3Output> for (f64, Option<Vec<f64>>, Option<Vec<f64>>) {
fn from(output: DFTD3Output) -> Self {
(output.energy, output.grad, output.sigma)
}
}
pub struct DFTD3PairwiseOutput {
pub pair_energy2: Vec<f64>,
pub pair_energy3: Vec<f64>,
}
impl From<DFTD3PairwiseOutput> for (Vec<f64>, Vec<f64>) {
fn from(output: DFTD3PairwiseOutput) -> Self {
(output.pair_energy2, output.pair_energy3)
}
}
pub struct DFTD3Model {
ptr: ffi::dftd3_model,
structure: DFTD3Structure,
}
impl Drop for DFTD3Model {
fn drop(&mut self) {
unsafe { ffi::dftd3_delete_model(&mut self.ptr) };
}
}
impl DFTD3Model {
pub fn new(
numbers: &[usize],
positions: &[f64],
lattice: Option<&[f64]>,
periodic: Option<&[bool]>,
) -> Self {
Self::new_f(numbers, positions, lattice, periodic).unwrap()
}
pub fn get_dispersion(&self, param: &DFTD3Param, eval_grad: bool) -> DFTD3Output {
self.get_dispersion_f(param, eval_grad).unwrap()
}
pub fn get_pairwise_dispersion(&self, param: &DFTD3Param) -> DFTD3PairwiseOutput {
self.get_pairwise_dispersion_f(param).unwrap()
}
pub fn set_realspace_cutoff(&self, r0: f64, r1: f64, r2: f64) {
self.set_realspace_cutoff_f(r0, r1, r2).unwrap()
}
pub fn get_natoms(&self) -> usize {
self.structure.get_natoms()
}
pub fn from_structure(structure: DFTD3Structure) -> Self {
Self::from_structure_f(structure).unwrap()
}
pub fn update(&mut self, positions: &[f64], lattice: Option<&[f64]>) {
self.structure.update(positions, lattice)
}
pub fn new_f(
numbers: &[usize],
positions: &[f64],
lattice: Option<&[f64]>,
periodic: Option<&[bool]>,
) -> Result<Self, DFTD3Error> {
let structure = DFTD3Structure::new_f(numbers, positions, lattice, periodic)?;
Self::from_structure_f(structure)
}
pub fn get_dispersion_f(
&self,
param: &DFTD3Param,
eval_grad: bool,
) -> Result<DFTD3Output, DFTD3Error> {
let structure = &self.structure;
let natoms = structure.get_natoms();
let mut energy = 0.0;
let mut grad = match eval_grad {
true => Some(vec![0.0; 3 * natoms]),
false => None,
};
let mut sigma = match eval_grad {
true => Some(vec![0.0; 9]),
false => None,
};
let mut error = DFTD3Error::new();
unsafe {
ffi::dftd3_get_dispersion(
error.get_c_ptr(),
structure.ptr,
self.ptr,
param.ptr,
&mut energy,
grad.as_mut().map_or(null_mut(), |x| x.as_mut_ptr()),
sigma.as_mut().map_or(null_mut(), |x| x.as_mut_ptr()),
)
};
match error.check() {
true => Err(error),
false => Ok(DFTD3Output { energy, grad, sigma }),
}
}
pub fn get_pairwise_dispersion_f(
&self,
param: &DFTD3Param,
) -> Result<DFTD3PairwiseOutput, DFTD3Error> {
let structure = &self.structure;
let natoms = structure.get_natoms();
let mut pair_energy2 = vec![0.0; natoms * natoms];
let mut pair_energy3 = vec![0.0; natoms * natoms];
let mut error = DFTD3Error::new();
unsafe {
ffi::dftd3_get_pairwise_dispersion(
error.get_c_ptr(),
structure.ptr,
self.ptr,
param.ptr,
pair_energy2.as_mut_ptr(),
pair_energy3.as_mut_ptr(),
)
};
match error.check() {
true => Err(error),
false => Ok(DFTD3PairwiseOutput { pair_energy2, pair_energy3 }),
}
}
pub fn set_realspace_cutoff_f(
&self,
disp2: f64,
disp3: f64,
cn: f64,
) -> Result<(), DFTD3Error> {
let mut error = DFTD3Error::new();
unsafe {
ffi::dftd3_set_model_realspace_cutoff(error.get_c_ptr(), self.ptr, disp2, disp3, cn)
};
match error.check() {
true => Err(error),
false => Ok(()),
}
}
pub fn from_structure_f(structure: DFTD3Structure) -> Result<Self, DFTD3Error> {
let mut error = DFTD3Error::new();
let ptr = unsafe { ffi::dftd3_new_d3_model(error.get_c_ptr(), structure.ptr) };
match error.check() {
true => Err(error),
false => Ok(Self { ptr, structure }),
}
}
pub fn update_f(
&mut self,
positions: &[f64],
lattice: Option<&[f64]>,
) -> Result<(), DFTD3Error> {
self.structure.update_f(positions, lattice)
}
}
#[cfg(test)]
mod tests {
use ffi::dftd3_load_optimizedpower_damping;
use super::*;
#[test]
fn test_get_api_version() {
println!("API version: {}", dftd3_get_api_version());
}
#[test]
fn test_get_api_version_compact() {
println!("API version: {:?}", dftd3_get_api_version_compact());
}
#[test]
fn test_dftd3_error() {
let mut error = DFTD3Error::new();
println!("Error check : {}", error.check());
println!("Error message : {}", error.get_message());
let token = std::ffi::CString::new("Hello").unwrap();
unsafe { dftd3_load_optimizedpower_damping(error.get_c_ptr(), token.into_raw(), false) };
println!("Error check : {}", error.check());
println!("Error message : {}", error.get_message());
let token = std::ffi::CString::new("B3LYP").unwrap();
unsafe { dftd3_load_optimizedpower_damping(error.get_c_ptr(), token.into_raw(), false) };
println!("Error check : {}", error.check());
println!("Error message : {}", error.get_message());
}
#[test]
fn test_get_dispersion() {
let numbers = vec![1, 1];
let positions = vec![0.0, 0.0, 0.0, 0.0, 0.0, 1.0];
let model = DFTD3Model::new(&numbers, &positions, None, None);
let param = DFTD3Param::load_mrational_damping("B3LYP", false);
let (energy, grad, sigma) = model.get_dispersion(¶m, true).into();
println!("Dispersion energy: {}", energy);
println!("Dispersion gradient: {:?}", grad);
println!("Dispersion sigma: {:?}", sigma);
}
}