Coverage for strongcoca/response/excitations.py: 100%
50 statements
« prev ^ index » next coverage.py v7.10.6, created at 2025-10-26 18:44 +0000
« prev ^ index » next coverage.py v7.10.6, created at 2025-10-26 18:44 +0000
1import numpy as np
3from .base import BaseResponse
4from ..types import Array
5from ..units import au_to_eV, eV_to_au, au_to_eA, eA_to_au
6from .utilities import Broadening, NoArtificialBroadening, broaden
9class Excitations(BaseResponse):
10 """Objects of this class represent discrete excitation spectra.
12 Parameters
13 ----------
14 energies
15 Excitation energies; array with shape {n}.
17 Units are eV by default, optionally atomic units (see :attr:`units`).
18 transition_dipole_moments
19 Transition dipole moments of the excitations; array with shape {n}x{3}.
21 Units are eÅ by default, optionally atomic units (see :attr:`units`).
22 broadening
23 Artificial broadening used for the continuous response;
24 defaults to no broadening.
25 units
26 `eVA` to specify energies in eV and transition_dipole_moments in eÅ
27 or `au` to specify inputs in atomic units.
29 This parameter determines whether conversion should be performed during
30 initialization and has no effect on instance methods and variables.
31 name
32 Name of response.
33 """
34 def __init__(self,
35 energies: np.ndarray,
36 transition_dipole_moments: np.ndarray,
37 broadening: Broadening = NoArtificialBroadening(),
38 units: str = 'eVA',
39 name: str = 'Excitations') -> None:
40 super().__init__(broadening=broadening, pbc=False, name=name)
42 if units == 'eVA':
43 energies = energies * eV_to_au
44 transition_dipole_moments = transition_dipole_moments * eA_to_au
45 elif units != 'au':
46 raise ValueError(f"units has to be 'eVA' or 'au', not '{units}'")
48 self._energies = energies
49 self._transition_dipole_moments = transition_dipole_moments
51 @property
52 def energies(self) -> np.ndarray:
53 """Excitation energies in units of eV; array with shape {n}."""
54 return self._energies * au_to_eV # type: ignore
56 @property
57 def transition_dipole_moments(self) -> np.ndarray:
58 """Transition dipole moments of the excitations in units of eÅ;
59 array with shape {n}x{3}.
60 """
61 return self._transition_dipole_moments * au_to_eA # type: ignore
63 @property
64 def oscillator_strengths(self) -> np.ndarray:
65 """Unitless oscillator strengths of the excitations;
66 array with shape {n}.
67 """
68 osc_nv = self.oscillator_strength_vectors
69 osc_n = np.average(osc_nv, axis=1)
70 return osc_n # type: ignore
72 @property
73 def oscillator_strength_vectors(self) -> np.ndarray:
74 """Unitless oscillator strength vectors of the excitations;
75 array with shape {n}x3.
76 """
77 omega_n = self._energies
78 mu_nv = self._transition_dipole_moments
79 osc_nv = 2 * omega_n[:, np.newaxis] * mu_nv**2
80 return osc_nv # type: ignore
82 @property
83 def oscillator_strength_tensors(self) -> np.ndarray:
84 """Unitless oscillator strength tensors of the excitations;
85 array with shape {n}x3x3.
86 """
87 omega_n = self._energies
88 mu_nv = self._transition_dipole_moments
89 osc_nvv = 2 * np.einsum('n,nx,ny->nxy', omega_n, mu_nv, mu_nv, optimize=True)
90 return osc_nvv # type: ignore
92 def _get_dynamic_polarizability(self, frequencies: Array) -> np.ndarray:
93 freq_w = np.asarray(frequencies)
94 omega_I = self._energies
95 osc_Ivv = self.oscillator_strength_tensors
96 dm_wvv = broaden(omega_I, osc_Ivv, freq_w, broadening=self._broadening)
97 return dm_wvv
99 def _get_dynamic_polarizability_imaginary_frequency(
100 self, frequencies: Array) -> np.ndarray:
101 freq_w = np.asarray(frequencies)
102 omega_I = self._energies
103 osc_Ivv = self.oscillator_strength_tensors
104 dm_wvv = broaden(omega_I, osc_Ivv, freq_w, freq_axis='imag')
105 return dm_wvv