use crate::prelude::*;
use crate::ffi::cecp_wrapper::get_ecp_integrator;
use crate::ffi::cint_wrapper::get_cint_integrator;
#[derive(Debug, Clone, PartialEq)]
pub struct CInt {
pub atm: Vec<[c_int; 6]>,
pub bas: Vec<[c_int; 8]>,
pub ecpbas: Vec<[c_int; 8]>,
pub env: Vec<f64>,
pub cint_type: CIntType,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CIntKind {
Int,
Ecp,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum CIntType {
#[default]
Spheric,
Cartesian,
Spinor,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum CIntSymm {
#[default]
S1,
S2ij,
S2kl,
S4,
S8,
}
#[derive(Debug, PartialEq)]
pub enum CIntOptimizer {
Int(*mut CINTOpt),
Ecp(*mut ECPOpt),
}
pub struct CIntOutput<F> {
pub out: Option<Vec<F>>,
pub shape: Vec<usize>,
}
#[derive(Builder, Debug)]
#[builder(pattern = "owned", build_fn(error = "CIntError"))]
pub struct IntegrateArgs<'l, F> {
#[builder(setter(into))]
pub intor: &'l str,
#[builder(default = "&[]")]
pub shls_slice: &'l [[usize; 2]],
#[builder(default, setter(into))]
pub aosym: CIntSymm,
#[builder(default, setter(strip_option))]
pub out: Option<&'l mut [F]>,
#[builder(default = false)]
pub row_major: bool,
#[builder(default, setter(strip_option))]
pub grids: Option<&'l [[f64; 3]]>,
}
#[derive(Builder, Debug)]
#[builder(pattern = "owned", build_fn(error = "CIntError"))]
pub struct IntorCrossArgs<'l, F> {
#[builder(setter(into))]
pub intor: &'l str,
pub mols: &'l [&'l CInt],
#[builder(default = "&[]")]
pub shls_slice: &'l [[usize; 2]],
#[builder(default, setter(into))]
pub aosym: CIntSymm,
#[builder(default, setter(strip_option))]
pub out: Option<&'l mut [F]>,
#[builder(default = false)]
pub row_major: bool,
}
pub fn get_integrator(intor: &str) -> Box<dyn Integrator> {
get_integrator_f(intor).cint_unwrap()
}
pub fn get_integrator_f(intor: &str) -> Result<Box<dyn Integrator>, CIntError> {
let intor = if let Some(intor) = get_cint_integrator(&intor.to_lowercase()) {
Ok(intor)
} else if let Some(intor) = get_ecp_integrator(&intor.to_lowercase()) {
Ok(intor)
} else {
cint_raise!(IntegratorNotFound, "{intor}")
}?;
Ok(intor)
}
impl From<&str> for CIntType {
#[inline]
fn from(cint_type: &str) -> Self {
match cint_type.to_lowercase().as_str() {
"sph" | "spheric" | "spherical" => CIntType::Spheric,
"cart" | "cartesian" => CIntType::Cartesian,
"spinor" => CIntType::Spinor,
_ => panic!("Unknown integral type: {cint_type}"),
}
}
}
impl From<&str> for CIntSymm {
#[inline]
fn from(aosym: &str) -> Self {
match aosym {
"S1" | "s1" => CIntSymm::S1,
"S2ij" | "s2ij" | "S2uv" | "s2uv" => CIntSymm::S2ij,
"S2kl" | "s2kl" => CIntSymm::S2kl,
"S4" | "s4" => CIntSymm::S4,
"S8" | "s8" => CIntSymm::S8,
_ => panic!("Unknown symmetry: {aosym}"),
}
}
}
impl From<Option<&str>> for CIntSymm {
#[inline]
fn from(aosym: Option<&str>) -> Self {
aosym.unwrap_or("s1").into()
}
}
unsafe impl Send for CIntOptimizer {}
unsafe impl Sync for CIntOptimizer {}
impl Drop for CIntOptimizer {
fn drop(&mut self) {
match self {
Self::Int(opt) => unsafe { cint_ffi::CINTdel_optimizer(opt) },
Self::Ecp(opt) => unsafe { cecp_ffi::ECPdel_optimizer(opt) },
}
}
}
impl CIntOptimizer {
#[inline]
pub fn as_ptr(&self) -> *const c_void {
match self {
Self::Int(opt) => *opt as *const c_void,
Self::Ecp(opt) => *opt as *const c_void,
}
}
}
impl<F> From<CIntOutput<F>> for (Vec<F>, Vec<usize>) {
fn from(output: CIntOutput<F>) -> Self {
if output.out.is_none() {
panic!(
"You have called integral with output buffer, and should not call `into` to get the integral buffer, but use your mutable reference instead."
);
}
(output.out.unwrap_or_default(), output.shape)
}
}
impl CInt {
pub fn integrate(&self, intor: &str, aosym: impl Into<CIntSymm>, shls_slice: impl Into<ShlsSlice>) -> CIntOutput<f64> {
self.integrate_f(intor, aosym, shls_slice.into()).cint_unwrap()
}
pub fn integrate_spinor(&self, intor: &str, aosym: impl Into<CIntSymm>, shls_slice: impl Into<ShlsSlice>) -> CIntOutput<Complex<f64>> {
self.integrate_spinor_f(intor, aosym, shls_slice.into()).cint_unwrap()
}
pub fn integrate_f(
&self,
intor: &str,
aosym: impl Into<CIntSymm>,
shls_slice: impl Into<ShlsSlice>,
) -> Result<CIntOutput<f64>, CIntError> {
let shls_slice = shls_slice.into();
let integrate_args = self.integrate_args_builder().intor(intor).aosym(aosym).shls_slice(shls_slice.as_ref()).build()?;
self.integrate_with_args_inner(integrate_args)
}
pub fn integrate_spinor_f(
&self,
intor: &str,
aosym: impl Into<CIntSymm>,
shls_slice: impl Into<ShlsSlice>,
) -> Result<CIntOutput<Complex<f64>>, CIntError> {
let shls_slice = shls_slice.into();
let integrate_args = self.integrate_args_builder_spinor().intor(intor).aosym(aosym).shls_slice(shls_slice.as_ref()).build()?;
self.integrate_with_args_inner(integrate_args)
}
pub fn integrate_cross<'l>(
intor: &str,
mols: impl AsRef<[&'l CInt]>,
aosym: impl Into<CIntSymm>,
shls_slice: impl Into<ShlsSlice>,
) -> CIntOutput<f64> {
CInt::integrate_cross_f(intor, mols, aosym, shls_slice).cint_unwrap()
}
pub fn integrate_cross_f<'l>(
intor: &str,
mols: impl AsRef<[&'l CInt]>,
aosym: impl Into<CIntSymm>,
shls_slice: impl Into<ShlsSlice>,
) -> Result<CIntOutput<f64>, CIntError> {
let shls_slice = shls_slice.into();
let args = IntorCrossArgsBuilder::default()
.intor(intor)
.mols(mols.as_ref())
.shls_slice(shls_slice.as_ref())
.aosym(aosym.into())
.build()?;
CInt::integrate_cross_with_args_inner(args)
}
pub fn integrate_cross_spinor<'l>(
intor: &str,
mols: impl AsRef<[&'l CInt]>,
aosym: impl Into<CIntSymm>,
shls_slice: impl Into<ShlsSlice>,
) -> CIntOutput<Complex<f64>> {
CInt::integrate_cross_spinor_f(intor, mols, aosym, shls_slice).cint_unwrap()
}
pub fn integrate_cross_spinor_f<'l>(
intor: &str,
mols: impl AsRef<[&'l CInt]>,
aosym: impl Into<CIntSymm>,
shls_slice: impl Into<ShlsSlice>,
) -> Result<CIntOutput<Complex<f64>>, CIntError> {
let shls_slice = shls_slice.into();
let args = IntorCrossArgsBuilder::default()
.intor(intor)
.mols(mols.as_ref())
.shls_slice(shls_slice.as_ref())
.aosym(aosym.into())
.build()?;
CInt::integrate_cross_with_args_inner(args)
}
pub fn integrate_row_major(&self, intor: &str, aosym: impl Into<CIntSymm>, shls_slice: impl Into<ShlsSlice>) -> CIntOutput<f64> {
self.integrate_row_major_f(intor, aosym, shls_slice.into()).cint_unwrap()
}
pub fn integrate_row_major_f(
&self,
intor: &str,
aosym: impl Into<CIntSymm>,
shls_slice: impl Into<ShlsSlice>,
) -> Result<CIntOutput<f64>, CIntError> {
let shls_slice = shls_slice.into();
let integrate_args =
self.integrate_args_builder().intor(intor).aosym(aosym).shls_slice(shls_slice.as_ref()).row_major(true).build()?;
self.integrate_with_args_inner(integrate_args)
}
pub fn integrate_row_major_spinor(
&self,
intor: &str,
aosym: impl Into<CIntSymm>,
shls_slice: impl Into<ShlsSlice>,
) -> CIntOutput<Complex<f64>> {
self.integrate_row_major_spinor_f(intor, aosym, shls_slice.into()).cint_unwrap()
}
pub fn integrate_row_major_spinor_f(
&self,
intor: &str,
aosym: impl Into<CIntSymm>,
shls_slice: impl Into<ShlsSlice>,
) -> Result<CIntOutput<Complex<f64>>, CIntError> {
let shls_slice = shls_slice.into();
let integrate_args =
self.integrate_args_builder_spinor().intor(intor).aosym(aosym).shls_slice(shls_slice.as_ref()).row_major(true).build()?;
self.integrate_with_args_inner(integrate_args)
}
pub fn integrate_cross_row_major<'l>(
intor: &str,
mols: impl AsRef<[&'l CInt]>,
aosym: impl Into<CIntSymm>,
shls_slice: impl Into<ShlsSlice>,
) -> CIntOutput<f64> {
CInt::integrate_cross_row_major_f(intor, mols, aosym, shls_slice).cint_unwrap()
}
pub fn integrate_cross_row_major_f<'l>(
intor: &str,
mols: impl AsRef<[&'l CInt]>,
aosym: impl Into<CIntSymm>,
shls_slice: impl Into<ShlsSlice>,
) -> Result<CIntOutput<f64>, CIntError> {
let shls_slice = shls_slice.into();
let args = IntorCrossArgsBuilder::default()
.intor(intor)
.mols(mols.as_ref())
.shls_slice(shls_slice.as_ref())
.aosym(aosym.into())
.row_major(true)
.build()?;
CInt::integrate_cross_with_args_inner(args)
}
pub fn integrate_cross_row_major_spinor<'l>(
intor: &str,
mols: impl AsRef<[&'l CInt]>,
aosym: impl Into<CIntSymm>,
shls_slice: impl Into<ShlsSlice>,
) -> CIntOutput<Complex<f64>> {
CInt::integrate_cross_row_major_spinor_f(intor, mols, aosym, shls_slice).cint_unwrap()
}
pub fn integrate_cross_row_major_spinor_f<'l>(
intor: &str,
mols: impl AsRef<[&'l CInt]>,
aosym: impl Into<CIntSymm>,
shls_slice: impl Into<ShlsSlice>,
) -> Result<CIntOutput<Complex<f64>>, CIntError> {
let shls_slice = shls_slice.into();
let args = IntorCrossArgsBuilder::default()
.intor(intor)
.mols(mols.as_ref())
.shls_slice(shls_slice.as_ref())
.aosym(aosym.into())
.row_major(true)
.build()?;
CInt::integrate_cross_with_args_inner(args)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cint_data() {
let cint_data = init_h2o_def2_tzvp();
let opt = cint_data.optimizer("int2e");
println!("int2e opt: {opt:?}");
if let CIntOptimizer::Int(opt) = opt {
println!("int2e opt inner {:?}", unsafe { opt.read() });
}
}
}