Coverage for strongcoca / response / base.py: 100%

64 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-04-15 18:15 +0000

1import logging 

2from abc import ABC, abstractmethod 

3from typing import Optional 

4 

5import numpy as np 

6from ase import Atoms 

7 

8from .utilities import Broadening 

9from ..types import Array 

10from ..units import au_to_eV, eV_to_au, au_to_eA 

11from ..utilities import ClassFormatter 

12 

13logger = logging.getLogger(__name__) 

14 

15 

16class BaseResponse(ABC): 

17 

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. 

21 

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 """ 

31 

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 

41 

42 def __str__(self) -> str: 

43 """String representation.""" 

44 fmt = ClassFormatter(self) 

45 fmt.append_class_name() 

46 

47 fmt.append_attr('name') 

48 fmt.append_attr('pbc') 

49 fmt.append_attr('atoms') 

50 

51 return fmt.to_string() 

52 

53 def _repr_html_(self) -> str: 

54 """HTML representation for Jupyter notebooks.""" 

55 rows = [ 

56 f'<tr><td style="text-align: left;">name</td><td>{self._name}</td></tr>', 

57 f'<tr><td style="text-align: left;">broadening</td><td>{self._broadening!r}</td></tr>', 

58 f'<tr><td style="text-align: left;">pbc</td><td>{self._pbc}</td></tr>', 

59 ] 

60 return ( 

61 f'<h4>{self.__class__.__name__}</h4>' 

62 '<table>' 

63 '<thead><tr>' 

64 '<th style="text-align: left;">field</th>' 

65 '<th style="text-align: left;">value</th>' 

66 '</tr></thead>' 

67 '<tbody>' + ''.join(rows) + '</tbody>' 

68 '</table>' 

69 ) 

70 

71 @property 

72 def pbc(self) -> bool: 

73 """True if underlying system is periodic.""" 

74 return self._pbc 

75 

76 @property 

77 def broadening(self) -> Broadening: 

78 """Broadening of the response.""" 

79 return self._broadening 

80 

81 @property 

82 def name(self) -> str: 

83 """Name of response function.""" 

84 return self._name 

85 

86 @property 

87 def atoms(self) -> Optional[Atoms]: 

88 """Atomic structure associated with this response function. 

89 None if not available.""" 

90 return self._atoms 

91 

92 def _get_dipole_strength_function(self, frequencies: Array) -> np.ndarray: 

93 """Returns the dipole strength function. 

94 

95 Parameters 

96 ---------- 

97 frequencies 

98 List of frequencies at which the dipole strength function is calculated; 

99 in atomic units. 

100 

101 Returns 

102 ------- 

103 Dipole strength function at the given frequencies; in atomic units. 

104 """ 

105 freq_w = np.asarray(frequencies) 

106 pol_wvv = self._get_dynamic_polarizability(frequencies) 

107 # Take diagonal 

108 pol_wv = pol_wvv[(Ellipsis, ) + np.diag_indices(3)] 

109 osc_wv = 2 / np.pi * freq_w[:, np.newaxis] * pol_wv.imag 

110 return osc_wv # type: ignore 

111 

112 def get_dipole_strength_function(self, frequencies: Array) -> np.ndarray: 

113 """Returns the dipole strength function. 

114 

115 Parameters 

116 ---------- 

117 frequencies 

118 List of frequencies at which the dipole strength function is calculated; 

119 in units of eV. 

120 

121 Returns 

122 ------- 

123 Dipole strength function at the given frequencies; in units of 1/eV. 

124 """ 

125 freq_w = np.asarray(frequencies) * eV_to_au 

126 osc_wv = self._get_dipole_strength_function(freq_w) 

127 osc_wv = osc_wv / au_to_eV 

128 return osc_wv 

129 

130 @abstractmethod 

131 def _get_dynamic_polarizability(self, frequencies: Array) -> np.ndarray: 

132 """Returns the dynamic polarizability in the real frequency domain. 

133 

134 Parameters 

135 ---------- 

136 frequencies 

137 List of frequencies at which the polarizability is calculated; in atomic units. 

138 

139 Returns 

140 ------- 

141 Dynamical polarizability tensor at the given frequencies; in atomic units. 

142 """ 

143 raise NotImplementedError() 

144 

145 def get_dynamic_polarizability(self, frequencies: Array) -> np.ndarray: 

146 """Returns the dynamic polarizability in the real frequency domain. 

147 

148 Parameters 

149 ---------- 

150 frequencies 

151 List of frequencies at which the polarizability is calculated; in units of eV. 

152 

153 Returns 

154 ------- 

155 Dynamical polarizability tensor at the given frequencies; in units of (eÅ)**2/eV. 

156 """ 

157 freq_w = np.asarray(frequencies) * eV_to_au 

158 dm_wvv = self._get_dynamic_polarizability(freq_w) 

159 dm_wvv = dm_wvv * au_to_eA**2 / au_to_eV 

160 return dm_wvv 

161 

162 @abstractmethod 

163 def _get_dynamic_polarizability_imaginary_frequency( 

164 self, frequencies: Array) -> np.ndarray: 

165 """Returns the dynamic polarizability in the imaginary frequency domain. 

166 

167 Parameters 

168 ---------- 

169 frequencies 

170 List of frequencies at which the polarizability is calculated; in atomic units. 

171 

172 Returns 

173 ------- 

174 Dynamical polarizability tensor at the given imaginary frequencies; in atomic units. 

175 """ 

176 raise NotImplementedError() 

177 

178 def get_dynamic_polarizability_imaginary_frequency( 

179 self, frequencies: Array) -> np.ndarray: 

180 """Returns the dynamic polarizability in the imaginary frequency domain. 

181 

182 Parameters 

183 ---------- 

184 frequencies 

185 List of frequencies at which the polarizability is calculated; in units of eV. 

186 

187 Returns 

188 ------- 

189 Dynamical polarizability tensor at the given imaginary frequencies; in units of (eÅ)**2/eV. 

190 """ 

191 freq_w = np.asarray(frequencies) * eV_to_au 

192 dm_wvv = self._get_dynamic_polarizability_imaginary_frequency(freq_w) 

193 dm_wvv = dm_wvv * au_to_eA**2 / au_to_eV 

194 return dm_wvv