use serde::*;
use gchemol_graph::*;
use gchemol_gut::prelude::*;
use bimap::BiHashMap;
use gchemol_lattice::Lattice;
use crate::atom::*;
use crate::bond::*;
use crate::element::*;
use crate::property::PropertyStore;
type MolGraph = NxGraph<Atom, Bond>;
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
pub struct Molecule {
pub properties: PropertyStore,
pub lattice: Option<Lattice>,
pub(crate) name: String,
pub(crate) graph: MolGraph,
pub(crate) mapping: BiHashMap<usize, NodeIndex>,
}
impl Molecule {
fn node_index(&self, sn: usize) -> NodeIndex {
*self
.get_node_index(sn)
.expect(&format!("invalid atom sn: {}", sn))
}
fn get_node_index(&self, sn: usize) -> Option<&NodeIndex> {
self.mapping.get_by_left(&sn)
}
fn atom_sn(&self, n: NodeIndex) -> usize {
*self
.mapping
.get_by_right(&n)
.expect(&format!("invalid NodeIndex: {:?}", n))
}
fn remove_atom_sn(&mut self, sn: usize) -> Option<NodeIndex> {
self.mapping.remove_by_left(&sn).map(|(_, n)| n)
}
}
impl Molecule {
pub fn new(name: &str) -> Self {
Molecule {
name: name.to_string(),
..Default::default()
}
}
pub fn add_atom(&mut self, a: usize, atom: Atom) {
if let Some(&n) = self.mapping.get_by_left(&a) {
self.graph[n] = atom;
} else {
let n = self.graph.add_node(atom);
self.mapping.insert(a, n);
}
}
pub fn remove_atom(&mut self, a: usize) -> Option<Atom> {
if let Some(n) = self.remove_atom_sn(a) {
self.graph.remove_node(n)
} else {
None
}
}
pub fn natoms(&self) -> usize {
self.graph.number_of_nodes()
}
pub fn nbonds(&self) -> usize {
self.graph.number_of_edges()
}
pub fn add_bond(&mut self, a: usize, b: usize, bond: Bond) {
let na = self.node_index(a);
let nb = self.node_index(b);
self.graph.add_edge(na, nb, bond);
}
pub fn remove_bond(&mut self, a: usize, b: usize) -> Option<Bond> {
let na = self.node_index(a);
let nb = self.node_index(b);
self.graph.remove_edge(na, nb)
}
pub fn clear(&mut self) {
self.mapping.clear();
self.graph.clear();
}
pub fn atoms(&self) -> impl Iterator<Item = (usize, &Atom)> {
self.serial_numbers().map(move |sn| {
let n = self.node_index(sn);
let atom = &self.graph[n];
(sn, atom)
})
}
pub fn bonds(&self) -> impl Iterator<Item = (usize, usize, &Bond)> {
self.graph.edges().map(move |(u, v, bond)| {
let sn1 = self.atom_sn(u);
let sn2 = self.atom_sn(v);
(sn1, sn2, bond)
})
}
pub fn serial_numbers(&self) -> impl Iterator<Item = usize> {
self.mapping.left_values().copied().sorted()
}
pub fn symbols(&self) -> impl Iterator<Item = &str> {
self.atoms().map(move |(_, atom)| atom.symbol())
}
pub fn numbers(&self) -> impl Iterator<Item = usize> + '_ {
self.atoms().map(move |(_, atom)| atom.number())
}
pub fn positions(&self) -> impl Iterator<Item = Point3> + '_ {
self.atoms().map(move |(_, atom)| atom.position())
}
pub fn title(&self) -> String {
let tlines: Vec<_> = self.name.lines().collect();
if tlines.is_empty() {
"untitled".to_owned()
} else {
tlines[0].trim().to_owned()
}
}
}
impl Molecule {
pub fn get_atom(&self, sn: usize) -> Option<&Atom> {
self.get_node_index(sn).map(|&n| &self.graph[n])
}
pub fn get_atom_mut(&mut self, sn: usize) -> Option<&mut Atom> {
if let Some(&n) = self.get_node_index(sn) {
Some(&mut self.graph[n])
} else {
None
}
}
pub fn get_bond(&self, sn1: usize, sn2: usize) -> Option<&Bond> {
if let Some(&n1) = self.get_node_index(sn1) {
if let Some(&n2) = self.get_node_index(sn2) {
return Some(&self.graph[(n1, n2)]);
}
}
None
}
pub fn get_bond_mut(&mut self, sn1: usize, sn2: usize) -> Option<&mut Bond> {
if let Some(&n1) = self.get_node_index(sn1) {
if let Some(&n2) = self.get_node_index(sn2) {
return Some(&mut self.graph[(n1, n2)]);
}
}
None
}
pub fn set_position<P: Into<Vector3f>>(&mut self, sn: usize, position: P) {
let atom = self.get_atom_mut(sn).expect("invalid atom serial number");
atom.set_position(position);
}
pub fn set_symbol<S: Into<AtomKind>>(&mut self, sn: usize, sym: S) {
let atom = self.get_atom_mut(sn).expect("invalid atom serial number");
atom.set_symbol(sym);
}
pub fn add_atoms_from<T, P>(&mut self, atoms: T)
where
T: IntoIterator<Item = (usize, P)>,
P: Into<Atom>,
{
for (n, a) in atoms {
self.add_atom(n, a.into());
}
}
pub fn from_atoms<T>(atoms: T) -> Self
where
T: IntoIterator,
T::Item: Into<Atom>,
{
let mut mol = Self::default();
for (i, a) in atoms.into_iter().enumerate() {
mol.add_atom(i + 1, a.into());
}
mol
}
pub fn set_title(&mut self, title: &str) {
self.name = title.to_owned();
}
pub fn add_bonds_from<T>(&mut self, bonds: T)
where
T: IntoIterator<Item = (usize, usize, Bond)>,
{
for (u, v, b) in bonds {
self.add_bond(u, v, b);
}
}
pub fn set_positions<T, P>(&mut self, positions: T)
where
T: IntoIterator<Item = P>,
P: Into<Vector3f>,
{
for (sn, p) in self.serial_numbers().zip(positions.into_iter()) {
let atom = self.get_atom_mut(sn).unwrap();
atom.set_position(p);
}
}
pub fn set_symbols<T, S>(&mut self, symbols: T)
where
T: IntoIterator<Item = S>,
S: Into<AtomKind>,
{
for (sn, sy) in self.serial_numbers().zip(symbols.into_iter()) {
let atom = self.get_atom_mut(sn).unwrap();
atom.set_symbol(sy);
}
}
pub fn remove_atoms_from(&mut self) {
unimplemented!()
}
pub fn remove_bonds_from(&mut self) {
unimplemented!()
}
}
#[test]
fn test() {
let mut mol = Molecule::new("test");
for i in 0..5 {
mol.add_atom(i, Atom::default());
}
assert_eq!(mol.natoms(), 5);
mol.add_bond(1, 2, Bond::single());
mol.add_bond(2, 3, Bond::double());
assert_eq!(mol.nbonds(), 2);
mol.add_bond(2, 1, Bond::single());
assert_eq!(mol.nbonds(), 2);
for (i, a) in mol.atoms() {
dbg!((i, a.symbol()));
}
}