Coverage for dynasor / modes / qpoint.py: 92%
63 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-16 12:31 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-16 12:31 +0000
1import numpy as np
2from numpy.typing import NDArray
4from ..units import radians_per_fs_to_THz
5from .band import Band
8class QPoint:
9 """Representation of a single q-point and properties.
11 The bands can be accessed by, e.g., `qp[2]` to get band index 2 in the form
12 of a :class:`~dynasor.modes.band.Band` object.
14 Parameters
15 ----------
16 q
17 q-point index.
18 mp
19 Mode project object.
20 """
21 def __init__(self, q_index: int, mp):
22 self._mp = mp
23 self._q = q_index
25 def __str__(self):
26 s = ['### q-point ###']
27 s += [f'Index: {self.index}']
28 s += [f'Reduced: {self.q_reduced}']
29 s += ['Cartesian: ['
30 f'{self.q_cartesian[0]:.2f}, {self.q_cartesian[1]:.2f}, {self.q_cartesian[2]:.2f}'
31 '] rad/Å']
32 s += [f'Wavenumber: {self.wavenumber:.2f} rad/Å']
33 s += [f'Wavelength: {self.wavelength:.2f} Å']
34 s += [f'q-minus: {self.q_minus.index} {self.q_minus.q_reduced}']
35 s += [f'Real: {self.is_real}']
36 s += ['']
37 s += ['Omegas: ['
38 ', '.join(f'{t * radians_per_fs_to_THz:.2f}' for t in self.omegas) + '] THz']
39 return '\n'.join(s)
41 def __repr__(self):
42 return str(self)
44 def __getitem__(self, s):
45 if s >= self._mp.primitive.n_atoms * 3:
46 raise IndexError
47 return Band(self.index, s, self._mp)
49 @property
50 def q_minus(self):
51 """The corresponding counter-propagating mode."""
52 return self._mp[self._mp.q_minus[self.index]]
54 @property
55 def polarizations(self) -> NDArray[float]:
56 """Polarization vectors for each band at this q-point, shape ``(Nb, Np, 3)``."""
57 return self._mp.polarizations[self.index]
59 @property
60 def omegas(self) -> NDArray[float]:
61 """Frequencies of each band at this q-point in rad/fs."""
62 return self._mp.omegas[self.index]
64 @property
65 def is_real(self) -> bool:
66 """If the q-point has purely real mode coordinates, `q=-q`."""
67 return self.index == self.q_minus.index
69 @property
70 def index(self) -> int:
71 """q-point index corresponding to :class:`~dynasor.ModeProjector.q_reduced`."""
72 return self._q
74 @property
75 def wavenumber(self) -> float:
76 """Wavenumber of mode in rad/Å. """
77 return np.linalg.norm(self._mp.q_cartesian[self.index])
79 @property
80 def wavelength(self) -> float:
81 """Wavelength of mode in Å. """
82 return np.inf if self.wavenumber == 0 else 2 * np.pi / self.wavenumber
84 @property
85 def q_reduced(self) -> NDArray[float]:
86 """Reduced coordinates of this q-point."""
87 return self._mp.q_reduced[self.index]
89 @property
90 def q_cartesian(self) -> NDArray[float]:
91 """Cartesian coordinates of this q-point in rad/Å (2π included)."""
92 return self._mp.q_cartesian[self.index]
94 @property
95 def eigenmodes(self) -> NDArray[float]:
96 """Eigenmodes in the supercell for each band at this q-point, shape ``(Nb, N, 3)``."""
97 return self._mp.eigenmodes[self.index]
99 @property
100 def potential_energies(self) -> NDArray[float]:
101 """Potential energy per band at this q-point as a ``(Nb,)`` array in eV."""
102 return self._mp.potential_energies[self.index]
104 @property
105 def kinetic_energies(self) -> NDArray[float]:
106 """Kinetic energy per band at this q-point as a ``(Nb,)`` array in eV."""
107 return self._mp.kinetic_energies[self.index]
109 @property
110 def virial_energies(self) -> NDArray[float]:
111 """Virial energy per band at this q-point as a ``(Nb,)`` array in eV."""
112 return self._mp.virial_energies[self.index]