Coverage for strongcoca/response/base.py: 100%
61 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 logging
2from abc import ABC, abstractmethod
3from typing import Optional
5import numpy as np
6from ase import Atoms
8from .utilities import Broadening
9from ..types import Array
10from ..units import au_to_eV, eV_to_au, au_to_eA
11from ..utilities import ClassFormatter
13logger = logging.getLogger(__name__)
16class BaseResponse(ABC):
18 """Objects of this class hold response function of, e.g., nanoparticles or
19 molecules, in several different representations. The latter include, e.g.,
20 the polarizability tensor in the real or imaginary frequency domain.
22 Parameters
23 ----------
24 broadening
25 Broadening of the response.
26 pbc
27 True if underlying system is periodic.
28 name
29 Name of response function.
30 """
32 def __init__(self,
33 broadening: Broadening,
34 pbc: bool = False,
35 name: str = 'BaseResponse') -> None:
36 logger.debug(f'Entering {self.__class__.__name__}.__init__')
37 self._broadening = broadening
38 self._pbc = pbc
39 self._name = name
40 self._atoms: Optional[Atoms] = None
42 def __str__(self) -> str:
43 """String representation."""
44 fmt = ClassFormatter(self)
45 fmt.append_class_name()
47 fmt.append_attr('name')
48 fmt.append_attr('pbc')
49 fmt.append_attr('atoms')
51 return fmt.to_string()
53 @property
54 def pbc(self) -> bool:
55 """True if underlying system is periodic."""
56 return self._pbc
58 @property
59 def broadening(self) -> Broadening:
60 """Broadening of the response."""
61 return self._broadening
63 @property
64 def name(self) -> str:
65 """Name of response function."""
66 return self._name
68 @property
69 def atoms(self) -> Optional[Atoms]:
70 """Atomic structure associated with this response function.
71 None if not available."""
72 return self._atoms
74 def _get_dipole_strength_function(self, frequencies: Array) -> np.ndarray:
75 """Returns the dipole strength function.
77 Parameters
78 ----------
79 frequencies
80 List of frequencies at which the dipole strength function is calculated;
81 in atomic units.
83 Returns
84 -------
85 Dipole strength function at the given frequencies; in atomic units.
86 """
87 freq_w = np.asarray(frequencies)
88 pol_wvv = self._get_dynamic_polarizability(frequencies)
89 # Take diagonal
90 pol_wv = pol_wvv[(Ellipsis, ) + np.diag_indices(3)]
91 osc_wv = 2 / np.pi * freq_w[:, np.newaxis] * pol_wv.imag
92 return osc_wv # type: ignore
94 def get_dipole_strength_function(self, frequencies: Array) -> np.ndarray:
95 """Returns the dipole strength function.
97 Parameters
98 ----------
99 frequencies
100 List of frequencies at which the dipole strength function is calculated;
101 in units of eV.
103 Returns
104 -------
105 Dipole strength function at the given frequencies; in units of 1/eV.
106 """
107 freq_w = np.asarray(frequencies) * eV_to_au
108 osc_wv = self._get_dipole_strength_function(freq_w)
109 osc_wv = osc_wv / au_to_eV
110 return osc_wv
112 @abstractmethod
113 def _get_dynamic_polarizability(self, frequencies: Array) -> np.ndarray:
114 """Returns the dynamic polarizability in the real frequency domain.
116 Parameters
117 ----------
118 frequencies
119 List of frequencies at which the polarizability is calculated; in atomic units.
121 Returns
122 -------
123 Dynamical polarizability tensor at the given frequencies; in atomic units.
124 """
125 raise NotImplementedError()
127 def get_dynamic_polarizability(self, frequencies: Array) -> np.ndarray:
128 """Returns the dynamic polarizability in the real frequency domain.
130 Parameters
131 ----------
132 frequencies
133 List of frequencies at which the polarizability is calculated; in units of eV.
135 Returns
136 -------
137 Dynamical polarizability tensor at the given frequencies; in units of (eÅ)**2/eV.
138 """
139 freq_w = np.asarray(frequencies) * eV_to_au
140 dm_wvv = self._get_dynamic_polarizability(freq_w)
141 dm_wvv = dm_wvv * au_to_eA**2 / au_to_eV
142 return dm_wvv
144 @abstractmethod
145 def _get_dynamic_polarizability_imaginary_frequency(
146 self, frequencies: Array) -> np.ndarray:
147 """Returns the dynamic polarizability in the imaginary frequency domain.
149 Parameters
150 ----------
151 frequencies
152 List of frequencies at which the polarizability is calculated; in atomic units.
154 Returns
155 -------
156 Dynamical polarizability tensor at the given imaginary frequencies; in atomic units.
157 """
158 raise NotImplementedError()
160 def get_dynamic_polarizability_imaginary_frequency(
161 self, frequencies: Array) -> np.ndarray:
162 """Returns the dynamic polarizability in the imaginary frequency domain.
164 Parameters
165 ----------
166 frequencies
167 List of frequencies at which the polarizability is calculated; in units of eV.
169 Returns
170 -------
171 Dynamical polarizability tensor at the given imaginary frequencies; in units of (eÅ)**2/eV.
172 """
173 freq_w = np.asarray(frequencies) * eV_to_au
174 dm_wvv = self._get_dynamic_polarizability_imaginary_frequency(freq_w)
175 dm_wvv = dm_wvv * au_to_eA**2 / au_to_eV
176 return dm_wvv