def __init__(self, name, ele_file_name=ELE_FILE_NAME): ele_file = file(ele_file_name) ele_lines = ele_file.readlines() self.ChemicalSymbol = None for ll in ele_lines: l = ll.split() if len(l) > 0 and l[0] == name: self.ChemicalSymbol = name self.AtomicNumber = int(l[1]) self.AtomicWeight = float(l[2]) self.kEdge = float(l[3]) self.FluorescenceYield = float(l[4]) break if self.ChemicalSymbol == None: logger.error("No element %s found, please update file %s" %\ (name, ele_file_name)) # The Density is re-calculated in gasmix:eval_properties # as partial density self.Density = self.AtomicWeight / MOLAR_VOLUME # Returns the mean ionization potential. # This parametrization is from Berger and Seltzer (1964); # see Joy's book for the reference. # The ionization potential is returned in keV. self.MeanIonizationPotential = 0.001*(9.76*self.AtomicNumber +\ 58.5/np.power(self.AtomicNumber, 0.19)) # eval X section self.GetPhotoelectricCrossSection = self.EvalPhotoelectricCrossSection( )
def __init__(self, name, ele_file_name = ELE_FILE_NAME): ele_file = file(ele_file_name) ele_lines = ele_file.readlines() self.ChemicalSymbol = None for ll in ele_lines: l = ll.split() if len(l)>0 and l[0] == name: self.ChemicalSymbol = name self.AtomicNumber = int(l[1]) self.AtomicWeight = float(l[2]) self.kEdge = float(l[3]) self.FluorescenceYield = float(l[4]) break if self.ChemicalSymbol == None: logger.error("No element %s found, please update file %s" %\ (name, ele_file_name)) # The Density is re-calculated in gasmix:eval_properties # as partial density self.Density = self.AtomicWeight/MOLAR_VOLUME; # Returns the mean ionization potential. # This parametrization is from Berger and Seltzer (1964); # see Joy's book for the reference. # The ionization potential is returned in keV. self.MeanIonizationPotential = 0.001*(9.76*self.AtomicNumber +\ 58.5/np.power(self.AtomicNumber, 0.19)) # eval X section self.GetPhotoelectricCrossSection = self.EvalPhotoelectricCrossSection()
def GetRutherfordScreeningFactor(self, energy): """ \brief Returns the Rutherford screening factor at a given anergy. This parametrization is from Bishop (1976); see Joy's book for the reference. Energy to be provided in keV (accurate down to 50 eV) """ if isinstance( energy , (int, float) ): energy = np.array([energy]) if energy.any()< MIN_ANALYTIC_CROSS_SECTIONS_ENERGY: logger.error("Cannot eval screening factor below %f keV"%\ MIN_ANALYTIC_CROSS_SECTIONS_ENERGY) return -1 return (3.4E-3)*np.power(self.AtomicNumber, 0.67)/energy
def GetRutherfordScreeningFactor(self, energy): """ \brief Returns the Rutherford screening factor at a given anergy. This parametrization is from Bishop (1976); see Joy's book for the reference. Energy to be provided in keV (accurate down to 50 eV) """ if isinstance(energy, (int, float)): energy = np.array([energy]) if energy.any() < MIN_ANALYTIC_CROSS_SECTIONS_ENERGY: logger.error("Cannot eval screening factor below %f keV"%\ MIN_ANALYTIC_CROSS_SECTIONS_ENERGY) return -1 return (3.4E-3) * np.power(self.AtomicNumber, 0.67) / energy
def GetStoppingPower(self, energy): """ \brief Returns the stopping power at a given energy. This parametrization is from Joy and Luo (1989); see Joy's book for the reference. Energy to be provided in keV, outcome in keV/cm. """ if isinstance( energy , (int, float) ): energy = np.array([energy]) if energy.any()< MIN_ANALYTIC_CROSS_SECTIONS_ENERGY: logger.error("Cannot eval StoppingPower below %f keV"%\ MIN_ANALYTIC_CROSS_SECTIONS_ENERGY) return -1 stop_pwr = 78500*self.AtomicNumber*np.log(1.166*(energy + 0.85*self.MeanIonizationPotential)/self.MeanIonizationPotential)/(energy*self.AtomicWeight)*self.Density return stop_pwr
def GetElasticMeanFreePath(self, energy, mode): """\brief Returns the the total elestic mean free path for the mixture. The mean free path is evaluated by summing the inverse of the mean free paths for all the elements. Energy to be provided in keV, mode cam be either RUTHERFORD or MOTT. """ if mode.lower() != "rutherford" and mode.lower() != "mott": logger.error("Mode can be only rutherford or mott, not %s" %\ mode) return -1 MeanFreePath = 0. for elem in self.elements_list: MeanFreePath += 1. / (elem.GetElasticMeanFreePath( energy, elem.Density, mode)) return 1. / MeanFreePath
def GetElasticMeanFreePath(self, energy, mode): """\brief Returns the the total elestic mean free path for the mixture. The mean free path is evaluated by summing the inverse of the mean free paths for all the elements. Energy to be provided in keV, mode cam be either RUTHERFORD or MOTT. """ if mode.lower() != "rutherford" and mode.lower() != "mott": logger.error("Mode can be only rutherford or mott, not %s" %\ mode) return -1 MeanFreePath = 0. for elem in self.elements_list: MeanFreePath += 1./(elem.GetElasticMeanFreePath(energy, elem.Density, mode)) return 1./MeanFreePath
def GetStoppingPower(self, energy): """ \brief Returns the stopping power at a given energy. This parametrization is from Joy and Luo (1989); see Joy's book for the reference. Energy to be provided in keV, outcome in keV/cm. """ if isinstance(energy, (int, float)): energy = np.array([energy]) if energy.any() < MIN_ANALYTIC_CROSS_SECTIONS_ENERGY: logger.error("Cannot eval StoppingPower below %f keV"%\ MIN_ANALYTIC_CROSS_SECTIONS_ENERGY) return -1 stop_pwr = 78500 * self.AtomicNumber * np.log( 1.166 * (energy + 0.85 * self.MeanIonizationPotential) / self.MeanIonizationPotential) / (energy * self.AtomicWeight) * self.Density return stop_pwr
def GetMottTotalCrossSection(self, energy): """\brief Returns the Mott integral cross section at a given energy. This parametrization is given to Browning (1992); see Joy's book for the reference Energy to be provided in keV, cross section returned in cm^2/atom. (accurate down to 50 eV). """ if isinstance( energy , (int, float) ): energy = np.array([energy]) if energy.any()< MIN_ANALYTIC_CROSS_SECTIONS_ENERGY: logger.error("Cannot eval Mott X section below %f keV"%\ MIN_ANALYTIC_CROSS_SECTIONS_ENERGY) return -1 u = np.log10(8*energy*np.power(self.AtomicNumber, -1.33)) out = (4.7E-18)*(np.power(self.AtomicNumber, 1.33) + 0.032*np.power(self.AtomicNumber, 2.0))/((energy + 0.0155*np.power(self.AtomicNumber, 1.33)*np.power(energy, 0.5))*(1 - 0.02*np.power(self.AtomicNumber, 0.5)*np.exp(-u*u))) return out
def GetScatteringElement(self, energy, mode, r0): """ \brief EXTRACTthe element scattering a travelling photoelectron. The element in the mixture is extracted according to the relative values ofthe elastic cross sections at a given energy. Energy to be provided in keV, mode can be either RUTHERFORD OR MOTT. """ total_x_sect = self.GetElasticMeanFreePath(energy, mode) # 1/x-section partial_probability = 0 for elem in self.elements_list: scat_prob = total_x_sect / elem.GetElasticMeanFreePath( energy, elem.Density, mode) if (r0 < partial_probability + scat_prob): return elem partial_probability += scat_prob # this should never happen! logger.error("Scattering element not extracted!!!") return None
def GetConvertingElement(self, energy, r0): """ \brief EXTRACT the element converting an incoming photon. The element in the mixture is extracted according to the relative values of the photoelectric cross sections at a given energy. Energy to be provided in keV. """ total_x_sect = self.GetPhotoelectricCrossSection(energy)*self.Density partial_probability = 0 for elem in self.elements_list: absorption_prob = elem.GetPhotoelectricCrossSection(energy)*elem.Density/total_x_sect if (r0 < partial_probability+absorption_prob): return elem partial_probability += absorption_prob # this should never happen! logger.error("Converting element not extracted!!!") return None
def GetConvertingElement(self, energy, r0): """ \brief EXTRACT the element converting an incoming photon. The element in the mixture is extracted according to the relative values of the photoelectric cross sections at a given energy. Energy to be provided in keV. """ total_x_sect = self.GetPhotoelectricCrossSection(energy) * self.Density partial_probability = 0 for elem in self.elements_list: absorption_prob = elem.GetPhotoelectricCrossSection( energy) * elem.Density / total_x_sect if (r0 < partial_probability + absorption_prob): return elem partial_probability += absorption_prob # this should never happen! logger.error("Converting element not extracted!!!") return None
def GetScatteringElement(self, energy, mode, r0): """ \brief EXTRACTthe element scattering a travelling photoelectron. The element in the mixture is extracted according to the relative values ofthe elastic cross sections at a given energy. Energy to be provided in keV, mode can be either RUTHERFORD OR MOTT. """ total_x_sect = self.GetElasticMeanFreePath(energy, mode) # 1/x-section partial_probability = 0 for elem in self.elements_list: scat_prob = total_x_sect/elem.GetElasticMeanFreePath(energy, elem.Density, mode) if (r0 < partial_probability+scat_prob): return elem partial_probability += scat_prob # this should never happen! logger.error("Scattering element not extracted!!!") return None
def GetRutherfordTotalCrossSection(self, energy): """\brief Returns the Rutherford integral cross section. This parametrization is given to Newbury and Myklebust (1981); see Joy's book for the reference. Energy to be provided in keV, cross section returned in cm^2/atom (accurate down to 50 eV). """ if isinstance(energy, (int, float)): energy = np.array([energy]) if energy.any() < MIN_ANALYTIC_CROSS_SECTIONS_ENERGY: logger.error("Cannot eval Rutherford X section below %f keV"%\ MIN_ANALYTIC_CROSS_SECTIONS_ENERGY) return -1 alpha = self.GetRutherfordScreeningFactor(energy) xsection = 5.21e-21 xsection *= (np.power(self.AtomicNumber, 2.0) / np.power(energy, 2.0)) xsection *= (4 * np.pi / (alpha * (1 + alpha))) xsection *= np.power(((energy + 511) / (energy + 1024)), 2.0) return xsection
def GetRutherfordTotalCrossSection(self, energy): """\brief Returns the Rutherford integral cross section. This parametrization is given to Newbury and Myklebust (1981); see Joy's book for the reference. Energy to be provided in keV, cross section returned in cm^2/atom (accurate down to 50 eV). """ if isinstance( energy , (int, float) ): energy = np.array([energy]) if energy.any()< MIN_ANALYTIC_CROSS_SECTIONS_ENERGY: logger.error("Cannot eval Rutherford X section below %f keV"%\ MIN_ANALYTIC_CROSS_SECTIONS_ENERGY) return -1 alpha = self.GetRutherfordScreeningFactor(energy); xsection = 5.21e-21 xsection *= (np.power(self.AtomicNumber, 2.0)/np.power(energy, 2.0)) xsection *= (4*np.pi/(alpha*(1 + alpha))) xsection *= np.power(((energy + 511)/(energy + 1024)), 2.0) return xsection
def __init__(self, name, fraction, comp_file_name = COMP_FILE_NAME): """ Init compound and eval properties from table """ self.name = name self.fraction = fraction comp_file = file(comp_file_name) comp_lines = comp_file.readlines() self.n_elements = 0 self.elements = [] self.atoms = [] for ll in comp_lines: l = ll.split() if l[0] == self.name: self.n_elements = int(l[1]) self.NIonsT = float(l[2]) # total ionization/cm self.StoppingP = float(l[3]) # de/dx in ev/cm for i in range(4, len(l), 2): self.elements.append(l[i]) self.atoms.append(int(l[i+1])) if self.n_elements == 0: logger.error("No compound %s found, please update file %s" %\ (self.name, comp_file_name))
def GetScatteringAngle(self, energy, mode, r0, r1): """Returns a RANDOM scattering angle at a given energy. Energy to be provided in keV; mode can be either RUTHERFORD or MOTT. The cosine of the angle is returned. r0 and r1 are 2 random number (from external generator) """ if isinstance( energy , (int, float) ): energy = np.array([energy]) if energy.any()< MIN_ANALYTIC_CROSS_SECTIONS_ENERGY: logger.error("Cannot eval Scattering Angle below %f keV"%\ MIN_ANALYTIC_CROSS_SECTIONS_ENERGY) return -1 screening_factor = self.GetRutherfordScreeningFactor(energy) if mode.lower()=='rutherford': return np.arccos(1.-(2.*screening_factor*r0)/(1.+screening_factor-r1)) elif mode.lower()=='mott': CorrectionFactor = 2.2 - ((92.0 - self.AtomicNumber)/92.0); return np.arccos(1.-(2.*screening_factor*np.power((r0), CorrectionFactor))/(1.+screening_factor-r1)) else: logger.error("Mode can be only rutherford or mott, not %s" %\ mode) return -1
def GetMottTotalCrossSection(self, energy): """\brief Returns the Mott integral cross section at a given energy. This parametrization is given to Browning (1992); see Joy's book for the reference Energy to be provided in keV, cross section returned in cm^2/atom. (accurate down to 50 eV). """ if isinstance(energy, (int, float)): energy = np.array([energy]) if energy.any() < MIN_ANALYTIC_CROSS_SECTIONS_ENERGY: logger.error("Cannot eval Mott X section below %f keV"%\ MIN_ANALYTIC_CROSS_SECTIONS_ENERGY) return -1 u = np.log10(8 * energy * np.power(self.AtomicNumber, -1.33)) out = (4.7E-18) * ( np.power(self.AtomicNumber, 1.33) + 0.032 * np.power(self.AtomicNumber, 2.0)) / ( (energy + 0.0155 * np.power(self.AtomicNumber, 1.33) * np.power(energy, 0.5)) * (1 - 0.02 * np.power(self.AtomicNumber, 0.5) * np.exp(-u * u))) return out
def __init__(self, name, fraction, comp_file_name=COMP_FILE_NAME): """ Init compound and eval properties from table """ self.name = name self.fraction = fraction comp_file = file(comp_file_name) comp_lines = comp_file.readlines() self.n_elements = 0 self.elements = [] self.atoms = [] for ll in comp_lines: l = ll.split() if l[0] == self.name: self.n_elements = int(l[1]) self.NIonsT = float(l[2]) # total ionization/cm self.StoppingP = float(l[3]) # de/dx in ev/cm for i in range(4, len(l), 2): self.elements.append(l[i]) self.atoms.append(int(l[i + 1])) if self.n_elements == 0: logger.error("No compound %s found, please update file %s" %\ (self.name, comp_file_name))
def GetElasticMeanFreePath(self, energy, density, mode): """\brief Returns the mean free path for elastic collisions at a given energy. Energy to be provided in keV, density in g/cm^3; mode can be either RUTHERFORD or MOTT. The mean free path is returned in cm. """ if isinstance( energy , (int, float) ): energy = np.array([energy]) if energy.any()< MIN_ANALYTIC_CROSS_SECTIONS_ENERGY: logger.error("Cannot eval ElasticMeanFreePath below %f keV"%\ MIN_ANALYTIC_CROSS_SECTIONS_ENERGY) return -1 MeanFreePath = self.AtomicWeight/(AVOGADRO_NUMBER*density) if mode.lower()=='rutherford': MeanFreePath /= self.GetRutherfordTotalCrossSection(energy); return MeanFreePath; elif mode.lower()=='mott': MeanFreePath /= self.GetMottTotalCrossSection(energy); return MeanFreePath; else: logger.error("Mode can be only rutherford or mott, not %s" %\ mode) return -1
def GetElasticMeanFreePath(self, energy, density, mode): """\brief Returns the mean free path for elastic collisions at a given energy. Energy to be provided in keV, density in g/cm^3; mode can be either RUTHERFORD or MOTT. The mean free path is returned in cm. """ if isinstance(energy, (int, float)): energy = np.array([energy]) if energy.any() < MIN_ANALYTIC_CROSS_SECTIONS_ENERGY: logger.error("Cannot eval ElasticMeanFreePath below %f keV"%\ MIN_ANALYTIC_CROSS_SECTIONS_ENERGY) return -1 MeanFreePath = self.AtomicWeight / (AVOGADRO_NUMBER * density) if mode.lower() == 'rutherford': MeanFreePath /= self.GetRutherfordTotalCrossSection(energy) return MeanFreePath elif mode.lower() == 'mott': MeanFreePath /= self.GetMottTotalCrossSection(energy) return MeanFreePath else: logger.error("Mode can be only rutherford or mott, not %s" %\ mode) return -1
def GetScatteringAngle(self, energy, mode, r0, r1): """Returns a RANDOM scattering angle at a given energy. Energy to be provided in keV; mode can be either RUTHERFORD or MOTT. The cosine of the angle is returned. r0 and r1 are 2 random number (from external generator) """ if isinstance(energy, (int, float)): energy = np.array([energy]) if energy.any() < MIN_ANALYTIC_CROSS_SECTIONS_ENERGY: logger.error("Cannot eval Scattering Angle below %f keV"%\ MIN_ANALYTIC_CROSS_SECTIONS_ENERGY) return -1 screening_factor = self.GetRutherfordScreeningFactor(energy) if mode.lower() == 'rutherford': return np.arccos(1. - (2. * screening_factor * r0) / (1. + screening_factor - r1)) elif mode.lower() == 'mott': CorrectionFactor = 2.2 - ((92.0 - self.AtomicNumber) / 92.0) return np.arccos(1. - (2. * screening_factor * np.power( (r0), CorrectionFactor)) / (1. + screening_factor - r1)) else: logger.error("Mode can be only rutherford or mott, not %s" %\ mode) return -1