use integral_core::os::{Prim, MAX_L};
use integral_math::am::n_cart;
use integral_math::norm::cart_norm;
use crate::IntegralError;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ShellKind {
#[default]
Cartesian,
Spherical,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Shell {
l: usize,
center: [f64; 3],
exponents: Vec<f64>,
coefficients: Vec<f64>,
kind: ShellKind,
}
impl Shell {
pub fn new(
l: usize,
center: [f64; 3],
exponents: Vec<f64>,
coefficients: Vec<f64>,
) -> Result<Self, IntegralError> {
Self::with_kind(l, center, exponents, coefficients, ShellKind::Cartesian)
}
pub fn new_spherical(
l: usize,
center: [f64; 3],
exponents: Vec<f64>,
coefficients: Vec<f64>,
) -> Result<Self, IntegralError> {
Self::with_kind(l, center, exponents, coefficients, ShellKind::Spherical)
}
pub fn with_kind(
l: usize,
center: [f64; 3],
exponents: Vec<f64>,
coefficients: Vec<f64>,
kind: ShellKind,
) -> Result<Self, IntegralError> {
if exponents.is_empty() {
return Err(IntegralError::EmptyContraction);
}
if exponents.len() != coefficients.len() {
return Err(IntegralError::MismatchedContraction {
exponents: exponents.len(),
coefficients: coefficients.len(),
});
}
if l > MAX_L {
return Err(IntegralError::AngularMomentumTooHigh { l, max: MAX_L });
}
Ok(Shell {
l,
center,
exponents,
coefficients,
kind,
})
}
#[must_use]
pub fn l(&self) -> usize {
self.l
}
#[must_use]
pub fn kind(&self) -> ShellKind {
self.kind
}
#[must_use]
pub fn center(&self) -> [f64; 3] {
self.center
}
#[must_use]
pub fn exponents(&self) -> &[f64] {
&self.exponents
}
#[must_use]
pub fn coefficients(&self) -> &[f64] {
&self.coefficients
}
#[must_use]
pub fn n_cart(&self) -> usize {
n_cart(self.l)
}
#[must_use]
pub fn n_func(&self) -> usize {
match self.kind {
ShellKind::Cartesian => n_cart(self.l),
ShellKind::Spherical => 2 * self.l + 1,
}
}
#[must_use]
pub fn n_prim(&self) -> usize {
self.exponents.len()
}
#[must_use]
pub fn primitive_coeff(&self, i: usize) -> f64 {
self.coefficients[i] * cart_norm(self.exponents[i], self.l, 0, 0)
}
#[must_use]
pub(crate) fn prim(&self, i: usize) -> Prim {
Prim::new(self.exponents[i], self.center, self.l)
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Basis {
shells: Vec<Shell>,
}
impl Basis {
#[must_use]
pub fn new(shells: Vec<Shell>) -> Self {
Basis { shells }
}
#[must_use]
pub fn shells(&self) -> &[Shell] {
&self.shells
}
#[must_use]
pub fn nao_cart(&self) -> usize {
self.shells.iter().map(Shell::n_cart).sum()
}
#[must_use]
pub fn nao(&self) -> usize {
self.shells.iter().map(Shell::n_func).sum()
}
#[must_use]
pub(crate) fn offsets(&self) -> Vec<usize> {
let mut offs = Vec::with_capacity(self.shells.len());
let mut acc = 0;
for s in &self.shells {
offs.push(acc);
acc += s.n_func();
}
offs
}
#[must_use]
pub fn atoms(&self) -> Vec<[f64; 3]> {
let mut atoms: Vec<[f64; 3]> = Vec::new();
for s in &self.shells {
if !atoms.contains(&s.center) {
atoms.push(s.center);
}
}
atoms
}
#[must_use]
pub(crate) fn shell_atom(&self) -> Vec<usize> {
let atoms = self.atoms();
self.shells
.iter()
.map(|s| atoms.iter().position(|c| *c == s.center).unwrap())
.collect()
}
#[must_use]
pub(crate) fn atom_at(&self, center: [f64; 3]) -> Option<usize> {
self.atoms().iter().position(|c| *c == center)
}
}