use super::periodic_table::{ELEMENT_MASS, ELEMENT_NAME, ELEMENT_NAME_UPPER, ELEMENT_VDW};
use tinystr::TinyAsciiStr;
pub type AtomStr = TinyAsciiStr<8>;
pub(crate) const ATOM_NAME_EXPECT: &str = "atom name fits in 8 bytes";
pub(crate) const ATOM_RESNAME_EXPECT: &str = "residue name fits in 8 bytes";
pub(crate) const ATOM_TYPE_NAME_EXPECT: &str = "atom type name fits in 8 bytes";
pub trait AtomLike {
fn get_name(&self) -> &str;
fn set_name(&mut self, name: &str);
fn get_resname(&self) -> &str;
fn set_resname(&mut self, resname: &str);
fn get_resid(&self) -> isize;
fn set_resid(&mut self, resid: isize);
fn get_resindex(&self) -> usize;
fn set_resindex(&mut self, resindex: usize);
fn get_atomic_number(&self) -> u8;
fn set_atomic_number(&mut self, atomic_number: u8);
fn get_mass(&self) -> f32;
fn set_mass(&mut self, mass: f32);
fn get_charge(&self) -> f32;
fn set_charge(&mut self, charge: f32);
fn get_type_name(&self) -> &str;
fn set_type_name(&mut self, type_name: &str);
fn get_type_id(&self) -> u32;
fn set_type_id(&mut self, type_id: u32);
fn get_chain(&self) -> char;
fn set_chain(&mut self, chain: char);
fn get_bfactor(&self) -> f32;
fn set_bfactor(&mut self, bfactor: f32);
fn get_occupancy(&self) -> f32;
fn set_occupancy(&mut self, occupancy: f32);
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct Atom {
pub name: AtomStr,
pub resname: AtomStr,
pub resid: i32, pub resindex: usize,
pub atomic_number: u8,
pub mass: f32,
pub charge: f32,
pub type_name: AtomStr,
pub type_id: u32,
pub chain: char,
pub bfactor: f32,
pub occupancy: f32,
}
impl Default for Atom {
fn default() -> Self {
let empty = AtomStr::try_from_str("").unwrap();
Atom {
name: empty,
resname: empty,
type_name: empty,
resid: 0,
resindex: 0,
atomic_number: 0,
mass: 0.0,
charge: 0.0,
type_id: 0,
chain: ' ',
bfactor: 0.0,
occupancy: 1.0,
}
}
}
impl Atom {
pub fn new() -> Self {
Default::default()
}
pub fn with_name(mut self, name: &str) -> Self {
self.name = AtomStr::try_from_str(name).expect(ATOM_NAME_EXPECT);
self
}
pub fn with_resname(mut self, resname: &str) -> Self {
self.resname = AtomStr::try_from_str(resname).expect(ATOM_RESNAME_EXPECT);
self
}
pub fn with_resid(mut self, resid: i32) -> Self { self.resid = resid; self }
pub fn with_resindex(mut self, resindex: usize) -> Self { self.resindex = resindex; self }
pub fn with_atomic_number(mut self, n: u8) -> Self { self.atomic_number = n; self }
pub fn with_mass(mut self, mass: f32) -> Self { self.mass = mass; self }
pub fn with_charge(mut self, charge: f32) -> Self { self.charge = charge; self }
pub fn with_type_name(mut self, type_name: &str) -> Self {
self.type_name = AtomStr::try_from_str(type_name).expect(ATOM_TYPE_NAME_EXPECT);
self
}
pub fn with_type_id(mut self, type_id: u32) -> Self { self.type_id = type_id; self }
pub fn with_chain(mut self, chain: char) -> Self { self.chain = chain; self }
pub fn with_bfactor(mut self, bfactor: f32) -> Self { self.bfactor = bfactor; self }
pub fn with_occupancy(mut self, occupancy: f32) -> Self { self.occupancy = occupancy; self }
pub fn guess(mut self) -> Self {
self.guess_element_and_mass_from_name();
self
}
pub fn hydrogen() -> Self { Atom::new().with_name("H").guess() }
pub fn carbon() -> Self { Atom::new().with_name("C").guess() }
pub fn nitrogen() -> Self { Atom::new().with_name("N").guess() }
pub fn oxygen() -> Self { Atom::new().with_name("O").guess() }
pub fn phosphorus() -> Self { Atom::new().with_name("P").guess() }
pub fn sulfur() -> Self { Atom::new().with_name("S").guess() }
pub fn guess_element_from_name(&mut self) {
self.atomic_number = 0;
if let Some(i) = self.name.find(|c: char| c.is_ascii_alphabetic()) {
match self.name.as_str() {
"SOD" => self.atomic_number = 11, "POT" => self.atomic_number = 19, _ => (),
}
if self.atomic_number == 0 && i + 1 < self.name.len() {
let c2 = self.name[i..=i + 1].to_ascii_uppercase();
for an in 1..ELEMENT_NAME_UPPER.len() {
let el = ELEMENT_NAME_UPPER[an];
if el.len() == 2 && el == c2 {
match el.chars().next().unwrap() {
'C' | 'N' | 'O' | 'H' | 'P' => {
if self.name == self.resname {
self.atomic_number = an as u8;
}
}
_ => {
self.atomic_number = an as u8;
}
}
}
}
}
if self.atomic_number == 0 {
for an in 1..ELEMENT_NAME.len() {
let el = ELEMENT_NAME[an];
if el.len() == 1 && el == &self.name[i..=i] {
self.atomic_number = an as u8;
}
}
}
}
}
pub fn guess_element_and_mass_from_name(&mut self) {
self.guess_element_from_name();
self.mass = ELEMENT_MASS[self.atomic_number as usize];
}
pub fn vdw(&self) -> f32 {
ELEMENT_VDW[self.atomic_number as usize] * 0.1
}
}
pub(crate) fn element_symbol(atomic_number: u8) -> &'static str {
ELEMENT_NAME_UPPER
.get(atomic_number as usize)
.copied()
.unwrap_or("")
}
impl<T: AtomLike> From<&T> for Atom {
fn from(a: &T) -> Self {
Atom::new()
.with_name(a.get_name())
.with_resname(a.get_resname())
.with_resid(a.get_resid() as i32)
.with_resindex(a.get_resindex())
.with_atomic_number(a.get_atomic_number())
.with_mass(a.get_mass())
.with_charge(a.get_charge())
.with_type_name(a.get_type_name())
.with_type_id(a.get_type_id())
.with_chain(a.get_chain())
.with_bfactor(a.get_bfactor())
.with_occupancy(a.get_occupancy())
}
}
impl AtomLike for Atom {
fn get_name(&self) -> &str {
self.name.as_str()
}
fn set_name(&mut self, name: &str) {
self.name = AtomStr::try_from_str(name).expect(ATOM_NAME_EXPECT);
}
fn get_resname(&self) -> &str {
self.resname.as_str()
}
fn set_resname(&mut self, resname: &str) {
self.resname = AtomStr::try_from_str(resname).expect(ATOM_RESNAME_EXPECT);
}
fn get_resid(&self) -> isize {
self.resid as isize
}
fn set_resid(&mut self, resid: isize) {
self.resid = resid as i32
}
fn get_resindex(&self) -> usize {
self.resindex
}
fn set_resindex(&mut self, resindex: usize) {
self.resindex = resindex;
}
fn get_atomic_number(&self) -> u8 {
self.atomic_number
}
fn set_atomic_number(&mut self, atomic_number: u8) {
self.atomic_number = atomic_number;
}
fn get_mass(&self) -> f32 {
self.mass
}
fn set_mass(&mut self, mass: f32) {
self.mass = mass;
}
fn get_charge(&self) -> f32 {
self.charge
}
fn set_charge(&mut self, charge: f32) {
self.charge = charge;
}
fn get_type_name(&self) -> &str {
self.type_name.as_str()
}
fn set_type_name(&mut self, type_name: &str) {
self.type_name = AtomStr::try_from_str(type_name).expect(ATOM_TYPE_NAME_EXPECT);
}
fn get_type_id(&self) -> u32 {
self.type_id
}
fn set_type_id(&mut self, type_id: u32) {
self.type_id = type_id;
}
fn get_chain(&self) -> char {
self.chain
}
fn set_chain(&mut self, chain: char) {
self.chain = chain;
}
fn get_bfactor(&self) -> f32 {
self.bfactor
}
fn set_bfactor(&mut self, bfactor: f32) {
self.bfactor = bfactor;
}
fn get_occupancy(&self) -> f32 {
self.occupancy
}
fn set_occupancy(&mut self, occupancy: f32) {
self.occupancy = occupancy;
}
}