xrr.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. # -*- coding: utf-8 -*-
  2. """
  3. Created on Mon Aug 20 10:03:37 2018
  4. @author: USER
  5. """
  6. # Codes are free to use. Do whatever you want
  7. from __future__ import absolute_import
  8. """Read raw data"""
  9. ####################### LIBRARY #############################
  10. # exceptions library
  11. from exceptions import (Excel_Load_Exception,
  12. Data_Load_Exception,
  13. Data_Format_Exception)
  14. # Python stdlib imports
  15. import os
  16. import pickle
  17. # data processing library
  18. import numpy as np
  19. import pandas as pd
  20. import matplotlib.pyplot as plt
  21. # pyrod library
  22. from read.read_parameters import initialization_parameters
  23. from read.read_raw_data import initialization_rhkl
  24. ####################### CONSTANT ############################
  25. # constant
  26. R0 = 2.82e-5 # Thompson scattering length, the radi of electron in Angs
  27. NA = 6.022e23 # Avogadros number corresponding to g/cm3 e23
  28. # atomic_mass.xlsx and atomic_scattering_factor.pickle
  29. ATOMIC_MASS = os.path.abspath(os.path.dirname('atomic_mass.xlsx')) +\
  30. '/base/atomic_mass.xlsx'
  31. ASF = os.path.abspath(os.path.dirname('atomic_scattering_factor.pickle')) +\
  32. '\\base\\atomic_scattering_factor.pickle'
  33. try:
  34. ATOM_TABLE = pd.read_excel(ATOMIC_MASS,
  35. sheet_name = 'Sheet1',
  36. index_col = 0)
  37. ATOM_LIST = ATOM_TABLE.index.values.tolist()
  38. f = open(ASF,'rb')
  39. ASF_TABLE = pickle.load(f)
  40. f.close()
  41. except Excel_Load_Exception:
  42. print('load atomic form factor data base load fail')
  43. # load ctr optimised data
  44. CTR_PATH = os.path.abspath(os.path.dirname('ctr_optimised_result.pickle')) +\
  45. '/data/ctr_optimised_result.pickle'
  46. ctr_optimised_result = {}
  47. try:
  48. with open(CTR_PATH, 'rb') as f:
  49. unpickler = pickle.Unpickler(f)
  50. ctr_optimised_result = unpickler.load()
  51. except Data_Load_Exception:
  52. print('Please optimise and save the CTR data first!')
  53. # save xrr optimised data path
  54. XRR_PATH = os.path.abspath(os.path.dirname('xrr_optimised_result.pickle')) +\
  55. '/data/xrr_optimised_result.pickle'
  56. ####################### FUNCTIONS ###########################
  57. # elements = [A,B]
  58. # result density - g/cm3
  59. def density(elements,lattice):
  60. #elements = ['Sr','Ti']
  61. #lattice = [3.905,3.905,3.905,np.pi/2,np.pi/2,np.pi/2]
  62. """theroy density of ABO3 pervoskite oxide"""
  63. try:
  64. A_mass = ATOM_TABLE.at[elements[0],'mass']
  65. B_mass = ATOM_TABLE.at[elements[1],'mass']
  66. O_mass = ATOM_TABLE.at['O','mass']
  67. a = lattice[0]
  68. b = lattice[1]
  69. c = lattice[2]
  70. alpha = lattice[3]*np.pi/180
  71. beta = lattice[4]*np.pi/180
  72. gamma = lattice[5]*np.pi/180
  73. except Data_Format_Exception:
  74. print('Parameters error!')
  75. lattice_volume = a*b*c*np.sqrt(1 + 2*np.cos(alpha)*np.cos(beta)*np.cos(gamma)-\
  76. np.cos(alpha)**2 - \
  77. np.cos(beta)**2 - \
  78. np.cos(gamma)**2)
  79. A_unitcell = A_mass
  80. B_unitcell = B_mass
  81. O_unitcell = 3*O_mass
  82. na = NA/1e23
  83. density_A = A_unitcell/(lattice_volume*na*0.1)
  84. density_B = B_unitcell/(lattice_volume*na*0.1)
  85. density_O = O_unitcell/(lattice_volume*na*0.1)
  86. density_pervoskite = density_A + density_B + density_O
  87. return density_pervoskite, density_A, density_B, density_O
  88. # wave length should be Angs
  89. # energy is the x-ray energy kev
  90. # elements = [A,B]
  91. def imag_f(elements, energy):
  92. """imag part of x-ray scattering factor for a certin element"""
  93. A_energys = ASF_TABLE[elements[0]]['E(kev)']
  94. B_energys = ASF_TABLE[elements[1]]['E(kev)']
  95. O_energys = ASF_TABLE['O']['E(kev)']
  96. A_imagf = ASF_TABLE[elements[0]]['imag_f']
  97. B_imagf = ASF_TABLE[elements[1]]['imag_f']
  98. O_imagf = ASF_TABLE['O']['imag_f']
  99. def f2(energy, energys, imag_f):
  100. atom_f2 = np.interp(energy, energys, imag_f)
  101. return atom_f2
  102. A_f2 = f2(energy, A_energys, A_imagf)
  103. B_f2 = f2(energy, B_energys, B_imagf)
  104. O_f2 = f2(energy, O_energys, O_imagf)
  105. return A_f2, B_f2, O_f2
  106. # energy is x-ray energy kev
  107. # elements = [A,B]
  108. # atttenuation coefficiment unit is Angs
  109. def attenuation_coefficient(elements, densities, f2, energy):
  110. wave_length = 12.38/energy # unit Angs
  111. na = NA/1e23
  112. A_ac = (densities[1]*na/ATOM_TABLE.at[elements[0],'mass'])*2*R0*wave_length*f2[0]
  113. B_ac = (densities[2]*na/ATOM_TABLE.at[elements[1],'mass'])*2*R0*wave_length*f2[1]
  114. O_ac = (densities[3]*na/ATOM_TABLE.at['O','mass'])*2*R0*wave_length*f2[2]
  115. ac = A_ac + B_ac + O_ac
  116. return ac
  117. ######################## CLASSS #############################
  118. class XRR(initialization_parameters, initialization_rhkl):
  119. def __init__(self, parameters, experiment_data, energy):
  120. initialization_parameters.__init__(self, parameters)
  121. initialization_parameters._var_list(self)
  122. initialization_parameters._var_table(self)
  123. initialization_rhkl.__init__(self, experiment_data)
  124. self.energy = energy # unit - kev
  125. self.wave_length = 12.38/energy # unit-Angs
  126. self.coefficient = {}
  127. self.iq,self.i = self._read_rhkl(self)
  128. self.lattice_c = self.var_list['substrate']['lattice'].at['constant','c']
  129. self.q = self.iq*2*np.pi/self.lattice_c
  130. for key in self.var_list:
  131. element = [self.var_list[key]['ions'].columns[3], self.var_list[key]['ions'].columns[0]]
  132. lattice = self.var_list[key]['lattice'].as_matrix().tolist()[0]
  133. densities = density(element, lattice)
  134. f2 = imag_f(element, energy)
  135. ac = attenuation_coefficient(element, densities, f2, energy)
  136. self.coefficient[key] = [element,lattice,densities,ac]
  137. # stack list
  138. self.scattering_factor = [] # density*R0 + 1j*attenuation_coefficient
  139. self.d_space = [] # stack thickness
  140. self.roughness = [] # roughness at each layer
  141. for layeri in range(len(self.var_table['posz_list'])):
  142. self.d_space.append(self.var_table['posz_table'][0,0]*self.lattice_c*0.98)
  143. self.roughness.append(0)
  144. slab = self.var_table['slab_list'][layeri]
  145. dens = self.coefficient[slab][2][0]
  146. ac = self.coefficient[slab][3]
  147. self.scattering_factor.append(dens*R0+1j*ac)
  148. self.scattering_factor.append(1e-21) # the scattering factor of vacuum
  149. self.roughness.append(0)
  150. self.roughness[0] = 0
  151. self.thickness = np.sum(self.d_space)
  152. # rt - ratio
  153. self.rt = 1
  154. # re initilization the factors-d_space, scattering_factor, roughness, thichness
  155. def re(self):
  156. # stack list
  157. self.scattering_factor = [] # density*R0 + 1j*attenuation_coefficient
  158. self.d_space = [] # stack thickness
  159. self.roughness = [] # roughness at each layer
  160. for layeri in range(len(self.var_table['posz_list'])):
  161. self.d_space.append(self.var_table['posz_table'][0,0]*self.lattice_c*0.98)
  162. self.roughness.append(0)
  163. slab = self.var_table['slab_list'][layeri]
  164. dens = self.coefficient[slab][2][0]
  165. ac = self.coefficient[slab][3]
  166. self.scattering_factor.append(dens*R0+1j*ac)
  167. self.scattering_factor.append(1e-21) # the scattering factor of vacuum
  168. self.roughness.append(0)
  169. self.roughness[0] = 0
  170. self.thickness = np.sum(self.d_space)
  171. def disp(self):
  172. # list all the self parameters
  173. print("--------------------------------------------")
  174. print("densities:\n")
  175. for key in self.coefficient:
  176. print(" " + key + ": %s\n" %self.coefficient[key][2][0])
  177. print("scattering factor: %s - %s\n" % (self.scattering_factor[ 0],
  178. self.scattering_factor[-1]))
  179. print("roughness: %s - %s\n" % (self.roughness[ 0],
  180. self.roughness[-1]))
  181. print("d_space: %s - %s Angs\n" % (self.d_space[ 0],
  182. self.d_space[-1]))
  183. print("thichness: %s Angs\n" % np.sum(self.d_space))
  184. print('--------------------------------------------')
  185. # homogeneous slab reflection
  186. def homo_slab(self):
  187. slabs = list(self.var_list.keys())
  188. slabs.remove('substrate')
  189. # check if the slab is homogeneous
  190. if len(slabs) != 1:
  191. raise Data_Format_Exception('Not homogeneous slab!')
  192. dens = self.coefficient[slabs[0]][2][0]
  193. thickness = np.sum(self.d_space)
  194. reflect_inten = -1j*(4*np.pi*dens*R0*thickness/self.q)*\
  195. (np.sin(self.q*thickness/2)/(self.q*thickness/2))*\
  196. np.exp(1j*self.q*thickness/2)
  197. return self.iq, reflect_inten
  198. # Parratt reflectivities
  199. # Maybe a little difficult while optimizing parameter, parratt model is accurate
  200. # Recomed method
  201. def parratt(self):
  202. """"Elements of Modern X-ray Physics" by Jens Als-Nielsen and Des McMorrow,
  203. Calculates: Parratt reflectivity of a multilayer"""
  204. k = 2*np.pi/self.wave_length # diffraction vector#
  205. layer_num = len(self.scattering_factor) # layer number, vacuum is added
  206. #----- Calculate refractive index n of each layer
  207. delta = self.wave_length**2*np.real(self.scattering_factor)/(2*np.pi)
  208. beta = self.wave_length*np.imag(self.scattering_factor)/(4*np.pi)
  209. # relfractive index
  210. # nu = 1 - delta + 1j*beta
  211. #----- Wavevector transfer in each layer
  212. trans_vector = np.zeros([layer_num+1, len(self.q)], dtype = np.complex)
  213. trans_vector[0,:] = self.q
  214. for i in range(layer_num):
  215. trans_vector[i+1,:] = np.sqrt(self.q**2 - 8*k**2*delta[i] + 1j*8*k**2*beta[i])
  216. #----- Reflection coefficients (no multiple scattering)
  217. reflect_coe = np.zeros([layer_num, len(self.q)], dtype = np.complex)
  218. for i in range(layer_num):
  219. reflect_coe[i,:] = ((trans_vector[i,:] - trans_vector[i+1,:])/\
  220. (trans_vector[i,:] + trans_vector[i+1,:]))*\
  221. np.exp(-0.5*trans_vector[i,:]*trans_vector[i+1,:]*self.roughness[i])
  222. #----- Reflectivity from first layer
  223. reflectivity = np.zeros([layer_num-1, len(self.q)], dtype = np.complex)
  224. phase1 = np.exp(1j*trans_vector[layer_num-1]*self.d_space[layer_num-2])
  225. if layer_num > 1:
  226. reflectivity[0,:] = (reflect_coe[layer_num-2,:] + \
  227. reflect_coe[layer_num-1,:]*phase1)/\
  228. (1 + reflect_coe[layer_num-2,:]*\
  229. reflect_coe[layer_num-1,:]*phase1)
  230. if layer_num > 2:
  231. for i in range(1,layer_num-1):
  232. phasei = np.exp(1j*trans_vector[layer_num-i-1,:]*self.d_space[layer_num-i-2])
  233. reflectivity[i,:] = (reflect_coe[layer_num-i-2,:] +\
  234. reflectivity[i-1,:]*phasei)/\
  235. (1 + reflect_coe[layer_num-i-2,:]*\
  236. reflectivity[i-1,:]*phasei)
  237. #------ Intensity reflectivity
  238. # should be reminded here! The data is not squared! To keep uniform with ctr fitting
  239. if layer_num == 1:
  240. reflect_inten = reflect_coe[0,:]
  241. else:
  242. reflect_inten = reflectivity[-1,:]
  243. return self.iq, reflect_inten
  244. # def p_xrr(q, inten):
  245. #
  246. # plt.plot(q, np.log(inten))
  247. class xr(XRR):
  248. # the relax of surface, secface and interface
  249. # parratt method only
  250. def xr_relax(self, interface = 0.9, secface = 1, surface = 1.03, rt = 1):
  251. XRR.re(self)
  252. self.rt = rt
  253. self.d_space[ 0] = interface
  254. self.d_space[-2] = secface
  255. self.d_space[-1] = surface
  256. q, inten = XRR.parratt(self)
  257. plt.plot(q, np.log(abs(self.rt*inten + ctr_optimised_result['substrate_ctr'])))
  258. plt.plot(q, np.log(abs(ctr_optimised_result['shkl'])))
  259. return q, inten
  260. # modulate the roughness at interface and surface
  261. # parratt method only
  262. def xr_roughness(self, interface = 0.1, surface = 0.1, rt = 1):
  263. XRR.re(self)
  264. self.rt = rt
  265. self.roughness[ 0] = interface
  266. self.roughness[-1] = surface
  267. q, inten = XRR.parratt(self)
  268. plt.plot(q, np.log(abs(rt*inten + ctr_optimised_result['substrate_ctr'])))
  269. plt.plot(q, np.log(abs(ctr_optimised_result['shkl'])))
  270. return q, inten
  271. # thickness modulation
  272. # homo slab method or parratt method
  273. def xr_thickness(self, miu = 0, mode = 'parratt', rt = 1):
  274. XRR.re(self)
  275. self.rt = rt
  276. self.d_space = (np.array(self.d_space) - miu).tolist()
  277. if mode == 'homo_slab':
  278. q, inten = XRR.homo_slab(self)
  279. elif mode == 'parratt':
  280. q, inten = XRR.parratt(self)
  281. plt.plot(q, np.log(abs(rt*inten + ctr_optimised_result['substrate_ctr'])))
  282. plt.plot(q, np.log(abs(ctr_optimised_result['shkl'])))
  283. return q, inten
  284. # export optimised xrr data to /data
  285. def save(self, mode = 'parratt'):
  286. xrr_optimised_result = {}
  287. if mode == 'homo_slab':
  288. q, inten = XRR.homo_slab(self)
  289. elif mode == 'parratt':
  290. q, inten = XRR.parratt(self)
  291. xrr_optimised_result['q'] = q
  292. xrr_optimised_result['inten'] = inten
  293. xrr_optimised_result['rt'] = self.rt
  294. f = open(XRR_PATH,'wb')
  295. pickle.dump(xrr_optimised_result, f)
  296. f.close()