Coverage for gpaw/pes/ds_beta.py: 94%
110 statements
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-14 00:18 +0000
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-14 00:18 +0000
1from math import sin, cos, pi, sqrt
3import numpy as np
5from ase.units import Bohr, Hartree, alpha
6from gpaw.fd_operators import Gradient
7from gpaw.utilities.gl_quadrature import GaussLegendre
8from gpaw.pes import ds_prefactor
9from gpaw.pes.state import H1s
10from gpaw.pes.continuum import PlaneWave
12# debug
13# from gpaw.mpi import rank
16class CrossSectionBeta:
18 def __init__(self,
19 initial=None,
20 final=None,
21 r0=[0, 0, 0], # center of mass vector
22 form='L',
23 ngauss=8):
25 self.initial = initial
26 self.r0 = np.array(r0) / Bohr
27 self.form = form
28 if final is None:
29 self.final = PlaneWave(initial.gd)
30 else:
31 self.final = final
33 self.Ekin = None
35 # Gauss-Legendre weights and abscissas
36 self.gl = {}
37 self.gl['x'] = GaussLegendre(-1., 1., ngauss)
38 self.gl['phi'] = GaussLegendre(0, 2 * pi, ngauss)
39 self.gl['psi'] = self.gl['phi']
40 self.angle = {}
42 # sin and cos of the magic angle (54.7 deg)
43 self.costhm = 1. / sqrt(3)
44 self.sinthm = sqrt(2. / 3.)
46 def calculate(self, Ekin):
47 """Calculate the necessary overlaps."""
49 Ekin = Ekin / Hartree
50 if self.Ekin == Ekin:
51 return
52 self.Ekin = Ekin
54 # photoelectron momentum
55 self.k = sqrt(2 * self.Ekin)
57 for angle in ['x', 'phi', 'psi']:
58 self.angle[angle] = self.gl[angle].get_x()[0]
59 self.T20, self.T2m = self.gi_x()
61 # we need the average
62 self.T20 /= 8 * pi ** 2
63 self.T2m /= 8 * pi ** 2
65 def get_omega(self):
66 """Return the necessary photon energy."""
67 return self.Ekin - self.initial.get_energy() / Hartree
69 def get_beta(self, Ekin=None):
70 """Return the asymmetry parameter.
72 E: photoelectron kinetic energy [eV]
73 """
74 if Ekin is not None:
75 self.calculate(Ekin)
76 return self.T20 / self.T2m - 1.
78 def get_ds(self, Ekin=None, units='Mb'):
79 """Return the total cross section.
81 Ekin: photoelectron kinetic energy [eV]
82 units:
83 'Mb', 1 Mb = 1.e-22 m**2
84 'Ang', 1 A**2 = 1.e-20 m**2
85 'a.u.', 1 a_0**2 = 2.8e-21 m**2
86 as output units
87 """
88 if Ekin is not None:
89 self.calculate(Ekin)
90 try:
91 pre = ds_prefactor[units]
92 except KeyError:
93 print('Unknown units: >' + units + '<')
94 raise
96# me_c = self.initial.get_me_c(np.array([0., 0., self.k]), self.form)
97# T2mana = np.abs(np.dot(me_c,me_c)) / 3.
98# print "T2m:", T2mana, self.T2m
100 omega = self.get_omega()
102 # integration over momentum agles
103 pre *= self.k * 4 * pi
105# print omega, self.initial.get_ds(self.k, omega, self.form), \
106# (self.k * 4 * pi * (2 * pi)**2 / 137.0359895 * self.T2m / omega)
108 return pre * ((2 * pi) ** 2 * alpha * self.T2m / omega)
110 def gauss_integrate(self, angle, function):
111 T20 = 0.
112 T2m = 0.
113 gl = self.gl[angle]
114 for x, w in zip(gl.get_x(), gl.get_w()):
115 self.angle[angle] = x
116 t20, t2m = function()
117 T20 += w * t20
118 T2m += w * t2m
119# print "<gauss_integrate> angle=", angle, T2m
120 return T20, T2m
122 def gi_x(self):
123 """Gauss integrate over x=cos(theta)"""
124 return self.gauss_integrate('x', self.gi_phi)
126 def gi_phi(self):
127 """Gauss integrate over phi"""
128 return self.gauss_integrate('phi', self.gi_psi)
130 def gi_psi(self):
131 """Gauss integrate over psi"""
132 return self.gauss_integrate('psi', self.integrand)
134 def integrand(self):
136 # polarisation in the direction of vk
137 costh = self.angle['x']
138 sinth = sqrt(1. - costh ** 2)
139 sinphi = sin(self.angle['phi'])
140 cosphi = cos(self.angle['phi'])
141 eps0 = np.array([sinth * cosphi,
142 sinth * sinphi,
143 costh])
144 vk = self.k * eps0
146 # polarisation at the magic angle
147 costhm = self.costhm
148 sinthm = self.sinthm
149 sinpsi = sin(self.angle['psi'])
150 cospsi = cos(self.angle['psi'])
151 epsm = np.array([sinthm * (cosphi * sinpsi * costh +
152 sinphi * cospsi) +
153 costhm * cosphi * sinth,
154 sinthm * (sinphi * sinpsi * costh -
155 cosphi * cospsi) +
156 costhm * sinphi * sinth,
157 costhm * costh - sinthm * sinth * sinpsi])
159 # initial and final state on the grid
160 initial_G = self.initial.get_grid()
161 final_G = self.final.get_grid(vk, self.r0)
162 ini_analyt = H1s(self.initial.gd, self.r0)
164 gd = self.initial.gd
165 if self.form == 'L':
166 if_G = initial_G * final_G
167 omega = self.get_omega()
168 if 0:
169 me_c = []
170 for c in range(3):
171 xyz_G = ((np.arange(gd.n_c[c]) + gd.beg_c[c]) * gd.h_c[c]
172 - self.r0[c])
173 shape = [1, 1, 1]
174 shape[c] = -1
175 xyz_G.shape = tuple(shape)
176 np.resize(xyz_G, gd.n_c)
177 me_c.append(gd.integrate(if_G * xyz_G))
178 me_c = np.array(me_c) * omega
179 else:
180 me_c = gd.calculate_dipole_moment(if_G)
181 me_c += self.r0 * gd.integrate(if_G)
182 me_c *= -omega
183 elif self.form == 'V':
184 dtype = final_G.dtype
185 phase_cd = np.ones((3, 2), dtype)
186 if not hasattr(gd, 'ddr'):
187 gd.ddr = [Gradient(gd, c, dtype=dtype).apply for c in range(3)]
188 dfinal_G = gd.empty(dtype=dtype)
189 me_c = np.empty(3, dtype=dtype)
190 for c in range(3):
191 gd.ddr[c](final_G, dfinal_G, phase_cd)
192 me_c[c] = gd.integrate(initial_G * dfinal_G)
193 else:
194 raise NotImplementedError
196 if 0:
197 omega = self.get_omega()
198 me_analyt = ini_analyt.get_me_c(vk, self.form)[0].imag
199 me = me_c[0].imag
201 def ds(me):
202 return self.k / omega * me ** 2
204 print(omega, ds(me_analyt), ds(me), me_analyt, me)
205# print 'analyt', self.initial.get_me_c(vk, self.form)
206# print 'num', me_c
207# print 'analyt/num', self.initial.get_me_c(vk, self.form) / me_c
209 # return the squared matrix elements
210 T2 = []
211 for eps in [eps0, epsm]:
212 me = np.dot(eps, me_c)
213# print "eps, T2:", eps, (me * me.conj()).real
214 T2.append((me * me.conj()).real)
215# print vk, T2
216 return T2[0], T2[1]