114 lines
5.2 KiB
Python
114 lines
5.2 KiB
Python
import pycatima as catima
|
|
import numpy as np
|
|
import pandas as pd
|
|
from scipy.interpolate import interp1d
|
|
from scipy.integrate import cumulative_trapezoid
|
|
import matplotlib.pyplot as plt
|
|
|
|
# --- 1. Constants ---
|
|
P_TORR = 400
|
|
TEMP_K = 293.15
|
|
R = 8.3144
|
|
MEV2U = 1.0 / 931.494
|
|
|
|
# Gas Density Calculations
|
|
p_pa = P_TORR * 133.322
|
|
molar_density = p_pa / (R * TEMP_K)
|
|
m_he, m_c, m_o= 4.0026, 12.0000, 15.9949
|
|
m_mix_avg = (0.96 * m_he) + (0.04 * (m_c + 2*m_o))
|
|
rho_g_cm3 = (molar_density * m_mix_avg) / 1e6
|
|
print(f"Gas density at {P_TORR} Torr: {rho_g_cm3:.6e} g/cm^3")
|
|
|
|
# --- 2. Material & Step Setup ---
|
|
material_def = [(m_he, 2, 0.96), (m_c, 6, 0.04), (m_o, 8, 0.08)] # 96% He, 4% C, 8% O by number (adjust as needed)
|
|
gas_mix = catima.Material(material_def)
|
|
gas_mix.density(rho_g_cm3)
|
|
|
|
# Thickness step settings
|
|
step_mg_cm2 = 0.001 # 1 ug/cm2 steps as per your example
|
|
step_g_cm2 = step_mg_cm2 / 1000.0
|
|
max_steps = 1000000000 # Adjust based on how far you want to track
|
|
|
|
def generate_lookup(z, mass_u, e_start_mev, label): # Function to generate lookup table for a given projectile
|
|
filename = f"{label}_lookup_{e_start_mev}MeV.dat"
|
|
projectile = catima.Projectile(mass_u, z)
|
|
|
|
current_e_total = e_start_mev
|
|
current_thickness_g_cm2 = 0.0
|
|
|
|
output = []
|
|
header = f"Energy(MeV) \tmg/cm2 \tcm\nStarting Energy: {e_start_mev} MeV"
|
|
|
|
for i in range(max_steps):
|
|
# 1. Record current state
|
|
dist_cm = current_thickness_g_cm2 / rho_g_cm3
|
|
output.append([current_e_total, current_thickness_g_cm2 * 1000.0, dist_cm])
|
|
|
|
# 2. Calculate energy loss for the NEXT step
|
|
e_u = current_e_total / mass_u
|
|
if e_u < 0.0001: # Stop at ATIMA limit
|
|
break
|
|
|
|
projectile.T(e_u)
|
|
# dedx returns MeV / (g/cm2)
|
|
loss_mev = catima.dedx(projectile, gas_mix) * step_g_cm2
|
|
|
|
# 3. Update values
|
|
current_e_total -= loss_mev
|
|
current_thickness_g_cm2 += step_g_cm2
|
|
|
|
np.savetxt(filename, output, fmt='%.6f', delimiter='\t', header=header)
|
|
print(f"Lookup table created: {filename}")
|
|
|
|
# --- 3. Run ---
|
|
# Format: generate_lookup(Z, mass_u, E_start_MeV, label)
|
|
generate_lookup(1, 1.0078, 20, "proton") # Example for proton, arguments: Z=1, mass_u=1.0078, E_start=20 MeV
|
|
generate_lookup(2, 4.0026, 20, "alpha") # Example for alpha, arguments: Z=2, mass_u=4.0026, E_start=20 MeV
|
|
generate_lookup(13,26.9815, 80, "aluminum") # Example for aluminum, arguments: Z=13, mass_u=26.9815, E_start=80 MeV
|
|
generate_lookup(9,17.0021, 70, "fluorine") # Example for fluorine, arguments: Z=9, mass_u=17.0021, E_start=70 MeV
|
|
generate_lookup(8,15.9949, 70, "oxygen") # Example for oxygen, arguments: Z=8, mass_u=15.9949, E_start=70 MeV
|
|
#data is output in format: Energy(MeV) \tmg/cm2 \tcm. To convert to E(x), you can use the cumulative energy
|
|
#loss to get distance as a function of energy, then invert that relationship to get energy as a function of distance.
|
|
#This is done in the EvXconverter.py script.
|
|
|
|
def EofXconverter(filename, density):
|
|
|
|
# Load data
|
|
data = pd.read_csv(filename, comment='#', delim_whitespace=True, header=None)
|
|
data = data.dropna()
|
|
data[0] = pd.to_numeric(data[0], errors='coerce')
|
|
data[1] = pd.to_numeric(data[1], errors='coerce')
|
|
data = data.dropna()
|
|
E = np.array(data[0], dtype=float)
|
|
S_mass = np.array(data[1], dtype=float)
|
|
S_linear = S_mass * density
|
|
sort_idx = np.argsort(E)[::-1]
|
|
E = E[sort_idx]
|
|
S_linear = S_linear[sort_idx]
|
|
invS = 1.0 / S_linear
|
|
x = cumulative_trapezoid(invS, E, initial=0)
|
|
x = -x
|
|
output = pd.DataFrame({
|
|
"Distance_cm": x,
|
|
"Energy_MeV": E
|
|
})
|
|
return output
|
|
|
|
#Put EofX converter into a txt file for use in the Armory and to generate the E(x) dataset for plotting.
|
|
def save_EofX_to_file(filename, density, output_filename):
|
|
output = EofXconverter(filename, density)
|
|
output.to_csv(output_filename, index=False, sep='\t')
|
|
print(f"Saved E(x) dataset to: {output_filename}")
|
|
|
|
EofXconverter("/home/jamesszalkie/anasen/eloss_calculations/proton_lookup_20MeV.dat", rho_g_cm3)
|
|
save_EofX_to_file("/home/jamesszalkie/anasen/eloss_calculations/proton_lookup_20MeV.dat", rho_g_cm3, "/home/jamesszalkie/anasen/eloss_calculations/E_vs_x_proton")
|
|
EofXconverter("/home/jamesszalkie/anasen/eloss_calculations/alpha_lookup_20MeV.dat", rho_g_cm3)
|
|
save_EofX_to_file("/home/jamesszalkie/anasen/eloss_calculations/alpha_lookup_20MeV.dat", rho_g_cm3, "/home/jamesszalkie/anasen/eloss_calculations/E_vs_x_alpha")
|
|
EofXconverter("/home/jamesszalkie/anasen/eloss_calculations/aluminum_lookup_80MeV.dat", rho_g_cm3)
|
|
save_EofX_to_file("/home/jamesszalkie/anasen/eloss_calculations/aluminum_lookup_80MeV.dat", rho_g_cm3, "/home/jamesszalkie/anasen/eloss_calculations/E_vs_x_aluminum")
|
|
EofXconverter("/home/jamesszalkie/anasen/eloss_calculations/fluorine_lookup_70MeV.dat", rho_g_cm3)
|
|
save_EofX_to_file("/home/jamesszalkie/anasen/eloss_calculations/fluorine_lookup_70MeV.dat", rho_g_cm3, "/home/jamesszalkie/anasen/eloss_calculations/E_vs_x_fluorine")
|
|
EofXconverter("/home/jamesszalkie/anasen/eloss_calculations/oxygen_lookup_70MeV.dat", rho_g_cm3)
|
|
save_EofX_to_file("/home/jamesszalkie/anasen/eloss_calculations/oxygen_lookup_70MeV.dat", rho_g_cm3, "/home/jamesszalkie/anasen/eloss_calculations/E_vs_x_oxygen")
|
|
|