Coverage for /builds/ase/ase/ase/vibrations/placzek.py: 96.88%
96 statements
« prev ^ index » next coverage.py v7.5.3, created at 2025-08-02 00:12 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2025-08-02 00:12 +0000
1# fmt: off
3import numpy as np
5import ase.units as u
6from ase.calculators.excitation_list import polarizability
7from ase.vibrations.raman import Raman, RamanPhonons
8from ase.vibrations.resonant_raman import ResonantRaman
11class Placzek(ResonantRaman):
12 """Raman spectra within the Placzek approximation."""
14 def __init__(self, *args, **kwargs):
15 self._approx = 'PlaczekAlpha'
16 ResonantRaman.__init__(self, *args, **kwargs)
18 def set_approximation(self, value):
19 raise ValueError('Approximation can not be set.')
21 def _signed_disps(self, sign):
22 for a, i in zip(self.myindices, self.myxyz):
23 yield self._disp(a, i, sign)
25 def _read_exobjs(self, sign):
26 return [disp.read_exobj() for disp in self._signed_disps(sign)]
28 def read_excitations(self):
29 """Read excitations from files written"""
30 self.ex0E_p = None # mark as read
31 self.exm_r = self._read_exobjs(sign=-1)
32 self.exp_r = self._read_exobjs(sign=1)
34 def electronic_me_Qcc(self, omega, gamma=0):
35 self.calculate_energies_and_modes()
37 V_rcc = np.zeros((self.ndof, 3, 3), dtype=complex)
38 pre = 1. / (2 * self.delta)
39 pre *= u.Hartree * u.Bohr # e^2Angstrom^2 / eV -> Angstrom^3
41 om = omega
42 if gamma:
43 om += 1j * gamma
45 for i, r in enumerate(self.myr):
46 V_rcc[r] = pre * (
47 polarizability(self.exp_r[i], om,
48 form=self.dipole_form, tensor=True) -
49 polarizability(self.exm_r[i], om,
50 form=self.dipole_form, tensor=True))
51 self.comm.sum(V_rcc)
53 return self.map_to_modes(V_rcc)
56class PlaczekStatic(Raman):
57 def read_excitations(self):
58 """Read excitations from files written"""
59 self.al0_rr = None # mark as read
60 self.alm_rr = []
61 self.alp_rr = []
62 for a, i in zip(self.myindices, self.myxyz):
63 for sign, al_rr in zip([-1, 1], [self.alm_rr, self.alp_rr]):
64 disp = self._disp(a, i, sign)
65 al_rr.append(disp.load_static_polarizability())
67 def electronic_me_Qcc(self):
68 self.calculate_energies_and_modes()
70 V_rcc = np.zeros((self.ndof, 3, 3), dtype=complex)
71 pre = 1. / (2 * self.delta)
72 pre *= u.Hartree * u.Bohr # e^2Angstrom^2 / eV -> Angstrom^3
74 for i, r in enumerate(self.myr):
75 V_rcc[r] = pre * (self.alp_rr[i] - self.alm_rr[i])
76 self.comm.sum(V_rcc)
78 return self.map_to_modes(V_rcc)
81class PlaczekStaticPhonons(RamanPhonons, PlaczekStatic):
82 pass
85class Profeta(ResonantRaman):
86 """Profeta type approximations.
88 Reference
89 ---------
90 Mickael Profeta and Francesco Mauri
91 Phys. Rev. B 63 (2000) 245415
92 """
94 def __init__(self, *args, **kwargs):
95 self.set_approximation(kwargs.pop('approximation', 'Profeta'))
96 self.nonresonant = kwargs.pop('nonresonant', True)
97 ResonantRaman.__init__(self, *args, **kwargs)
99 def set_approximation(self, value):
100 approx = value.lower()
101 if approx in ['profeta', 'placzek', 'p-p']:
102 self._approx = value
103 else:
104 raise ValueError('Please use "Profeta", "Placzek" or "P-P".')
106 def electronic_me_profeta_rcc(self, omega, gamma=0.1,
107 energy_derivative=False):
108 """Raman spectra in Profeta and Mauri approximation
110 Returns
111 -------
112 Electronic matrix element, unit Angstrom^2
113 """
114 self.calculate_energies_and_modes()
116 V_rcc = np.zeros((self.ndof, 3, 3), dtype=complex)
117 pre = 1. / (2 * self.delta)
118 pre *= u.Hartree * u.Bohr # e^2Angstrom^2 / eV -> Angstrom^3
120 def kappa_cc(me_pc, e_p, omega, gamma, form='v'):
121 """Kappa tensor after Profeta and Mauri
122 PRB 63 (2001) 245415"""
123 k_cc = np.zeros((3, 3), dtype=complex)
124 for p, me_c in enumerate(me_pc):
125 me_cc = np.outer(me_c, me_c.conj())
126 k_cc += me_cc / (e_p[p] - omega - 1j * gamma)
127 if self.nonresonant:
128 k_cc += me_cc.conj() / (e_p[p] + omega + 1j * gamma)
129 return k_cc
131 mr = 0
132 for a, i, r in zip(self.myindices, self.myxyz, self.myr):
133 if energy_derivative >= 0:
134 V_rcc[r] += pre * (
135 kappa_cc(self.expm_rpc[mr], self.ex0E_p,
136 omega, gamma, self.dipole_form) -
137 kappa_cc(self.exmm_rpc[mr], self.ex0E_p,
138 omega, gamma, self.dipole_form))
139 if energy_derivative:
140 V_rcc[r] += pre * (
141 kappa_cc(self.ex0m_pc, self.expE_rp[mr],
142 omega, gamma, self.dipole_form) -
143 kappa_cc(self.ex0m_pc, self.exmE_rp[mr],
144 omega, gamma, self.dipole_form))
145 mr += 1
146 self.comm.sum(V_rcc)
148 return V_rcc
150 def electronic_me_Qcc(self, omega, gamma):
151 self.read()
152 Vel_rcc = np.zeros((self.ndof, 3, 3), dtype=complex)
153 approximation = self.approximation.lower()
154 if approximation == 'profeta':
155 Vel_rcc += self.electronic_me_profeta_rcc(omega, gamma)
156 elif approximation == 'placzek':
157 Vel_rcc += self.electronic_me_profeta_rcc(omega, gamma, True)
158 elif approximation == 'p-p':
159 Vel_rcc += self.electronic_me_profeta_rcc(omega, gamma, -1)
160 else:
161 raise RuntimeError(
162 'Bug: call with {} should not happen!'.format(
163 self.approximation))
165 return self.map_to_modes(Vel_rcc)