Source code for atompy.physics.particles

import copy
from collections import UserList
from typing import Self, Union

import numpy as np

from atompy import _vectors as vec


class Particle:
    def __init__(
        self,
        mom: vec.VectorLike,
        pos: vec.VectorLike,
        mass: float,
        charge: float,
        name: str,
    ) -> None:
        self._mass = mass
        self._charge = charge
        self._mom = vec.asvector(mom)
        self._pos = vec.asvector(pos)
        self._name = name

    @property
    def pos(self) -> vec.Vector:
        """Position of the particle."""
        return self._pos

    @pos.setter
    def pos(self, pos) -> None:
        self._pos = pos

    @property
    def mass(self) -> float:
        """Mass of the particle."""
        return self._mass

    @mass.setter
    def mass(self, mass) -> None:
        self._mass = mass

    @property
    def charge(self) -> float:
        """Charge of the particle."""
        return self._charge

    @charge.setter
    def charge(self, charge) -> None:
        self._charge = charge

    @property
    def mom(self) -> vec.Vector:
        """Momentum of the particle."""
        return self._mom

    @mom.setter
    def mom(self, vec: vec.Vector) -> None:
        self.mom = vec

    @property
    def speed(self) -> vec.Vector:
        """Speed (derived from its momentum) of the particle."""
        return self.mom / self.mass

    @speed.setter
    def speed(self, speed: vec.Vector) -> None:
        self._mom = speed * self.mass

    @property
    def energy(self) -> float:
        """Energy (derived from its momentum) of the particle."""
        return self.mom.mag() ** 2 / 2.0 / self.mass

    @property
    def name(self) -> str:
        """Descriptor of the particle."""
        return self._name

    @name.setter
    def name(self, name: str) -> None:
        self._name = name


[docs] class Electron(Particle): """ Class describing electrons (as classical particles). .. note:: If you want to use anything but atomic units (a.u.), be sure to pass the (optional) values of *charge* and *mass* (which assume a.u.). Parameters ---------- mom : :class:`.Vector` Momentum of the electron. pos : :class:`.Vector` Position of the electron. mass : float, default 1.0 a.u. Mass of the electron. charge : float, default -1.0 a.u. Charge of the electron. name : str, default ``"e"`` Name of the electron. Attributes ---------- mom : :class:`.Vector` pos : :class:`.Vector` mass : float charge : float speed : :class:`.Vector` energy : float name : str """ def __init__( self, mom: vec.Vector, pos: vec.Vector, mass: float = 1.0, charge: float = -1.0, name="e", ) -> None: super().__init__(mom, pos, mass, charge, name)
[docs] class Atom(Particle): """ Class describing atoms (as classical particles). Parameters ---------- mom : :class:`.Vector` Momentum of the atom. pos : :class:`.Vector` Position of the atom. mass : float Mass of the atom charge : float Charge of the atom. name : str Name of the atom. Attributes ---------- mom : :class:`.Vector` pos : :class:`.Vector` mass : float charge : float speed : :class:`.Vector` energy : float name : str """ pass
class ParticleList(UserList[Atom]): def momenta(self) -> vec.VectorArray: """ Retrieve all momenta of particles in the list. Returns ------- momenta : :class:`.VectorArray` *momenta[i]* corresponds to particle *i* in the list. """ return vec.VectorArray([d.mom for d in self]) def positions(self) -> vec.VectorArray: """ Retrieve all positions of particles in the list. Returns ------- positions : :class:`.VectorArray` *positions[i]* corresponds to particle *i* in the list. """ return vec.VectorArray([d.pos for d in self]) def masses(self) -> np.ndarray[tuple[int], np.dtype[np.float64]]: """ Retrieve all masses of particles in the list. Returns ------- masses : ndarray *masses[i]* corresponds to particle *i* in the list. """ return np.array([d.mass for d in self]) def charges(self) -> np.ndarray[tuple[int], np.dtype[np.float64]]: """ Retrieve all charges of particles in the list. Returns ------- charges : ndarray *charges[i]* corresponds to particle *i* in the list. """ return np.array([d.charge for d in self]) def speeds(self) -> vec.VectorArray: """ Retrieve all speeds of particles in the list. Returns ------- speeds : :class:`.VectorArray` *speeds[i]* corresponds to particle *i* in the list. """ return vec.VectorArray([d.speed for d in self]) def names(self) -> list[str]: """ Retrieve all names of particles in the list. Returns ------- names : ndarray *names[i]* corresponds to particle *i* in the list. """ return [self.data[i].name for i in range(len(self))]
[docs] class ElectronList(ParticleList): """Wrapper for Python lists only containing :class:`.Electron`""" pass
[docs] class AtomList(ParticleList): """Wrapper for Python lists only containing :class:`.Atom`""" pass
AtomListLike = Union[tuple[Atom, ...], list[Atom], AtomList] ElectronListLike = Union[tuple[Electron, ...], list[Electron], ElectronList]
[docs] class Molecule: def __init__(self, atoms: AtomListLike) -> None: self._atoms = AtomList(atoms) @property def size(self) -> int: """Number of atoms within the molecule.""" return len(self._atoms) @property def atoms(self) -> AtomList: """Atoms within the molecule.""" return self._atoms
[docs] def momenta(self) -> vec.VectorArray: """ Retrieve all momenta of atoms in the molecule. Returns ------- momenta : :class:`.VectorArray` *momenta[i]* corresponds to particle *i* in the molecule. """ return self.atoms.momenta()
[docs] def masses(self) -> np.ndarray[tuple[int], np.dtype[np.float64]]: """ Retrieve all masses of atoms in the molecule. Returns ------- masses : ndarray *masses[i]* corresponds to particle *i* in the molecule. """ return self.atoms.masses()
[docs] def speeds(self) -> vec.VectorArray: """ Retrieve all speeds of atoms in the molecule. Returns ------- speeds : :class:`.VectorArray` *speeds[i]* corresponds to particle *i* in the molecule. """ return self.atoms.speeds()
[docs] def positions(self) -> vec.VectorArray: """ Retrieve all positions of atoms in the molecule. Returns ------- positions : :class:`.VectorArray` *positions[i]* corresponds to particle *i* in the molecule. """ return self.atoms.positions()
[docs] def charges(self) -> np.ndarray[tuple[int], np.dtype[np.float64]]: """ Retrieve all charges of atoms in the molecule. Returns ------- charges : ndarray *charges[i]* corresponds to particle *i* in the molecule. """ return self.atoms.charges()
[docs] def names(self) -> list[str]: """ Retrieve all names of atoms in the molecule. Returns ------- names : ndarray *names[i]* corresponds to atom *i* in the molecule. """ return self.atoms.names()
[docs] def copy(self) -> Self: """Retrieve a copy of the molecule.""" return type(self)(copy.deepcopy(self._atoms))
[docs] def mom_sum(self) -> vec.Vector: """ Sum momentum of all atoms in the molecule. Returns ------- momentum : :class:`.Vector` """ mom_sum = vec.Vector(0, 0, 0) mom_sum.x = np.sum(self.momenta().x) mom_sum.y = np.sum(self.momenta().y) mom_sum.z = np.sum(self.momenta().z) return mom_sum
[docs] def kinetic_energy_release(self) -> float: """ Kinetic energy of all atoms of the molecule in rest frame. Returns ------- energy : float The energy in the frame where :meth:`~.Molecule.mom_sum` is zero. """ momenta = self.momenta() - self.mom_sum() kinetic_energies = momenta.mag() ** 2 / self.masses() / 2.0 return float(np.sum(kinetic_energies))