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

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 @property 

54 def pbc(self) -> bool: 

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

56 return self._pbc 

57 

58 @property 

59 def broadening(self) -> Broadening: 

60 """Broadening of the response.""" 

61 return self._broadening 

62 

63 @property 

64 def name(self) -> str: 

65 """Name of response function.""" 

66 return self._name 

67 

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 

73 

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

75 """Returns the dipole strength function. 

76 

77 Parameters 

78 ---------- 

79 frequencies 

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

81 in atomic units. 

82 

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 

93 

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

95 """Returns the dipole strength function. 

96 

97 Parameters 

98 ---------- 

99 frequencies 

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

101 in units of eV. 

102 

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 

111 

112 @abstractmethod 

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

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

115 

116 Parameters 

117 ---------- 

118 frequencies 

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

120 

121 Returns 

122 ------- 

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

124 """ 

125 raise NotImplementedError() 

126 

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

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

129 

130 Parameters 

131 ---------- 

132 frequencies 

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

134 

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 

143 

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. 

148 

149 Parameters 

150 ---------- 

151 frequencies 

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

153 

154 Returns 

155 ------- 

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

157 """ 

158 raise NotImplementedError() 

159 

160 def get_dynamic_polarizability_imaginary_frequency( 

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

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

163 

164 Parameters 

165 ---------- 

166 frequencies 

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

168 

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