def cs_total(element,energy): """ Get the total cross section for a given element when excitated with x-rays of a given energy (keV) using the xraylib backend This is the total absorption cross section which is the sum of the photoionization cross section, the Rayleigh scattering cross section and the Compton scattering cross section Parameters: element : Name, Symbol or Atomic Number (Z) as input energy : energy in keV - scalar, list, tuple or numpy array Returns: cs_total: float or numpy array - same size as input energy total cross section for this element at this incident energy in cm2/g. """ z = elementDB[element]["Z"] if isinstance(energy, (list, tuple, np.ndarray)): xsec = np.zeros(len(energy)) for i,enrg in enumerate(energy): xsec[i] = xraylib.CS_Total(z,enrg) else: xsec = xraylib.CS_Total(z,energy) return xsec
def set_attenuation(__self__, energy): """ Sets the linear and total attenuation coefficients according to input. Values are taken from xraylib (Brunetti et al., 2004), which is constantly updated. -------------------------------------------------------------------------- INPUT: energy; int or string """ mu1_w, mu2_w = 0, 0 if type(energy) == int: for element in __self__.weight: ele_no = EnergyLib.Element_No[element] print(ele_no, element) mu1 = xlib.CS_Total(ele_no, energy) mu2 = 0 mu1_w += mu1 * __self__.weight[element] mu2_w += mu2 * __self__.weight[element] else: for element in __self__.weight: attenuated_no = EnergyLib.Element_No[energy] ele_no = EnergyLib.Element_No[element] attenergy_a = EnergyLib.Energies[attenuated_no] attenergy_b = EnergyLib.kbEnergies[attenuated_no] mu1, mu2 = xlib.CS_Total(ele_no, attenergy_a), xlib.CS_Total( ele_no, attenergy_b) mu1_w += mu1 * __self__.weight[element] mu2_w += mu2 * __self__.weight[element] __self__.tot_att = (mu1_w, mu2_w) __self__.lin_att = (__self__.density * mu1_w, __self__.density * mu2_w)
def detector_efficiency_convolution( data, spectrum_size, scatt_orders, rivelatore): Z = rivelatore.Detect_Z thickness = rivelatore.Detect_thickness Z_window = rivelatore.Detect_window_Z window_thick = rivelatore.Detect_window_thickness energy_max = rivelatore.energy_max #FILE *file_in,*file_out; i,j,k = 0,0,0 sigma_var = 0 density, density1 = 0, 0 density_air = 0.0012 attenuation = 0 attenuation_window = 0 attenuation_air = 0 energy = 0 air_thickness = rivelatore.AirThick #xrl_error **error; spectrum_temp = np.zeros(spectrum_size, dtype=np.double) spectrum_conv = np.zeros([scatt_orders,spectrum_size], dtype=np.double) k=0 print("Efficiency correction") ######################################################################### #NOTE: No longer performs the convolution PER ORDER. Instead, we sum #all orders and apply it only once ######################################################################### for k in range(scatt_orders): spectrum_temp += data[k,:] ######################################################################### density = xlib.ElementDensity(Z) density1 = xlib.ElementDensity(Z_window) i=0 for i in range(spectrum_size): energy = i*rivelatore.energy_max/rivelatore.MCA_chns print(f"{energy} - Progress {k}: {i}/{spectrum_size}", end="\r") if energy > 0: attenuation = xlib.CS_Total(Z, energy) attenuation_window = xlib.CS_Total(Z_window, energy) attenuation_air = 0.7804 * xlib.CS_Total(7, energy) + 0.20946 * xlib.CS_Total(8, energy) + 0.00934 * xlib.CS_Total(18, energy) spectrum_conv[k][i] = spectrum_temp[i] * (1.0-np.exp(-attenuation*thickness*density)) * np.exp(-attenuation_window * window_thick * density1-attenuation_air * air_thickness*density_air) else: spectrum_conv[k][i] = 0.0 return spectrum_conv
def CS_Total(ID,E=None): if E==None: E=pypsepics.get("SIOC:SYS0:ML00:AO627")/1000 E=eV(E)/1000 z=elementZ[ID] CS=xraylib.CS_Total(z,E) CS=CS*AtomicMass[ID]/c['NA']/u['cm']**2 return CS
def calc_cross_section(z, energy): """ Calculate the total cross section in cm^2/g using xraylib... Returns a numpy array... """ result = np.zeros_like(energy) for i, en in np.ndenumerate(energy): result[i] = xraylib.CS_Total(z, en) return result
def GetMaterialMu(E, data): # send in the photon energy and the dictionary holding the layer information Ele = data['Element'] Mol = data['MolFrac'] t = 0 for i in range(len(Ele)): t += xraylib.AtomicWeight(xraylib.SymbolToAtomicNumber(Ele[i]))*Mol[i] mu=0 for i in range(len(Ele)): mu+= (xraylib.CS_Total(xraylib.SymbolToAtomicNumber(Ele[i]),E) * xraylib.AtomicWeight(xraylib.SymbolToAtomicNumber(Ele[i]))*Mol[i]/t) return mu # total attenuataion w/ coherent scattering in cm2/g
def test_attenuator(): # # create preprocessor file # symbol = "Cu" number_of_rays = 10000 cm_or_mm = 0 # 0=using cm, 1=using mm # # run prerefl # density = xraylib.ElementDensity(xraylib.SymbolToAtomicNumber(symbol)) Shadow.ShadowPreprocessorsXraylib.prerefl(interactive=0, SYMBOL=symbol, DENSITY=density, FILE="prerefl.dat", E_MIN=5000.0, E_MAX=15000.0, E_STEP=100.0) # # run SHADOW for a sample of thickness 10 um # if cm_or_mm == 0: beam = run_example_attenuator(user_units_to_cm=1.0, npoint=number_of_rays) else: beam = run_example_attenuator(user_units_to_cm=0.1, npoint=number_of_rays) print("Intensity: %f" % (beam.intensity(nolost=1))) # # check SHADOW results against xraylib attenuation # cs1 = xraylib.CS_Total(xraylib.SymbolToAtomicNumber(symbol), 10.0) # print("xralib Fe cross section [cm^2/g]: %f"%cs1) # print("xralib mu [cm^-1]: %f"%(cs1*density)) sh100 = 100 * beam.intensity(nolost=1) / number_of_rays xrl100 = 100 * numpy.exp(-cs1 * density * 10e-4) print("xralib attenuation [per cent] for 10 um %s at 10 keV: %f" % (symbol, xrl100)) print("Transmission [per cent]: %f" % (sh100)) numpy.testing.assert_almost_equal(sh100, xrl100, 2)
'10%': estimate_measurement_time(0.10), '5%': estimate_measurement_time(0.05), '1%': estimate_measurement_time(0.01), '0.5%': estimate_measurement_time(0.005), '0.1%': estimate_measurement_time(0.001) } #estimate the amount of element of interest in the sample #convert the absorption edge string to energy edge_element, edge_shell = inputs['edge_string'].split('-') Z = xrl.SymbolToAtomicNumber(edge_element) edge_energy = xrl.EdgeEnergy(Z, _SHELL_MACROS[edge_shell]) murho = [ xrl.CS_Total(Z, edge_energy - 0.001), xrl.CS_Total(Z, edge_energy + 0.001) ] mass_surface_density = deltamux / (murho[1] - murho[0]) print('') print('OUTPUT:') print( u' Estimated sample µx (below edge, above edge, edge step): %.3f, %.3f, %.3f ' % (mux_below, mux_above, deltamux)) print( u' Estimated sample µx (below edge, above edge, edge step): %.3f, %.3f, %.3f ' % (mux_below, mux_above, deltamux)) print(u' Optimal transmitted-to-direct beam acquisition time ratio: %.2f' % time_ratio)
def outgoing_cmam(p, q, maia_d, angle, energy, increasing_ix=True): """Compute and return the outgoing cumulative multiplicative absorption map (cmam), aka xi'. Parameters ---------- p : Phantom2d object p.energy - incident beam photon energy (keV). p.um_per_px - length of one pixel of the map (um). q : int Maia detector channel id maia_d : Maia() instance angle : float Tomography projection angle (degree) energy : float outgoing radiation energy (keV) increasing_ix : bool, optional If False, performs cumulative sum in opposite direction (in direction of decreasing y-index). Note, for the standard Radon transform, this direction is unimportant, but for the Radon transform with attenuation, we should project in the beam propagation direction. (default True). Returns ------- 2d ndarray of float cumulative multiplicative absorption map. """ # The linear absorption map mu = ma_M * mu_M + sum_k ( ma_k * mu_k ) mu = p.matrix.ma(energy) * p.el_maps['matrix'] for el in p.el_maps: if el == 'matrix': continue Z = xrl.SymbolToAtomicNumber(el) mu += xrl.CS_Total(Z, energy) * p.el_maps[el] # project at angle theta by rotating the phantom by angle -theta and # projecting along the z-axis (along columns) # rotate by the sum of the projection angle and local detector angle # Get angle to rotate maps so that propagation toward detector plane # corresponds with direction to maia detector element phi_x = maia_d.pads[q].angle_X_rad phix_deg = rad_to_deg(phi_x) # Apply local rotation, accumulation and rotation operators # Apply R_{-theta} operator to mu # Apply R_{-phi} operator mu = rotate(mu, angle + 180 - phix_deg) saveflag = np.isclose( angle, config.cmam_angle_save) and config.save_projection_images if saveflag: path = os.path.join(config.mlem_im_path, 'project_cmam1_%03d.tif' % config.i) helpers.write_tiff32(path, mu) # Apply C_z operator if increasing_ix: mu = np.cumsum(mu, axis=0) else: mu = np.cumsum(mu[::-1], axis=0)[::-1] if saveflag: path = os.path.join(config.mlem_im_path, 'project_cmam2_%03d.tif' % config.i) helpers.write_tiff32(path, mu) # Apply R_{phi} operator mu = rotate(mu, 180 + phix_deg) t = p.um_per_px / UM_PER_CM if saveflag: path = os.path.join(config.mlem_im_path, 'project_cmam3_%03d.tif' % config.i) helpers.write_tiff32(path, mu) phi = maia_d.pads[q].out_of_xz_plane_angle cmam = mu * t / np.cos(phi) return cmam
def irradiance_map(p, angle, n0=1.0, increasing_ix=True, matrix_only=False): """Generates the image-sized map of irradiance [1/(cm2 s)] at each 2d pixel for a given angle accounting for absorption at the incident energy by the full elemental distribution. Note: In the full biological approximation, where we only need to consider absorption by the matrix and not by the full elemental composition, we could cache this result, possibly by adding the memoization decorator I use in Sakura here: https://github.com/AustralianSynchrotron/Sakura/blob/master/utils.py https://github.com/AustralianSynchrotron/Sakura/blob/master/memoize_core.py Parameters ---------- p : phantom object p.energy - incident beam energy (keV) p.um_per_px - length of one pixel of the map (um) angle : float tomography angle in degrees. The rotation axis is the y-axis in a conventional xyz right-handed coordinate system, so positive rotation in the xz-plane is ccw. i.e. with y(out-of-page) o--> x | v z therefore, using our rotate function, which assumes 2d rotation n0 : float incident irradiance (default 1.0). increasing_ix : bool, optional If False, performs cumulative sum in opposite direction (in direction of decreasing y-index). Note, for the standard Radon transform, this direction is unimportant, but for the Radon transform with attenuation, we should project in the beam propagation direction. (default True). matrix_only : bool, optional If True, the map only considers the matrix and doesn't consider the other elements in the model. (default False). Returns ------- 2d ndarray of float The irradiance map. """ # matrix_map = zero_outside_circle(p.el_maps['matrix']) # The linear absorption map mu0 = ma_M * mu_M + sum_k ( ma_k * mu_k ) mu0 = p.matrix.ma(p.energy) * p.el_maps['matrix'] if not config.absorb_with_matrix_only: for el in p.el_maps: if el == 'matrix': continue Z = xrl.SymbolToAtomicNumber(el) mu0 += xrl.CS_Total(Z, p.energy) * p.el_maps[el] # project at angle theta by rotating the phantom by angle -theta and # projecting along the z-axis (along columns) im = rotate(mu0, angle) # rotate by angle degrees ccw t = p.um_per_px / UM_PER_CM # accumulate along z-axis (image defined in xz-coordinates, so accumulate # along image rows), consistent with matlab sinogram convention. # See http://www.mathworks.com/help/images/radon-transform.html if increasing_ix: cmam = t * np.cumsum(im, axis=0) else: cmam = t * np.cumsum(im[::-1], axis=0)[::-1] if config.no_in_absorption: n_map = n0 + np.zeros_like(cmam) else: n_map = n0 * exp(-cmam) return n_map
def ma(self, energy): return sum([ self.cp[el] * xrl.CS_Total(xrl.SymbolToAtomicNumber(el), energy) for el in self.cp ])
def calc_escape_peak_ratios(lineE, detectorelement='Si'): """ Calculate ratio of escape peak to main peak based on emperical calculation from Alves et. al. Parameters ---------- detectorelement : string "Si" or "Ge" Returns ------- ratio of a single Si K escape peak to a single input peak or ratio of Ge Ka, Kb escape peaks to a single input peak References ---------- [1] "Experimental X-Ray peak-shape determination for a Si(Li) detector", L.C. Alves et al., Nucl. Instr. and Meth. in Phys. Res. B 109|110 (1996) 129-133. """ if (detectorelement == 'Si'): # # For Si the K peak is 95% of the transition # and the photoionization to total cross section is ~ 95% # Si escape peak is typically only 0.2-1% (bigger at lower energies) # jump = xl.JumpFactor(14, xl.K_SHELL) fluy = xl.FluorYield(14, xl.K_SHELL) corr = fluy * (jump - 1.0) / jump corr_photo = xl.CS_Photo(14, lineE) / xl.CS_Total(14, lineE) corr_trans = xl.RadRate(14, xl.KA_LINE) + xl.RadRate(14, xl.KB_LINE) mu_si = xl.CS_Total(14, lineE) mu_internal = xl.CS_Total(14, 1.73998) r = mu_internal / mu_si eta = corr_trans * corr_photo * corr * 0.5 * (1.0 - r * log(1.0 + 1.0 / r)) ratio = eta / (1.0 - eta) # # escape peak sigma should be narrower than the main peak. # return ratio else: # # Ge detector... # Ge has a large escape peak ratio ~ 5-15% and a Ka and kb component # if (lineE < 11.5): return 0.0, 0.0 jump = xl.JumpFactor(32, xl.K_SHELL) fluy = xl.FluorYield(32, xl.K_SHELL) corr = fluy * (jump - 1.0) / jump corr_photo = xl.CS_Photo(32, lineE) / xl.CS_Total(32, lineE) corr_trans_ka = xl.RadRate(32, xl.KA_LINE) corr_trans_kb = xl.RadRate(32, xl.KB_LINE) mu_ge = xl.CS_Total(32, lineE) # # one for the Ka and one for the Kb peak... mu_internal_ka = xl.CS_Total(32, xl.LineEnergy(32, xl.KA_LINE)) r_ka = mu_internal_ka / mu_ge eta_ka = corr_trans_ka * corr_photo * corr * 0.5 * ( 1.0 - r_ka * log(1.0 + 1.0 / r_ka)) ratio_ka = eta_ka / (1.0 - eta_ka) mu_internal_kb = xl.CS_Total(32, xl.LineEnergy(32, xl.KB_LINE)) r_kb = mu_internal_kb / mu_ge eta_kb = corr_trans_kb * corr_photo * corr * 0.5 * ( 1.0 - r_kb * log(1.0 + 1.0 / r_kb)) ratio_kb = eta_kb / (1.0 - eta_kb) return ratio_ka, ratio_kb
def test_refractiveindex(self): density = float(2.328) c = compoundfromformula.CompoundFromFormula("Si", density, name="silicon") energy = np.asarray([5, 10], dtype=float) Z = 14 # Xraylib (TODO: n_im sign is wrong!) n_re0 = np.asarray( [xraylib.Refractive_Index_Re("Si", e, density) for e in energy] ) n_im0 = -np.asarray( [xraylib.Refractive_Index_Im("Si", e, density) for e in energy] ) # Exactly like Xraylib (TODO: CS_Total -> CS_Photo_Total) delta = ( density * 4.15179082788e-4 * (Z + np.asarray([xraylib.Fi(Z, e) for e in energy])) / xraylib.AtomicWeight(Z) / energy ** 2 ) beta = ( np.asarray([xraylib.CS_Total(Z, e) for e in energy]) * density * 9.8663479e-9 / energy ) n_re1 = 1 - delta n_im1 = -beta np.testing.assert_allclose(n_re0, n_re1) np.testing.assert_allclose(n_im0, n_im1) # Im: Kissel n_im1b = ( -np.asarray([xraylib.CS_Total_Kissel(Z, e) for e in energy]) * density * 9.8663479e-9 / energy ) # Im: Kissel with pint n_im1d = ( ureg.Quantity(c.mass_att_coeff(energy), "cm^2/g") * ureg.Quantity(c.density, "g/cm^3") * ureg.Quantity(energy, "keV").to("cm", "spectroscopy") / (4 * np.pi) ) n_im1d = -n_im1d.to("dimensionless").magnitude r = n_im1b / n_im1d np.testing.assert_allclose(r, r[0]) # Im: other formula n_im1c = ( density * 4.15179082788e-4 * (np.asarray([xraylib.Fii(Z, e) for e in energy])) / xraylib.AtomicWeight(Z) / energy ** 2 ) # Spectrocrunch n_re2 = c.refractive_index_real(energy) n_im2 = c.refractive_index_imag(energy) np.testing.assert_allclose(n_re2, n_re0) np.testing.assert_allclose(n_im2, n_im1c, rtol=1e-6)
def capXsect(element, energy): C = xl.CS_Total(symToNum(element), energy) return C
def tube_atten(E, Z, D): expon = -xrl.CS_Total(Z, E) * D * 1e-4 * xrl.ElementDensity(Z) ta = exp(expon) return ta
def _CS_Total_Layer(layer, E): rv = 0.0 for i in zip(layer.Z, layer.weight): (Z, weight) = i rv += weight * xrl.CS_Total(Z, E) return rv
photons_per_second = float( input('Incident photon flux (background subtracted) (ph/s): ')) compound = xrl.CompoundParser(input_str['compound']) #convert the absorption edge string to energy edge_element, edge_shell = input_str['absorption_edge'].split('-') edge_energy = xrl.EdgeEnergy(xrl.SymbolToAtomicNumber(edge_element), _SHELL_MACROS[edge_shell]) #calculate the mass attenuation coefficient below and above edge murho = [0, 0] for i in range(compound['nElements']): murho[0] = murho[0] + compound['massFractions'][i] * xrl.CS_Total( compound['Elements'][i], edge_energy - 0.001) murho[1] = murho[1] + compound['massFractions'][i] * xrl.CS_Total( compound['Elements'][i], edge_energy + 0.001) #calculate filler attenuation using an "average" nitrogen filter filler_mux = xrl.CS_Total(7, edge_energy) * 1.5 * 0.1 #Measurement specific values for background count rates, estimates are used beta = 1.1 gamma = 0.03 * exp(filler_mux) #optimization condition f = 0 def f(x, mu0, mu, beta=1, gamma=0, filler_mux=0): aux1 = sqrt(8 * beta * exp(-filler_mux)) * sqrt( exp(mu0 * x) + exp(mu * x) + gamma *