diff --git a/libhreels/calcHREELS.py b/libhreels/calcHREELS.py index 5bdd9e936f91cdd6cc9f8bd3b9136537f980a865..b3000e031383a2a427659344e567760b5a5c88b2 100644 --- a/libhreels/calcHREELS.py +++ b/libhreels/calcHREELS.py @@ -14,6 +14,7 @@ if not (sys.version.startswith('3.6') or sys.version.startswith('3.8')): try: from libhreels import myEels2 as LambinEELS # wrapper for myEels2.f90 from libhreels import myBoson as LambinBoson # wrapper for myBoson.f90 + from libhreels import myEels3 as LambinEELS3 # wrapper for myEels3.f90 except: print('myEels2 and MyBoson are not available here (Check your version)') @@ -61,6 +62,9 @@ def addDrude(wPlasma, gPlasma, material): # To select the Drude response, the Lambin Fortran code requires a negative first oscillator frequency # which is then interpreted as plasma frequency. # The values of the former LO parameter are irrelevant (but need to be provided). + if not wPlasma > 0: + print('Cannot add Drude to material',material) + return material try: if len(newMaterial['wTO']) > 0: newMaterial['wTO'] += [-1*wPlasma] @@ -139,6 +143,25 @@ class lambin: i += 1 return wn_array[i-1:], loss_array[i-1:] + def calcSurfaceLoss3(self,x): + ''' Calculate the surface loss spectrum for the array of x, which needs to be an equidistant array. + All parameters are defined in the class __init__() call.''' + wmin = min(x) + wmax = max(x)-0.001 + dw = (wmax-wmin)/(len(x)-1) # assumes that x is an equidistant array + wn_array_size = len(x) # size of array for x and epsilon (wn_array, loss_array) + nper = 1. + contrl = '{:<10}'.format('None'[:10]) # Can be 'image' to include image charge + mode = '{:<10}'.format('kurosawa'[:10]) + wn_array,loss_array = LambinEELS3.mod_doeels.doeels(self.e0,self.theta,self.phia,self.phib, + wmin,wmax,dw,self.layers,self.neps,nper,self.name, + self.thick,self.epsinf,self.listNOsci,self.osc,contrl,mode,wn_array_size) + i=0 + for item in wn_array: + if item > 0: break + i += 1 + return wn_array[i-1:], loss_array[i-1:] + def calcHREELS(self,x, normalized=True): emin = min(x) emax = max(x)-0.001 @@ -161,6 +184,14 @@ class lambin: epsArray.append(yn) return np.transpose(np.array(epsArray)) + def calcEps3(self, x): + epsArray = [] + nOsci = len(self.wOsc) + for wn in x: + yn = LambinEELS3.mod_doeels.seteps(self.listNOsci,nOsci,self.osc,self.epsinf,wn,self.layers) + epsArray.append(yn) + return np.transpose(np.array(epsArray)) + #################################################################################### def myMain(): import matplotlib.pyplot as plt diff --git a/libhreels/mod_doeels.mod b/libhreels/mod_doeels.mod index 221c658ae96d0bbceb9a3785c8fc2508c6090133..48bacadb0b52c9427a2527bcc7752342bf3b9f32 100644 Binary files a/libhreels/mod_doeels.mod and b/libhreels/mod_doeels.mod differ diff --git a/libhreels/myEels3.cpython-38-x86_64-linux-gnu.so b/libhreels/myEels3.cpython-38-x86_64-linux-gnu.so new file mode 100644 index 0000000000000000000000000000000000000000..cccb9a7fbf263a6a0d26891cd36aa2b661034fe8 Binary files /dev/null and b/libhreels/myEels3.cpython-38-x86_64-linux-gnu.so differ diff --git a/libhreels/myEels3.f90 b/libhreels/myEels3.f90 new file mode 100644 index 0000000000000000000000000000000000000000..bf5e57645764f591a6bab7a92043a59ddc8bc121 --- /dev/null +++ b/libhreels/myEels3.f90 @@ -0,0 +1,996 @@ +module mod_doeels + contains +subroutine doeels (e0, theta, phia, phib, wmin, wmax, dw, & + layers, neps, nper, name, name_size, thick, epsinf, nos, osc, osc_size,& + contrl, mode, wn_array, debug, f_array, wn_array_size) + +! ****************************************************************** +! * * +! * compute the classical eels spectrum of an arbitrary plane- * +! * statified medium made from isotropic materials in specular * +! * geometry using the dielectric theory of eels. * +! * +! * It is based on the work of Lambin'90 and modified for the use * +! * within python * +! * (KMS and WFW, Martin-Luther-Universität Halle-Wittenberg) * * +! ****************************************************************** + + implicit none + + integer, parameter :: nt = 5 + + double precision, intent(in) :: e0, theta, phia, phib, wmin, wmax, dw + double precision, intent(in) :: thick(name_size), epsinf(name_size), osc(3, osc_size) + character*10, intent(in) :: name(name_size) + character*10, intent(in) :: contrl, mode + integer, intent(in) :: name_size, osc_size, neps + integer, intent(in) :: wn_array_size + integer, intent(in) :: layers, nper, nos(name_size) + double precision, intent(out) :: wn_array(wn_array_size), f_array(wn_array_size) + logical, intent(in), optional :: debug + + logical :: ration, user + integer :: i, iw, lstart, nofu, nout, nw, lmax, flag + double precision :: a, acoef, aerr, alpha, argmin, argmax, b, bcoef, beta, & + c1, c2, ccoef, cospsi, dlimf, dx, elleps, ener, epsmac, facru, f, f0, & + f1, fpic, pi, prefac, psia, psii, rerr, ru, sinpsi, t, & + tanpsi, table, um, widt, wn, wpic, x, xmin, xmax, z, z1, z2 + double complex, dimension(30) :: eps + dimension table(nt) + logical debugFirstRun + common /control/ debugFirstRun + + common / param / acoef, bcoef, ccoef, elleps, cospsi, sinpsi, tanpsi, & + ru, um, dlimf, wn, user, ration + common / mulayr / argmin, argmax, epsmac, flag + + !if (present(debug)) debug = .True. + debugFirstRun = .True. + + data aerr / 0.0d0 /, rerr / 1.0d-06 /, f / 0.0d0 /, f1 / 0.0d0 / + +! *** machine-dependent constants +! *** epsmac + 1.0 = epsmac , cosh(argmin) = 1.0 , tanh(argmax) = 1.0 + flag = 1 + + if (debug) write(*,*) 'doeels:' + if (debug) write(*,*) 'thick: ', size(thick) + if (debug) write(*,*) 'epsinf: ', size(epsinf), epsinf + if (debug) write(*,*) 'osc: ', size(osc), size(osc, 1), size(osc, 2) + if (debug) write(*,*) 'osc(1,1):', osc(1,1) + if (debug) write(*,*) 'nos: ', size(nos) + if (debug) write(*,*) 'wn_array: ', size(wn_array) + if (debug) write(*,*) 'f_array: ', size(f_array) + + pi = 4.0d0 * atan(1.0d0) + epsmac = 1.0d0 +1 epsmac = epsmac / 2.0d0 + if (1.0d0 + epsmac > 1.0d0) goto 1 + argmin = sqrt(2.0d0 * epsmac) + argmax = 0.5d0 * log(2.0d0 / epsmac) + + dlimf = 0.0d0 + ration = .false. + +! *** read target specifications + + user = layers == 0 + if (user) then + + if (layers == 1) ration = .true. + lstart = layers - nper + 1 + endif + +! *** initialize constants + + lmax = size(thick) + nw = size(wn_array) + ! if (debug) write(*,*) 'lmax: ', lmax + ! allocate(eps(lmax)) + ! if (debug) write(*,*) 'eps: ', size(eps) + nout = 1 + nw / 20 + ener = 8.065d+03 * e0 + psia = phia / 180.0d0 * pi + psii = theta / 180.0d0 * pi + cospsi = cos(psii) + sinpsi = sin(psii) + tanpsi = tan(psii) + prefac = sqrt(2.555d+05 / e0)/(1.37d+02 * cospsi) + facru = psia / cospsi * sqrt(0.2624664d0 * e0) + elleps = (1.0d0 - phia / phib) * (1.0d0 + phia / phib) + acoef = sinpsi**2 + elleps * cospsi**2 + bcoef = sinpsi * cospsi + if (dlimf > 0.0d0) then + ration = .false. + write(*,*) ' = > electron attracted by an image charge = ', dlimf +! *** dlimf : half the length unit imposed by the image force + dlimf = 1.80d0 * dlimf/(e0 * cospsi**2) + endif + ! if (debug) write(*,*) 'ration: ', ration + if (.not. ration) goto 35 + +! *** set up coefficients for the rational approximation to the integral + + write(*,*) ' = > set up a rational approximation to the integral' + call quanc8(fun, 0.0d0, pi / 2.0d0, aerr, rerr, alpha, c1, nofu, c2, eps, thick, layers, nper) + alpha = (2.0d0 / pi)**2 * alpha + c1 = 2.0d0 / pi / sqrt(1.0d0 - elleps) * sinpsi * alpha**2 + if (c1 > 0.99d0) goto 30 + c2 = 3.0d0 * alpha**2 / (1.0d0 - c1) + c1 = c1 * c2 + xmin = wmin / (2.0d0 * ener * psia) + xmax = wmax / (2.0d0 * ener * psia) + if (xmin <= 0.0d0) xmin = 0.0d0 + dx = max(0.02d0, (xmax - xmin) / nt) + z1 = 0.0d0 + z2 = 0.0d0 + do i = 1, nt + x = xmin + i * dx + call queels(x, f, aerr, rerr, facru, eps, thick, layers, nper) + table(i) = f + f = f * (1.0d0 + alpha * x)**2 + if (abs(c2 * f - c1) < c2 * rerr) cycle + z = (1.0d0 - f) / (c2 * f - c1) + if (z <= 0.0d0) cycle + z1 = z1 + x * z * (x**2 - z) + z2 = z2 + (x * z)**2 + enddo + if (z2 == 0.0d0) goto 30 + beta = z1 / z2 + z = 0.0d0 + do i = 1, nt + x = xmin + i * dx + z = z + (table(i) - qrat(x,alpha,beta,c1,c2))**2 + enddo + z = sqrt(z) / nt + if (z > 5.0d-03) goto 30 + write(*, 109) alpha, c1, c2, beta, z + goto 35 +30 write(*, *) ' ===> cannot do it' + ration = .false. + +! *** loop over the energy losses + +35 if (debug) write(*, 110) + do iw = 1, nw + f0 = f1 + f1 = f + f = 0.0d0 + wn = wmin + (iw - 1) * dw +! if (debug) write(*,*) 'wn: ', wn + if (wn < 0.0d0) goto 45 + if (wn == 0.0d0) goto 40 + if (.not. user) call seteps(nos, osc_size, osc, epsinf, wn, name_size, eps) + ! subroutine seteps(nos, osc_size, osc, epsinf, wn, nLayer, eps) + + x = wn / (2.0d0 * ener * psia) + if (ration) then + f = qrat(x,alpha,beta,c1,c2) * aimag(-2.0 / (1.0 + eps(1))) + else + call queels(x, f, aerr, rerr, facru, eps, thick, layers, nper) + endif + f = prefac * f / wn +40 continue + wn_array(iw) = wn + f_array(iw) = f +! *** localize a peak using a parabolic interpolation + if (iw < 3) goto 45 + if (f1 - f0 <= aerr) goto 45 + if (f1 - f <= aerr) goto 45 + a = (f1 - f0) + (f1 - f) + if (a <= 4.0d0 * rerr * f1) goto 45 + b = 0.5d0 * (f1 - f0 + 3.0d0 * (f1 - f)) + t = b / a + wpic = wn - t * dw + fpic = f + 0.5d0 * b * t + widt = sqrt(8.0d0 * fpic / a) * dw + if (debug) write(*, 112) wpic, fpic, widt +45 if (mod(iw, nout) == 0) then + if (debug) write(*, 113) 100.0 * iw / nw, wn, f + endif + enddo + return +109 format(5x, 'alpha = ', f9.4, 4x, 'c1 = ', f9.4, 4x, 'c2 = ', f9.4, 4x, & + 'beta = ', f9.4/5x, 'accuracy = ', e9.2) +110 format(//' run (%) wn (cm**-1) pcl(wn) (cm) |', & + ' peak location amplitude width') +112 format(40x, f10.2, d12.4, f10.2) +113 format(2x, f5.1, 3x, f11.3, d14.5) +end subroutine doeels +! #################################################################### + +double precision function qrat(x, alpha, beta, c1, c2) + double precision, intent(in) :: x, alpha, beta, c1, c2 + qrat = (1.0d0 + x * (beta + c1 * x)) / ((1.0d0 + x * (beta + c2 * x)) * (1.0d0 + alpha * x)**2) + return +end function qrat +! #################################################################### + +subroutine quanc8(fun, a, b, abserr, relerr, result, errest, nofun, flag, eps, d, layers, nper) + +! estimate the integral of fun(x) from a to b +! to a user provided tolerance. +! an automatic adaptive routine based on +! the 8-panel newton-cotes rule (g. forsythe et al, 1977, p. 92) +! +! input .. +! +! fun the name of the integrand function subprogram fun(x). +! a the lower limit of integration. +! b the upper limit of integration.(b may be less than a.) +! relerr a relative error tolerance. (should be non-negative) +! abserr an absolute error tolerance. (should be non-negative) +! +! output .. +! +! result an approximation to the integral hopefully satisfying the +! least stringent of the two error tolerances. +! errest an estimate of the magnitude of the actual error. +! nofun the number of function values used in calculation of result. +! flag a reliability indicator. if flag is zero, then result +! probably satisfies the error tolerance. if flag is +! xxx.yyy , then xxx = the number of intervals which have +! not converged and 0.yyy = the fraction of the interval +! left to do when the limit on nofun was approached. + + implicit none + external fun + double precision :: fun + double precision, intent(in) :: a + double precision, intent(in) :: b + double precision, intent(in out) :: abserr + double precision, intent(in) :: relerr + double precision, intent(out) :: result + double precision, intent(out) :: errest + integer, intent(out) :: nofun + double precision, intent(out) :: flag + + double precision, intent(in) :: d(:) + double complex, intent(in) :: eps(:) + integer, intent(in) :: layers, nper + + double precision :: w0, w1, w2, w3, w4, area, x0, f0, stone, step, cor11, temp + double precision :: qprev, qnow, qdiff, qleft, esterr, tolerr + double precision :: qright(31), f(16), x(16), fsave(8, 30), xsave(8, 30) + double precision :: dabs, dmax1 + + integer :: levmin, levmax, levout, nomax, nofin, lev, nim, i, j + +! *** stage 1 *** general initialization +! set constants. + +! write (*,*) 'quanc8:' +! write (*,*) 'd: ', size(d) +! write (*,*) 'eps: ', size(eps) + + levmin = 1 + levmax = 30 + levout = 6 + nomax = 5000 + nofin = nomax - 8 * (levmax - levout + 2**(levout + 1)) + +! trouble when nofun reaches nofin + + w0 = 3956.0d0 / 14175.0d0 + w1 = 23552.0d0 / 14175.0d0 + w2 = -3712.0d0 / 14175.0d0 + w3 = 41984.0d0 / 14175.0d0 + w4 = -18160.0d0 / 14175.0d0 + +! initialize running sums to zero. + + flag = 0.0d0 + result = 0.0d0 + cor11 = 0.0d0 + errest = 0.0d0 + area = 0.0d0 + nofun = 0 + if (a == b) return + +! *** stage 2 *** initialization for first interval + + lev = 0 + nim = 1 + x0 = a + x(16) = b + qprev = 0.0d0 + f0 = fun(x0, eps, d, layers, nper, size(eps)) + stone = (b - a) / 16.0d0 + x(8) = (x0 + x(16)) / 2.0d0 + x(4) = (x0 + x(8)) / 2.0d0 + x(12) = (x(8) + x(16)) / 2.0d0 + x(2) = (x0 + x(4)) / 2.0d0 + x(6) = (x(4) + x(8)) / 2.0d0 + x(10) = (x(8) + x(12)) / 2.0d0 + x(14) = (x(12) + x(16)) / 2.0d0 + do j = 2, 16, 2 + f(j) = fun(x(j), eps, d, layers, nper, size(eps)) + enddo + nofun = 9 + + do + +! *** stage 3 *** central calculation +! requires qprev, x0, x2, x4, ..., x16, f0, f2, f4, ..., f16. +! calculates x1, x3, ...x15, f1, f3, ...f15, qleft, qright, qnow, qdiff, area. + + x(1) = (x0 + x(2)) / 2.0d0 + f(1) = fun(x(1), eps, d, layers, nper, size(eps)) + do j = 3, 15, 2 + x(j) = (x(j - 1) + x(j + 1)) / 2.0d0 + f(j) = fun(x(j), eps, d, layers, nper, size(eps)) + enddo + nofun = nofun + 8 + step = (x(16) - x0) / 16.0d0 + qleft = (w0 * (f0 + f(8)) + w1 * (f(1) + f(7)) + w2 * (f(2) + f(6)) & + + w3 * (f(3) + f(5)) + w4 * f(4)) * step + qright(lev + 1) = (w0 * (f(8) + f(16)) + w1 * (f(9) + f(15)) + w2 * (f(10) + f(14)) & + + w3 * (f(11) + f(13)) + w4 * f(12)) * step + qnow = qleft + qright(lev + 1) + qdiff = qnow - qprev + area = area + qdiff + +! *** stage 4 *** interval convergence test + + esterr = dabs(qdiff) / 1023.0d0 + tolerr = dmax1(abserr, relerr * dabs(area)) * (step / stone) + + if (lev >= levmin) then + if (lev >= levmax) then +! current level is levmax. + flag = flag + 1.0d0 + else + if (nofun > nofin) then +! *** stage 6 *** trouble section +! number of function values is about to exceed limit. + nofin = 2 * nofin + levmax = levout + flag = flag + (b - x0) / (b - a) + else + if (esterr > tolerr) then +! *** stage 5 *** no convergence +! locate next interval. + nim = 2 * nim + lev = lev + 1 +! store right hand elements for future use. + do i = 1, 8 + fsave(i, lev) = f(i + 8) + xsave(i, lev) = x(i + 8) + enddo +! assemble left hand elements for immediate use. + qprev = qleft + do i = 1, 8 + f(18 - 2 * i) = f(9 - i) + x(18 - 2 * i) = x(9 - i) + enddo + cycle + endif + endif + endif + +! *** stage 7 *** interval converged +! add contributions into running sums. + result = result + qnow + errest = errest + esterr + cor11 = cor11 + qdiff / 1023.0d0 +! locate next interval. + do while (nim /= 2 * (nim / 2)) + nim = nim / 2 + lev = lev - 1 + enddo + nim = nim + 1 + + if (lev <= 0) exit + +! assemble elements required for the next interval. + qprev = qright(lev) + x0 = x(16) + f0 = f(16) + do i = 1, 8 + f(2*i) = fsave(i, lev) + x(2*i) = xsave(i, lev) + enddo + cycle + else +! *** stage 5 *** no convergence +! locate next interval. + nim = 2 * nim + lev = lev + 1 +! store right hand elements for future use. + do i = 1, 8 + fsave(i, lev) = f(i + 8) + xsave(i, lev) = x(i + 8) + enddo +! assemble left hand elements for immediate use. + qprev = qleft + do i = 1, 8 + f(18 - 2 * i) = f(9 - i) + x(18 - 2 * i) = x(9 - i) + enddo + endif + + enddo + + ! *** stage 8 *** finalize and return + result = result + cor11 + +! make sure errest not less than roundoff level. + if (errest /= 0.0d0) then + temp = dabs(result) + errest + do while (temp == dabs(result)) + errest = 2 * errest + temp = dabs(result) + errest + enddo + endif + return +end subroutine quanc8 +! ############################################################################ + +double precision function fun(phi) + +! ****************************************************************** +! * * +! * integrand of the expression of the 1st order term in the * +! * expansion of the eels integral for a homogeneous target. * +! * * +! ****************************************************************** + + implicit none + + double precision, intent(in out) :: phi + + logical :: user, ration + double precision :: acoef, bcoef, ccoef, cospsi, dlimf, elleps, ru + double precision :: sinphi, sinpsi, tanpsi, um, wn + + common / param / acoef, bcoef, ccoef, elleps, cospsi, sinpsi, tanpsi, & + ru, um, dlimf, wn, user, ration + + sinphi = sin(phi) + fun = sqrt((1.0d0 - elleps + elleps * sinphi**2) * & + (1.0d0 - sinpsi * sinphi) * & + (1.0d0 + sinpsi * sinphi)) + return +end function fun +! ########################################################################## + +subroutine queels(x, f, aerr, rerr, facru, eps, d, layers, nper) + +! ****************************************************************** +! * * +! * perform q-space integration for computing the eels spectrum of * +! * a isotropic target using polar coordinates. * +! * * +! * x is the dimensionless energy loss hbar*omega/(2*e0*phia) * +! * aerr and rerr are the desired absolute and relative accuracies * +! * facru*x is the units of wavevectors omega/v_perpendicular * +! * f is the q-integral multiplied by (2/pi)**2 * +! * * +! ****************************************************************** + + implicit none + + double precision, intent(in) :: x + double precision, intent(out) :: f + double precision, intent(in out) :: aerr + double precision, intent(in out) :: rerr + double precision, intent(in) :: facru + + double precision, intent(in) :: d(:) + double complex, intent(in) :: eps(:) + integer, intent(in) :: layers, nper + + logical :: ration, user + double precision :: acoef, bcoef, ccoef, cospsi, dlimf, elleps + double precision :: error, flag, ru, sinpsi + double precision :: u1, u2, um, ut, tanpsi, wn, y + integer :: ie, nofu + dimension error(3), flag(3) + + common / param / acoef, bcoef, ccoef, elleps, cospsi, sinpsi, tanpsi, & + ru, um, dlimf, wn, user, ration + +! write (*,*) 'queels:' +! write (*,*) 'eps: ', size(eps) +! write (*,*) 'd: ', size(d) + + f = 0.0d0 + if (x <= 0.0d0) then + return + endif + ru = facru * x + ccoef = cospsi**2 / x + ut = ccoef - bcoef + u1 = abs(ut) + u2 = ccoef + bcoef + if (ut > 0.0d0) then + call quanc8(fint1, 0.0d0, u1, aerr, rerr, y, error(1), nofu, flag(1), eps, d, layers, nper) + f = y + else + flag(1) = 0.0d0 + endif + if (u2 > u1) then + call quanc8(fint2, u1, u2, aerr, rerr, y, error(2), nofu, flag(2), eps, d, layers, nper) + f = f + y + else + flag(2) = 0.0d0 + endif + if (abs(acoef) > x * (1.0d0 - elleps) * bcoef) then + um = sqrt(ccoef / x / (1.0d0 - elleps) + bcoef**2 / acoef) + if (um > u2) then + call quanc8(fint3, u2, um, aerr, rerr, y, error(3), nofu, flag(3), eps, d, layers, nper) + f = f + y + endif + if (um < u1) then + call quanc8(fint3, um, u1, aerr, rerr, y, error(3), nofu, flag(3), eps, d, layers, nper) + f = f - y + endif + else + flag(3) = 0.0d0 + endif + do ie = 1, 3 + if (flag(ie) == 0.0d0) cycle + write(*,*) ' +++ flag(', ie, ') =', flag(ie), ', error =', error(ie), ' +++' + if (flag(ie) - aint(flag(ie)) > 0.5d-02) then + stop '*** execution aborted ***' + endif + enddo + f = (2.0d0 / 3.141592653589793238d0)**2 * f + return +end subroutine queels +! ########################################################################### + +double precision function fint1(u, eps, d, layers, nper, eps_size) + +! ****************************************************************** +! * * +! * integration over the azimutal angle from 0.0 to pi * +! * * +! ****************************************************************** + + implicit none + + double precision, intent(in) :: u + + double precision, intent(in) :: d(eps_size) + double complex, intent(in) :: eps(eps_size) + integer, intent(in) :: layers, nper, eps_size + + logical :: ration, user + double precision :: acoef, bcoef, ccoef, cospsi, den, dif, dlimf, e, elleps + double precision :: pi, rom, rop, ru, sinpsi, sum, um, t + double precision :: tanpsi, wn + + common / param / acoef, bcoef, ccoef, elleps, cospsi, sinpsi, tanpsi, & + ru, um, dlimf, wn, user, ration + + data pi / 3.141592653589793238d0 / + +! write (*,*) 'fint1:' +! write (*,*) 'd: ', size(d) +! write (*,*) 'eps: ', size(eps) + + if (u == 0.0d0) then + fint1 = 0.0d0 + return + endif + e = tanpsi * u + rom = (1.0d0 - e)**2 + u**2 + rop = (1.0d0 + e)**2 + u**2 + sum = rop + rom + rom = sqrt(rom) + rop = sqrt(rop) + dif = rop - rom + den = sqrt((2.0d0 - dif) * (2.0d0 + dif)) * rop * rom + fint1 = pi * u**2 * (4.0d0 * sum - dif**2 * (sum - rop * rom)) / den**3 + if (ration) then + return + endif + if (user) then + fint1 = fint1 * usurlo(ru * u, wn) + else + fint1 = fint1 * surlos(ru * u, eps, d, layers, nper) + if (dlimf > 0.0d0) then + t = ru * u * dlimf + fint1 = fint1 * (1.d0 + t * log(t / (t + 0.26d0)))**2 / (1.d0 + 1.40d0 * t) + endif + endif + return +end function fint1 +! ################################################### + +double precision function fint2(u, eps, d, layers, nper, eps_size) + +! ****************************************************************** +! * * +! * integration over the azimutal angle from 0.0 to phi < pi * +! * * +! ****************************************************************** + + implicit none + + double precision, intent(in) :: u + + double precision, intent(in) :: d(eps_size) + double complex, intent(in) :: eps(eps_size) + integer, intent(in) :: layers, nper, eps_size + + logical :: ration, user + double precision :: a, arg, b, b2, c, ccoef, cospsi, dlimf, elleps, phi + double precision :: ru, sinpsi, um, t, tanpsi, wn, x + + common / param / a, b, ccoef, elleps, cospsi, sinpsi, tanpsi, & + ru, um, dlimf, wn, user, ration + +! write (*,*) 'fint2:' +! write (*,*) 'd: ', size(d) +! write (*,*) 'eps: ', size(eps) + + if (u == 0.0d0) then + fint2 = 0.0d0 + return + endif + b2 = b**2 + c = (1.0d0 - elleps) * (cospsi * u)**2 + (b - ccoef) * (b + ccoef) + if (abs(a * c) > 1.0d-03 * b2) then + x = (b - sqrt(b2 - a * c)) / a + else + x = a * c / b2 + x = 0.5d0 * c * (1.d0 + 0.25d0 * x * (1.d0 + 0.5d0 * x * (1.d0 + 0.625d0 * x))) / b + endif + arg = x / u + if (abs(arg) > 1.0d0) then + arg = sign(1.0d0, arg) + endif + phi = acos(arg) + fint2 = phint(phi, tanpsi, u) + if (ration) then + return + endif + if (user) then + fint2 = fint2 * usurlo(ru * u, wn) + else + fint2 = fint2 * surlos(ru * u, eps, d, layers, nper) + if (dlimf > 0.0d0) then + t = ru * u * dlimf + fint2 = fint2 * (1.d0 + t * log(t / (t + 0.26d0)))**2 / (1.d0 + 1.40d0 * t) + endif + endif + return +end function fint2 +! ######################################################################## + +double precision function fint3(u, eps, d, layers, nper, eps_size) + +! ****************************************************************** +! * * +! * integration over the azimutal angle from phi1 > 0 to phi2 < pi * +! * * +! ****************************************************************** + + implicit none + + double precision, intent(in) :: u + + double precision, intent(in) :: d(eps_size) + double complex, intent(in) :: eps(eps_size) + integer, intent(in) :: layers, nper, eps_size + + logical :: ration, user + double precision :: a, arg, b, ccoef, cospsi, dlimf, elleps, phi1, phi2 + double precision :: sinpsi, rac, ru, um, t, tanpsi, wn + + common / param / a, b, ccoef, elleps, cospsi, sinpsi, tanpsi, & + ru, um, dlimf, wn, user, ration + + +! write (*,*) 'fint3:' +! write (*,*) 'd: ', size(d) +! write (*,*) 'eps: ', size(eps) + + if (u == 0.0d0) then + fint3 = 0.0d0 + return + endif + rac = sign(1.0d0, a) * cospsi * sqrt((1.0d0 - elleps) * a * (um - u) * (um + u)) + arg = (b - rac) / (u * a) + if (abs(arg) > 1.0d0) arg = sign(1.0d0, arg) + phi2 = acos(arg) + fint3 = phint(phi2, tanpsi, u) + arg = (b + rac) / (u * a) + if (abs(arg) > 1.0d0) arg = sign(1.0d0, arg) + phi1 = acos(arg) + fint3 = fint3 - phint(phi1, tanpsi, u) + if (ration) return + if (user) then + fint3 = fint3 * usurlo(ru * u, wn) + else + fint3 = fint3 * surlos(ru * u, eps, d, layers, nper) + if (dlimf > 0.0d0) then + t = ru * u * dlimf + fint3 = fint3 * (1.d0 + t * log(t / (t + 0.26d0)))**2 / (1.d0 + 1.40d0 * t) + endif + endif + return +end function fint3 +! ########################################################################## + +double precision function usurlo(dq, wn) + +! ****************************************************************** +! * * +! * user-supplied dielectric surface loss function aimag(g(dq, wn)) * +! * input arguments : * +! * dq : modulus of the two-dimensional surface wave vector * +! * (angstroem**-1) * +! * wn : frequency (cm**-1) * +! * * +! ****************************************************************** + + implicit none + + double precision, intent(in) :: dq + double precision, intent(in) :: wn + + if ((wn .GT. 0).AND.(dq.GT.0)) then + write(*,*) 'hello, here is the user loss function' + endif + usurlo = 1.0d0 + return +end function usurlo +! ######################################################################### + +double precision function surlos(dk, eps, d, layers, nper) + +! ****************************************************************** +! * * +! * eels surface loss function for an arbitrary multilayered target* +! * * +! ****************************************************************** + + implicit none + + double precision, intent(in) :: dk + double complex, intent(in) :: eps(30) + double precision, intent(in) :: d(:) + integer, intent(in) :: layers, nper + + integer :: lmax, flag + logical :: static, zero + integer :: lstart, n + double precision, allocatable :: arg(:) + double precision :: argmin, argmax, cn, cnm1, epsmac, sn, snm1, t + double complex :: a, b, csi, pnm2, pnm1, pn, pp, qnm2, qnm1, qn, qp, z + + common / mulayr / argmin, argmax, epsmac, flag + + zero(z) = (real(z) == 0.0) .and. (aimag(z) == 0.0) + +! write (*,*) 'surlos:' +! write (*,*) 'd: ', size(d) +! write (*,*) 'eps: ', size(eps) + + lmax = size(eps) + allocate (arg(lmax)) + lstart = layers - nper + 1 + static = .true. + n = 1 +1 arg(n) = dk * d(n) + if (arg(n) > argmax .or. zero(eps(n))) goto 10 + static = .not. (n >= lstart .and. arg(n) > argmin) + n = n + 1 + if (n <= layers) goto 1 + +! *** periodic continued fraction, period = nper + + if (nper > 1) goto 2 + csi = eps(layers) + goto 9 +2 if (static) goto 5 + cn = cosh(arg(lstart)) + sn = sinh(arg(lstart)) + pnm1 = 1.0 + pn = cn + pp = eps(lstart) * sn + qnm1 = 0.0 + qn = sn / eps(lstart) + qp = pn + do n = lstart + 1, layers + cnm1 = cn + snm1 = sn + cn = cosh(arg(n)) + sn = sinh(arg(n)) + a = eps(n) * sn + pp = cn * pp + a * pn + qp = cn * qp + a * qn + b = (eps(n - 1) / eps(n)) * (sn / snm1) + a = cnm1 * b + cn + pnm2 = pnm1 + pnm1 = pn + qnm2 = qnm1 + qnm1 = qn + pn = a * pnm1 - b * pnm2 + qn = a * qnm1 - b * qnm2 + enddo + if (zero(qn)) goto 4 + a = 0.5 * (pn - qp) / qn + b = sqrt(a**2 + pp / qn) + pn = a - pn / qn + if (abs(pn + b) > abs(pn - b)) then + b = -b + endif + csi = a + b + goto 9 +4 a = qp - pn + if (zero(a)) goto 12 + csi = pp / a + goto 9 + +! *** small-dk limit of the periodic tail + +5 pn = 0.0 + qn = 0.0 + do n = lstart, layers + pn = pn + d(n) * eps(n) + qn = qn + d(n) / eps(n) + enddo + if (zero(qn)) goto 12 + csi = sqrt(pn / qn) + if (aimag(csi) > 0.0) goto 9 + if ((aimag(csi) < 0.0) .or. (real(qn) < 0.0)) then + csi = -csi + endif +9 n = lstart + goto 11 +10 csi = eps(n) + +! *** backward algorithm + +11 n = n - 1 + if (n <= 0) goto 15 + if (arg(n) == 0.0d0) goto 11 + t = tanh(arg(n)) + b = eps(n) + csi * t + if (zero(b)) goto 13 + csi = eps(n) * (csi + t * eps(n)) / b + goto 11 +12 n = lstart +13 if (n <= 1) goto 14 + n = n - 1 + csi = eps(n) / tanh(arg(n)) + goto 11 +14 surlos = 0.0d0 + return +15 a = csi + 1.0 + if (zero(a)) then + surlos = 2.0d0 / epsmac + else + surlos = aimag(-2.0 / a) + endif + return +end function surlos +! ######################################################################### + +double precision function phint(phi, a, u) + +! ****************************************************************** +! * * +! * evaluate the integral from zero to phi of * +! * * +! * u 2 * +! * ( ----------------------------- ) dphi * +! * 2 2 * +! * (1 - a * u * cos(phi)) + u * +! * * +! * for 0 <= phi <= pi , u >= 0 and a >= 0 * +! * * +! ****************************************************************** + + implicit none + + double precision, intent(in out) :: phi + double precision, intent(in) :: a + double precision, intent(in) :: u + + double precision :: ai, ar, bi, br, c, cpr, d, e, esr, pi, qr, ri, rm, root + double precision :: rp, rr, s, spr, tm, tp, u2, x, zeta, zetai, zetar, zr + + pi = 3.141592653589793238d0 + c = cos(phi) + s = sin(phi) + u2 = u**2 + e = a*u + if (u < 1.0d0 .and. e < 1.0d-02 * (1.0d0 + u2)) then + zr = 1.0d0 + u2 + esr = e / zr + phint = u2 / zr**2 * ((( (4.0d0 / 3.0d0) * (2.0d0 + c**2) * s * (5.0d0 - 3.0d0 * u2) * & + esr + (phi + c * s) * (5.0d0 - u2)) * esr + 4.0d0 * s) * esr + phi) + else + rm = sqrt((1.0d0 - e)**2 + u2) + tm = 0.5d0 * atan2(u, 1.0d0 - e) + rp = sqrt((1.0d0 + e)**2 + u2) + tp = 0.5d0 * atan2(u, 1.0d0 + e) + root = sqrt(rm * rp) + cpr = cos(tm + tp) + spr = sin(tm + tp) + if (c >= 0.0d0) then + x = s / (1.0d0 + c) + elseif (abs(s) > 1.0d-07) then + x = (1.0d0 - c) / s + endif + if ((c >= 0.0d0) .or. (abs(s) > 1.0d-07)) then + zeta = sqrt(rm / rp) + zetar = -zeta * sin(tm - tp) + zetai = zeta * cos(tm - tp) + br = 0.5d0 * log(((zetar + x)**2 + zetai**2) / ((zetar - x)**2 + zetai**2)) + bi = atan2(zetai, zetar + x) - atan2(zetai, zetar - x) + rr = -(br * spr - bi * cpr) / root + ri = -(bi * spr + br * cpr) / root + d = e * s / ((1.0d0 - e * c)**2 + u2) + ar = d * (1.0d0 - e * c) - rr + u * ri + ai = -d * u - ri - u * rr + else + rr = -pi / root * cpr + ri = pi / root * spr + ar = -rr + u * ri + ai = -ri - u * rr + endif + qr = (ar * (cpr - spr) * (cpr + spr) + 2.0d0 * ai * cpr * spr) / (rm * rp) + phint = 0.5d0 * (ri / u - qr) + endif + return +end function phint +! ################################################################ + +subroutine seteps(nos, osc_size, osc, epsinf, wn, nLayer, eps) + + ! ****************************************************************** + ! * set up long-wavelength dielectric functions of the layers for * + ! * the present frequency wn (in cm**-1) * + ! ****************************************************************** + + ! implicit none + integer, intent(in) :: nLayer + integer, dimension(nLayer),intent(in) :: nos + integer, intent(in) :: osc_size + double precision, dimension(3,osc_size),intent(in) :: osc + !f2py depend(osc_size) osc + double precision, dimension(nLayer),intent(in) :: epsinf + double precision, intent(in) :: wn + double complex, dimension(nLayer), intent(out) :: eps + !f2py depend(nLayer) nos, epsinf, eps + + double complex :: nomi, deno, addeps + double precision :: wn2, b + integer :: flag + logical debugFirstRun + common /control/ debugFirstRun + + j = 0 + do l = 1, nLayer ! loop over different thin film layers + m = nos(l)/2 ! m number of TO modes = offset to reach the LO mode in the joint TO-LO list + nomi = dcmplx(1.0d0, 0.0d0) + deno = dcmplx(1.0d0, 0.0d0) + addeps = dcmplx(0.0d0, 0.0d0) + wn2 = wn**2 + do k = 1, m ! loop over all TO modes + j = j + 1 + if (osc(1,j) > 0.) then ! positive TO mode: 'Kurosa' form + b = wn/osc(1, j+m) + nomi = nomi * osc(1, j+m)**2 * (1.0 - b * dcmplx(b, osc(3,j+m)/osc(1, j+m)) ) + deno =deno * (osc(1,j)**2 - wn * dcmplx( wn, osc(3,j) ) ) + else if (osc(1,j) < 0.) then! Negative TO mode means: treat as term added to epsilon + addeps = addeps + osc(1,j)**2 * osc(2,j) /dcmplx(osc(1,j)**2 + wn2, wn*osc(3,j)) + else ! osc(1,j) = 0 -> it is a Drude term + ! addeps = addeps - osc(1,j)**2/dcmplx(wn2, -1*wn*osc(3,j)) + addeps = addeps - dcmplx(osc(1,j+m)**2, wn*(osc(3,j+m)-osc(3,j))) /dcmplx(wn2, -1*wn*osc(3,j)) + end if + enddo + j = j+m ! we have already looped over the LO modes, therefore increase the index + eps(l) = epsinf(l) * nomi / deno + addeps + enddo + debugFirstRun = .false. + return +end subroutine seteps + +end module mod_doeels \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 1ca0fd6a3f0437fc1f30079dfeac138ccb5d0569..55ebbd7b1e4c24e07a9a5df9f383c907c3b4f584 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "libhreels" -version = "1.1.4" +version = "1.2.0" description = "Handling, simulating, and plotting HREELS and Auger spectroscopy data" authors = ["Wolf Widdra <wolf.widdra@physik.uni-halle.de>"] include = ["*./libhreels/*"]