This repository was archived by the owner on Jan 15, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdataStructures.py
More file actions
108 lines (83 loc) · 3.68 KB
/
dataStructures.py
File metadata and controls
108 lines (83 loc) · 3.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
from dataclasses import dataclass
from typing import Generator, List, TypeVar
import numpy as np
from numpy.typing import NDArray
# --- Atom Object ---
@dataclass
class Atom:
index: int
atom_type: int
atom_string: str | None
mass: float | None
position: np.ndarray
unwrapped_position: np.ndarray | None
velocity: np.ndarray
def __str__(self) -> str:
return f"Atom {self.index} of type {self.atom_string} at position {self.position} and velocity {self.velocity}"
def __repr__(self) -> str:
return f"Atom(index={self.index}, atom_type={self.atom_type}, atom_string={self.atom_string}, mass={self.mass}, position={self.position}, unwrapped_position={self.unwrapped_position}, velocity={self.velocity})"
@dataclass
class Frame:
index: int
timestep: float
molecules: List[List[Atom]]
def __str__(self) -> str:
return f"Frame {self.index} at timestep {self.timestep} with {len(self.molecules)} molecules"
def __repr__(self) -> str:
return f"Frame(index={self.index}, timestep={self.timestep}, molecules={self.molecules})"
def __len__(self) -> int:
"""Return the number of atoms in the frame."""
return sum(len(molecule) for molecule in self.molecules)
def __iter__(self):
"""Iterate over all atoms in the frame."""
for molecule in self.molecules:
for atom in molecule:
yield atom
def get_positions(
self, type: str | None = None
) -> Generator[np.ndarray, None, None]:
"""Yield positions of all atoms one by one (memory efficient)."""
for atom in self:
if type is not None:
if atom.atom_string == type:
yield atom.position
else:
yield atom.position
def get_all_positions(self, type: str | None = None) -> np.ndarray:
"""Return a numpy array with all atom positions."""
return np.array(list(self.get_positions(type)))
def get_unwrapped_positions(self) -> Generator[np.ndarray | None, None, None]:
"""Yield unwrapped positions of all atoms one by one (memory efficient)."""
for atom in self:
if atom.unwrapped_position:
yield atom.unwrapped_position
def get_all_unwrapped_positions(self) -> np.ndarray:
"""Return a numpy array with all unwrapped atom positions."""
return np.array(list(self.get_unwrapped_positions()))
def get_velocities(self) -> Generator[np.ndarray, None, None]:
"""Yield positions of all atoms one by one (memory efficient)."""
for atom in self:
yield atom.velocity
def get_all_velocities(self) -> np.ndarray:
"""Return a numpy array with all atom positions."""
return np.array(list(self.get_velocities()))
@dataclass
class Simulation:
n_atoms: int
lattice_string: str | None
cell_vectors: NDArray[np.float64]
properties_string: str | None
def __str__(self) -> str:
return f"Simulation with {self.n_atoms} atoms and lattice {self.lattice_string}"
def __repr__(self) -> str:
return f"Simulation(n_atoms={self.n_atoms}, lattice_string={self.lattice_string}, cell_vectors={self.cell_vectors}, properties_string={self.properties_string})"
def __len__(self) -> int:
"""Return the number of atoms in the simulation."""
return self.n_atoms
def calculate_volume(self) -> float:
"""Calculate the volume of the simulation cell."""
return np.linalg.det(self.cell_vectors)
AtomType = TypeVar("AtomType", bound="Atom")
SimulationType = TypeVar("SimulationType", bound="Simulation")
FrameType = TypeVar("FrameType", bound="Frame")
MoleculeType = List[AtomType]