def calc_Bragg_angle(crystal='Si', energy=8, h=1, k=1, l=1, corrected=False): ''' Calculates Bragg angle using xraylib. Parameters: - crystal: crystal material. [str] - energy: energy in keV. [float] - h, k, l: Miller indexes. [int] Returns: - bragg: Bragg angle in rad. [float] ''' cryst = xraylib.Crystal_GetCrystal(crystal) bragg = xraylib.Bragg_angle(cryst, energy, h, k, l) if (corrected): w0 = calc_rocking_curve_shift(crystal=crystal, energy=energy, h=h, k=k, l=l, rel_angle=1, debye_temp_factor=1) bragg += w0 return bragg
def calc_Darwin_width(crystal='Si', energy=8, h=1, k=1, l=1, rel_angle=1, debye_temp_factor=1): ''' Calculates Darwin width and intrinsic resolution for p and s polarizations. It uses xraylib. Parameters: - crystal: crystal material. [str] - energy: energy in keV. [float] - h, k, l: Miller indexes. [int] - rel_angle: relative angle [float] - debye_temp_factor: Debye Temperature Factor. [float] Returns: - dw_s: Angular Darwin width for s polarization in rad. [float] - dw_p: Angular Darwin width for p polarization in rad. [float] - resolution_s: Intrinsic resolution for s polarization. [float] - resolution_p: Intrinsic resolution for p polarization. [float] References: A simple formula to calculate the x-ray flux after a double-crystal monochromator - M. S. del Rio; O. MATHON. ''' cryst = xraylib.Crystal_GetCrystal(crystal) bragg = xraylib.Bragg_angle(cryst, energy, h, k, l) FH = xraylib.Crystal_F_H_StructureFactor(cryst, energy, h, k, l, debye_temp_factor, rel_angle) FHbar = xraylib.Crystal_F_H_StructureFactor(cryst, energy, -h, -k, -l, debye_temp_factor, rel_angle) C = abs(np.cos(2 * bragg)) dw_p = 1e10 * 2 * C * (xraylib.R_E / cryst['volume']) * ( xraylib.KEV2ANGST * xraylib.KEV2ANGST / (energy * energy)) * np.sqrt( abs(FH * FHbar)) / np.pi / np.sin(2 * bragg) dw_s = 1e10 * 2 * (xraylib.R_E / cryst['volume']) * ( xraylib.KEV2ANGST * xraylib.KEV2ANGST / (energy * energy)) * np.sqrt( abs(FH * FHbar)) / np.pi / np.sin(2 * bragg) resolution_p = dw_p / np.tan(bragg) resolution_s = dw_s / np.tan(bragg) return dw_s, dw_p, resolution_s, resolution_p
def calc_rocking_curve_shift(crystal='Si', energy=8, h=1, k=1, l=1, rel_angle=1, debye_temp_factor=1): ''' Calculates the angular shift of the Rocking Curve. It uses xraylib. Parameters: - crystal: crystal material. [str] - energy: energy in keV. [float] - h, k, l: Miller indexes. [int] - rel_angle: relative angle [float] - debye_temp_factor: Debye Temperature Factor. [float] Returns: - w0: Angular shift of the Rocking Curve in rad. [float] References: Elements of modern X-ray physics / Jens Als-Nielsen, Des McMorrow – 2nd ed. Cap. 6. ''' # Function: def calc_g(d, r0, Vc, F): return abs((2 * d * d * r0 / (Vc)) * F) # Calculating rocking curve shift: cryst = xraylib.Crystal_GetCrystal(crystal) bragg = xraylib.Bragg_angle(cryst, energy, h, k, l) r0 = physical_constants['classical electron radius'][0] F0 = xraylib.Crystal_F_H_StructureFactor(cryst, energy, 0, 0, 0, debye_temp_factor, rel_angle) d = 1e-10 * xraylib.Crystal_dSpacing(cryst, h, k, l) V = 1e-30 * cryst['volume'] g0 = calc_g(d, r0, V, F0) w0 = (g0 / (np.pi)) * np.tan(bragg) return w0
def angleBragg(self, energy): """ Returns the Bragg angle in rad for a given energy. :param energy: Energy to calculate the Bragg angle for. :return: Bragg angle. """ energy_in_kev = energy / 1000.0 # Retrieve bragg angle from xraylib. angle_bragg = xraylib.Bragg_angle(self._crystal, energy_in_kev, self.millerH(), self.millerK(), self.millerL()) return angle_bragg
def test_crystal_diffraction(self): crystals_list = xraylib.Crystal_GetCrystalsList() self.assertEqual(len(crystals_list), 38) for crystal_name in crystals_list: cs = xraylib.Crystal_GetCrystal(crystal_name) self.assertEqual(crystal_name, cs['name']) with self.assertRaises(ValueError): cs = xraylib.Crystal_GetCrystal(None) with self.assertRaises(ValueError): cs = xraylib.Crystal_GetCrystal("non-existent-crystal") cs = xraylib.Crystal_GetCrystal("Diamond") cs_copy = xraylib.Crystal_MakeCopy(cs) with self.assertRaises(ValueError): xraylib.Crystal_AddCrystal(cs) with self.assertRaises(ValueError): xraylib.Crystal_AddCrystal(cs_copy) cs_copy['name'] = "Diamond-copy" xraylib.Crystal_AddCrystal(cs_copy) cs_copy['name'] = 20012016 with self.assertRaises(TypeError): xraylib.Crystal_AddCrystal(cs_copy) cs_copy['name'] = "Diamond-copy" cs_copy['atom'] = list() with self.assertRaises(TypeError): xraylib.Crystal_AddCrystal(cs_copy) cs_copy['atom'] = (25, "jkewjfpwejffj", None, ) with self.assertRaises(TypeError): xraylib.Crystal_AddCrystal(cs_copy) del cs_copy['atom'] with self.assertRaises(KeyError): xraylib.Crystal_AddCrystal(cs_copy) crystals_list = xraylib.Crystal_GetCrystalsList() self.assertEqual(len(crystals_list), 39) for crystal_name in crystals_list: cs = xraylib.Crystal_GetCrystal(crystal_name) self.assertEqual(crystal_name, cs['name']) current_ncrystals = len(crystals_list) for i in range(xraylib.CRYSTALARRAY_MAX): cs_copy = xraylib.Crystal_MakeCopy(cs) cs_copy['name'] = "Diamond copy {}".format(i) if current_ncrystals < xraylib.CRYSTALARRAY_MAX: xraylib.Crystal_AddCrystal(cs_copy) current_ncrystals = current_ncrystals + 1 self.assertEqual(len(xraylib.Crystal_GetCrystalsList()), current_ncrystals) else: with self.assertRaises(RuntimeError): xraylib.Crystal_AddCrystal(cs_copy) self.assertEqual(len(xraylib.Crystal_GetCrystalsList()), xraylib.CRYSTALARRAY_MAX) cs = xraylib.Crystal_GetCrystal("Diamond") # Bragg angle angle = xraylib.Bragg_angle(cs, 10.0, 1, 1, 1) self.assertAlmostEqual(angle, 0.3057795845795849) with self.assertRaises(TypeError): angle = xraylib.Bragg_angle(None, 10.0, 1, 1, 1) with self.assertRaises(ValueError): angle = xraylib.Bragg_angle(cs, -10.0, 1, 1, 1) with self.assertRaises(TypeError): angle = xraylib.Bragg_angle(cs, 1, 1, 1) # Q_scattering_amplitude tmp = xraylib.Q_scattering_amplitude(cs, 10.0, 1, 1, 1, math.pi/4.0) self.assertAlmostEqual(tmp, 0.19184445408324474) tmp = xraylib.Q_scattering_amplitude(cs, 10.0, 0, 0, 0, math.pi/4.0) self.assertEqual(tmp, 0.0) # Atomic factors (f0, f_prime, f_prime2) = xraylib.Atomic_Factors(26, 10.0, 1.0, 10.0) self.assertAlmostEqual(f0, 65.15) self.assertAlmostEqual(f_prime, -0.22193271025027966) self.assertAlmostEqual(f_prime2, 22.420270655080493) with self.assertRaises(ValueError): (f0, f_prime, f_prime2) = xraylib.Atomic_Factors(-10, 10.0, 1.0, 10.0) # unit cell volume tmp = xraylib.Crystal_UnitCellVolume(cs) self.assertAlmostEqual(tmp, 45.376673902751) # crystal dspacing tmp = xraylib.Crystal_dSpacing(cs, 1, 1, 1) self.assertAlmostEqual(tmp, 2.0592870875248344) del cs
cryst['gamma'])) print("Si unit cell volume is {}".format(cryst['volume'])) print("Si atoms at:") print(" Z fraction X Y Z") for i in range(cryst['n_atom']): atom = cryst['atom'][i] print(" {} {} {} {} {}".format(atom['Zatom'], atom['fraction'], atom['x'], atom['y'], atom['z'])) print("") print("Si111 at 8 KeV. Incidence at the Bragg angle:") energy = 8 debye_temp_factor = 1.0 rel_angle = 1.0 bragg = xraylib.Bragg_angle(cryst, energy, 1, 1, 1) print(" Bragg angle: Rad: {} Deg: {}".format(bragg, bragg * 180 / math.pi)) q = xraylib.Q_scattering_amplitude(cryst, energy, 1, 1, 1, rel_angle) print(" Q Scattering amplitude: {}".format(q)) #notice the 3 return values!!! f0, fp, fpp = xraylib.Atomic_Factors(14, energy, q, debye_temp_factor) print(" Atomic factors (Z = 14) f0, fp, fpp: {}, {}, i*{}".format( f0, fp, fpp)) FH = xraylib.Crystal_F_H_StructureFactor(cryst, energy, 1, 1, 1, debye_temp_factor, rel_angle) print(" FH(1,1,1) structure factor: ({}, {})".format(FH.real, FH.imag)) F0 = xraylib.Crystal_F_H_StructureFactor(cryst, energy, 0, 0, 0,
def apply(self): if not self._input_available: raise Exception("AlignmentTool: Input data not available!\n") energy_in_kev = self.BASE_ENERGY / 1000.0 outgoing_bunch = PolarizedPhotonBunch([]) x_axis = Vector(1, 0, 0) neg_x_axis = Vector(-1, 0, 0) # Retrieve Bragg angle from xraylib. angle_bragg = xraylib.Bragg_angle(self.CRYSTAL, energy_in_kev, self.MILLER_H, self.MILLER_K, self.MILLER_L) # TODO: change rotation using the crystalpy tool to get full alignment. for i in range(self.incoming_bunch.getNumberOfPhotons()): polarized_photon = self.incoming_bunch.getPhotonIndex(i) if self.MODE == 0: # ray-to-crystal rotated_vector = polarized_photon.unitDirectionVector().\ rotateAroundAxis(neg_x_axis, angle_bragg + self.ALPHA_X*np.pi/180) elif self.MODE == 1: # crystal-to-ray rotated_vector = polarized_photon.unitDirectionVector(). \ rotateAroundAxis(neg_x_axis, angle_bragg - self.ALPHA_X*np.pi/180) else: raise Exception( "AlignmentTool: The alignment mode could not be interpreted correctly!\n" ) polarized_photon.setUnitDirectionVector(rotated_vector) outgoing_bunch.addPhoton(polarized_photon) # Dump data to file if requested. if self.DUMP_TO_FILE: print("AlignmentTool: Writing data in {file}...\n".format( file=self.FILE_NAME)) with open(self.FILE_NAME, "w") as file: try: file.write( "#S 1 photon bunch\n" "#N 9\n" "#L Energy [eV] Vx Vy Vz S0 S1 S2 S3 CircularPolarizationDegree\n" ) file.write(outgoing_bunch.toString()) file.close() print("File written to disk: %s \n" % self.FILE_NAME) except: raise Exception( "AlignmentTool: The data could not be dumped onto the specified file!\n" ) self.send("photon bunch", outgoing_bunch) print("AlignmentTool: Photon bunch aligned.\n")
def calc_Darwin_curve(delta_theta=np.linspace(-0.00015, 0.00015, 5000), crystal='Si', energy=8, h=1, k=1, l=1, rel_angle=1, debye_temp_factor=1, use_correction=True, save_txt=True, save_fig=True, filename_to_save='Darwin_curve'): ''' Calculates the Darwin curve. It does not considers absortion. Valid for s-polarization only. Parameters: - delta_theta: array containing values of (Theta - Theta_Bragg) in rad. [array] - crystal: crystal material. [str] - energy: energy in keV. [float] - h, k, l: Miller indexes. [int] - rel_angle: relative angle [float] - debye_temp_factor: Debye Temperature Factor. [float] - use_correction: if True, considers the corrected Bragg angle due to refraction. [boolean] - save_txt: if True, saves the Darwin Curve in a .txt file. [boolean] - save_fig: if True, saves a figure with the Darwin Curve in .png. [boolean] - filename_to_save: name to save the figure and the .txt file. [string] Returns: - delta_theta: Array containing values of (Theta - Theta_Bragg) in rad. [array] - R: Intensity reflectivity array. [array] - zeta_total: Total Darwin width (delta_lambda/lambda). [float] - zeta_FWHM: Darwin width FWHM (delta_lambda/lambda). [float] - w_total: Total Angular Darwin width (delta_Theta) in rad. [float] - w_FWHM: Angular Darwin width FWHM (delta_Theta) in rad. [float] - w0: Angular shift of the Rocking Curve in rad. [float] References: Elements of modern X-ray physics / Jens Als-Nielsen, Des McMorrow – 2nd ed. Cap. 6. ''' # Functions: def calc_g(d, r0, Vc, F): return abs((2 * d * d * r0 / (Vc)) * F) def calc_xc(zeta, g, g0): return np.pi * zeta / g - g0 / g def calc_zeta_total(d, r0, Vc, F): return (4 / np.pi) * (d) * (d) * (r0 * abs(F) / Vc) def Darwin_curve(xc): R = [] for x in xc: if (x >= 1): r = (x - np.sqrt(x * x - 1)) * (x - np.sqrt(x * x - 1)) if (x <= 1): r = 1 if (x <= -1): r = (x + np.sqrt(x * x - 1)) * (x + np.sqrt(x * x - 1)) R.append(r) return np.array(R) # Calculating Darwin curve: cryst = xraylib.Crystal_GetCrystal(crystal) bragg = xraylib.Bragg_angle(cryst, energy, h, k, l) zeta = delta_theta / np.tan(bragg) r0 = physical_constants['classical electron radius'][0] FH = xraylib.Crystal_F_H_StructureFactor(cryst, energy, h, k, l, debye_temp_factor, rel_angle) F0 = xraylib.Crystal_F_H_StructureFactor(cryst, energy, 0, 0, 0, debye_temp_factor, rel_angle) d = 1e-10 * xraylib.Crystal_dSpacing(cryst, h, k, l) V = 1e-30 * cryst['volume'] g = calc_g(d, r0, V, FH) g0 = calc_g(d, r0, V, F0) xc = calc_xc(zeta, g, g0) R = Darwin_curve(xc) zeta_total = calc_zeta_total(d, r0, V, FH) zeta_FWHM = (3 / (2 * np.sqrt(2))) * zeta_total w_total = zeta_total * np.tan(bragg) w_FWHM = (3 / (2 * np.sqrt(2))) * w_total w0 = (g0 / (np.pi)) * np.tan(bragg) # Correcting curve offset (due to refraction): if (use_correction): delta_theta = delta_theta - w0 # Saving .txt file: if (save_txt): filename = filename_to_save + '.txt' with open(filename, 'w') as f: f.write('#Delta_Theta[rad] Intensity_Reflectivity \n') for i in range(len(R)): f.write('%.6E\t%.6E \n' % (delta_theta[i], R[i])) # Plotting Graph: plt.figure() plt.plot(delta_theta, R, linewidth=1.8, color='black') plt.fill_between(delta_theta, R, alpha=0.9, color='C0') plt.ylabel('Intensity reflectivity', fontsize=13) plt.xlabel('$\Delta$' + '$\Theta$' + ' [rad]', fontsize=13) plt.xscale('linear') plt.yscale('linear') plt.minorticks_on() plt.ticklabel_format(style='sci', axis='x', scilimits=(0, 0)) plt.tick_params(which='both', axis='both', direction='in', right=True, top=True, labelsize=12) plt.grid(which='both', alpha=0.2) plt.tight_layout() textstr = '\n'.join((r'$\zeta_{total}=$%.4E' % (zeta_total, ), r'$\zeta_{FWHM}=$%.4E' % (zeta_FWHM, ), r'$\omega_{total}=$%.4E rad' % (w_total, ), r'$\omega_{FWHM}=$%.4E rad' % (w_FWHM, ))) props = dict(boxstyle='round', facecolor='wheat', alpha=0.5) # wheat # gray plt.text(0.05, 0.95, textstr, transform=plt.gca().transAxes, fontsize=10, verticalalignment='top', bbox=props) plt.show() if (save_fig): plt.savefig(filename_to_save + '.png', dpi=600) return delta_theta, R, zeta_total, zeta_FWHM, w_total, w_FWHM, w0