1
0
Fork 0
mirror of https://github.com/gwm17/spspy.git synced 2024-09-21 15:07:25 -04:00
spspy/spanc/EnergyLoss.py
2021-12-06 15:37:47 -05:00

214 lines
7.0 KiB
Python
Executable File

#!/usr/bin/env python3
import numpy as np
import EnergyLossData as edata
from NucData import Masses
class EnergyLoss:
MAX_FRACTIONAL_STEP=0.001
MAX_H_E_PER_U=100000.0
AVOGADRO=0.60221367
MEV2U=1.0/931.4940954
def __init__(self):
self.ZP = 0
self.AP = 0
self.MP = 0
self.ZT = np.zeros(0)
self.AT = np.zeros(0)
self.Stoich = np.zeros(0)
self.illegalFlag = True
def SetTargetData(self, zt, at, stoich):
self.ZT = zt
self.AT = at
total = np.sum(stoich)
self.Stoich = stoich/total
for z in self.ZT:
if z >= edata.MaxZ:
self.illegalFlag = True
return
self.illegalFlag = False
def GetEnergyLoss(self, zp, ap, e_initial, thickness):
if self.illegalFlag:
print("Unable to get energy loss with unset target data... returning 0")
return 0.0
if self.ZP != zp:
self.ZP = zp
self.AP = ap
self.MP = Masses.GetMass(self.ZP, self.AP)*self.MEV2U
e_final = e_initial
x_traversed = 0
x_step = 0.25*thickness
e_step = self.GetTotalStoppingPower(e_final)*x_step/1000.0
if thickness == 0.0:
return 0.0
go = True
while go:
if e_step/e_final > self.MAX_FRACTIONAL_STEP:
x_step *= 0.5
e_step = self.GetTotalStoppingPower(e_final)*x_step/1000.0
elif x_step+x_traversed >= thickness:
go = False
x_step = thickness - x_traversed #get valid portion of last chunk
e_final -= self.GetTotalStoppingPower(e_final)*x_step/1000.0
if e_final <= 0.0:
return e_initial
else:
x_traversed += x_step
e_step = self.GetTotalStoppingPower(e_final)*x_step/1000.0
e_final -= e_step
if e_final <= 0.0:
return e_initial
return e_initial - e_final
def GetReverseEnergyLoss(self, zp, ap, e_final, thickness):
if self.illegalFlag:
print("Unable to get energy loss with unset target data... returning 0")
return 0.0
if self.ZP != zp:
self.ZP = zp
self.AP = ap
self.MP = Masses.GetMass(self.ZP, self.AP)*self.MEV2U
e_initial = e_final
x_traversed = 0
x_step = 0.25*thickness
e_step = self.GetTotalStoppingPower(e_initial)*x_step/1000.0
if thickness == 0.0:
return 0.0
go = True
while go:
if e_step/e_final > self.MAX_FRACTIONAL_STEP:
x_step *= 0.5
e_step = self.GetTotalStoppingPower(e_initial)*x_step/1000.0
elif x_step+x_traversed >= thickness:
go = False
x_step = thickness - x_traversed #get valid portion of last chunk
e_initial += self.GetTotalStoppingPower(e_initial)*x_step/1000.0
else:
x_traversed += x_step
e_step = self.GetTotalStoppingPower(e_initial)*x_step/1000.0
e_final += e_step
if e_final <= 0.0:
return e_initial
return abs(e_initial - e_final)
def GetTotalStoppingPower(self, energy):
return self.GetElectronicStoppingPower(energy)+self.GetNuclearStoppingPower(energy)
def GetElectronicStoppingPower(self, energy):
e_per_u = energy*1000.0/self.MP
values = np.zeros(len(self.ZT))
if e_per_u > self.MAX_H_E_PER_U:
print("Bombarding energy exceeds maximum allowed value for energy loss! Returning 0")
return 0.0
elif e_per_u > 1000.0:
for i in range(len(self.ZT)):
values[i] = self.Hydrogen_dEdx_High(e_per_u, energy, self.ZT[i])
elif e_per_u > 10.0:
for i in range(len(self.ZT)):
values[i] = self.Hydrogen_dEdx_Med(e_per_u, self.ZT[i])
elif e_per_u > 0.0:
for i in range(len(self.ZT)):
values[i] = self.Hydrogen_dEdx_Low(e_per_u, self.ZT[i])
else:
print("Negative energy encountered at EnergyLoss::GetElectronicStoppingPower! Returning 0")
return 0.0
if self.ZP > 1:
for i in range(len(values)):
values[i] *= self.CalculateEffectiveChargeRatio(e_per_u, self.ZT[i])
stopping_total = np.sum(values*self.Stoich)
conv_factor = 0.0
for i in range(len(self.ZT)):
conv_factor += self.Stoich[i]*edata.NaturalMass[self.ZT[i]]
stopping_total *= self.AVOGADRO/conv_factor
return stopping_total
def GetNuclearStoppingPower(self, energy):
e = energy*1000.0
stopping_total = 0.0
for i in range(len(self.ZT)):
zt = self.ZT[i]
mt = edata.NaturalMass[self.ZT[i]]
x = (self.MP + mt) * np.sqrt(self.ZP**(2.0/3.0) + zt**(2.0/3.0))
epsilon = 32.53*mt*e/(self.ZP*zt*x)
sn = 8.462*(0.5*np.log(1.0+epsilon)/(epsilon+0.10718*(epsilon**0.37544)))*self.ZP*zt*self.MP/x
conversion_factor = self.AVOGADRO/mt
stopping_total += sn*conversion_factor*self.Stoich[i]
return stopping_total
def Hydrogen_dEdx_Low(self, e_per_u, zt):
return np.sqrt(e_per_u)*edata.HydrogenCoeff[zt][0]
def Hydrogen_dEdx_Med(self, e_per_u, zt):
x = edata.HydrogenCoeff[zt][1]*e_per_u**0.45
y = edata.HydrogenCoeff[zt][2]/e_per_u * np.log(1.0 + edata.HydrogenCoeff[zt][3]/e_per_u + edata.HydrogenCoeff[zt][4]*e_per_u)
return x*y/(x+y)
def Hydrogen_dEdx_High(self, e_per_u, energy, zt):
beta_sq = energy * (energy + 2.0*self.MP/self.MEV2U)/((energy + self.MP/self.MEV2U)**2.0)
alpha = edata.HydrogenCoeff[zt][5]/beta_sq
epsilon = edata.HydrogenCoeff[zt][6]*beta_sq/(1.0 - beta_sq) - beta_sq - edata.HydrogenCoeff[zt][7]
for i in range(1,5):
epsilon += edata.HydrogenCoeff[zt][7+i]*(np.log(e_per_u))**float(i)
return alpha * np.log(epsilon)
def CalculateEffectiveChargeRatio(self, e_per_u, zt):
z_ratio=0
if self.ZP == 2:
ln_epu = np.log(e_per_u)
gamma = 1.0+(0.007+0.00005*zt)*np.exp(-1.0*(7.6-ln_epu)**2.0)
alpha = 0.7446 + 0.1429*ln_epu + 0.01562*ln_epu**2.0 - 0.00267*ln_epu**3.0 + 1.338e-6*ln_epu**8.0
z_ratio = gamma*(1.0-np.exp(-alpha))*2.0
elif self.ZP == 3:
ln_epu = np.log(e_per_u)
gamma = 1.0+(0.007+0.00005*zt)*np.exp(-1.0*(7.6-ln_epu)**2.0)
alpha = 0.7138+0.002797*e_per_u+1.348e-6*e_per_u**2.0
z_ratio = gamma*(1-np.exp(-alpha))*3.0
else:
B = 0.886*(e_per_u/25.0)**0.5/(self.ZP**(2.0/3.0))
A = B + 0.0378*np.sin(np.pi/2.0*B)
z_ratio = (1.0 - np.exp(-A)*(1.034-0.1777*np.exp(-0.08114*self.ZP)))*self.ZP
return z_ratio*z_ratio
def main():
targetA = np.array([12])
targetZ = np.array([6])
targetS = np.array([1])
beamKE = 16.0
thickness = 20.0
eloss = EnergyLoss()
eloss.SetTargetData(targetZ, targetA, targetS)
print("Testing various cases for energy loss. Using 12C target with 20 ug/cm^2 thickness. Compare to values given by LISE++ or SRIM")
result = eloss.GetEnergyLoss(1, 1, beamKE, 20.0)
print("Case 1: ZP = 1, AP=1, Beam energy = 16 MeV -> Resulting energy loss = ", result, " MeV")
beamKE = 1.0
result = eloss.GetEnergyLoss(1, 1, beamKE, 20.0)
print("Case 2: ZP = 1, AP=1, Beam energy = 1.0 MeV -> Resulting energy loss = ", result, " MeV")
beamKE = 0.1
result = eloss.GetEnergyLoss(1, 1, beamKE, 20.0)
print("Case 3: ZP = 1, AP=1, Beam energy = 0.1 MeV -> Resulting energy loss = ", result, " MeV")
beamKE = 0.01
result = eloss.GetEnergyLoss(1, 1, beamKE, 20.0)
print("Case 4: ZP = 1, AP=1, Beam energy = 0.01 MeV -> Resulting energy loss = ", result, " MeV")
beamKE = 24.0
result = eloss.GetEnergyLoss(2, 4, beamKE, 20.0)
print("Case 5: ZP = 2, AP=4, Beam energy = 24.0 MeV -> Resulting energy loss = ", result, " MeV")
beamKE = 24.0
result = eloss.GetEnergyLoss(3, 6, beamKE, 20.0)
print("Case 6: ZP = 3, AP=6, Beam energy = 24 MeV -> Resulting energy loss = ", result, " MeV")
print("Finished.")
if __name__ == '__main__':
main()