Streamlined, save new data as root file

This commit is contained in:
James Szalkie 2026-05-22 14:04:52 -04:00
parent 804c89b320
commit 17e36ad451
3 changed files with 683 additions and 585 deletions

File diff suppressed because it is too large Load Diff

View File

@ -11,9 +11,6 @@ from scipy.interpolate import interp1d
import argparse import argparse
import uproot import uproot
import pycatima as catima import pycatima as catima
import numpy as np
import pandas as pd
from scipy.interpolate import interp1d
from scipy.integrate import cumulative_trapezoid from scipy.integrate import cumulative_trapezoid
import matplotlib import matplotlib
matplotlib.use("TkAgg") matplotlib.use("TkAgg")
@ -46,6 +43,9 @@ alpha_data = [2, 4.0026, 40, "alpha"]
proton_data = [1, 1.0078, 20, "proton"] proton_data = [1, 1.0078, 20, "proton"]
deuteron_data = [1, 2.014102, 30, "deuteron"] deuteron_data = [1, 2.014102, 30, "deuteron"]
dA = 3.2 #cm
dC = 4.2 #cm
dsx3 = 8.8 #cm
particles = { particles = {
"alpha": alpha_data, "alpha": alpha_data,
@ -53,6 +53,8 @@ particles = {
"deuteron": deuteron_data "deuteron": deuteron_data
} }
interp_cache = {}
def make_E_vs_x(z, mass_u, emax_mev, label, npoints, P_TORR, TEMP_K): def make_E_vs_x(z, mass_u, emax_mev, label, npoints, P_TORR, TEMP_K):
# GAS SETUP # GAS SETUP
R = 8.3144 R = 8.3144
@ -149,113 +151,79 @@ def load_table(filename):
data = pd.read_csv( data = pd.read_csv(
filename, filename,
sep='\s+', sep=r'\s+',
comment="#", comment="#",
header=None, header=None,
skiprows=1 skiprows=1
) )
x = data.iloc[:, 0].values x = data.iloc[:, 0].to_numpy()
E = data.iloc[:, 1].values E = data.iloc[:, 1].to_numpy()
return x, E return x, E
def energy_loss(particle, Ei, dl): def get_interpolators(particle):
if particle in interp_cache:
return interp_cache[particle]
filename = f"E_vs_x_{particle}.dat" filename = f"E_vs_x_{particle}.dat"
data = pd.read_csv(
filename, x, E = load_table(filename)
sep='\s+',
comment="#",
header=None,
skiprows=1
)
x = data.iloc[:, 0].values
E = data.iloc[:, 1].values
E_of_x = interp1d( E_of_x = interp1d(
x, x,
E, E,
bounds_error=False, bounds_error=False,
fill_value=0.0 fill_value=0.0
) )
x_of_E = interp1d( x_of_E = interp1d(
E[::-1], # reverse so energy increases E[::-1],
x[::-1], x[::-1],
bounds_error=False, bounds_error=False,
fill_value="extrapolate" fill_value="extrapolate"
) )
xi = float(x_of_E(Ei))
xf = xi + float(dl) interp_cache[particle] = (E_of_x, x_of_E)
Ef = float(E_of_x(xf)) return E_of_x, x_of_E
return max(Ef, 0.0) def energy_loss(particle, Ei, dl):
E_of_x, x_of_E = get_interpolators(particle)
xi = x_of_E(Ei)
xf = xi + dl
Ef = E_of_x(xf)
return np.maximum(Ef, 0.0)
#def energy_loss_angular(particle, Ei, theta): #def energy_loss_angular(particle, Ei, theta):
def energy_reconstruction(particle, Ef, dl): def energy_reconstruction(particle, Ef, dl):
filename = f"E_vs_x_{particle}.dat"
data = pd.read_csv( E_of_x, x_of_E = get_interpolators(particle)
filename,
sep='\s+', xf = x_of_E(Ef)
comment="#",
header=None,
skiprows=1
)
x = data.iloc[:, 0].values
E = data.iloc[:, 1].values
E_of_x = interp1d(
x,
E,
bounds_error=False,
fill_value=0.0
)
x_of_E = interp1d(
E[::-1], # reverse so energy increases
x[::-1],
bounds_error=False,
fill_value="extrapolate"
)
xf = float(x_of_E(Ef))
xi = xf - dl xi = xf - dl
Ei = float(E_of_x(xi)) Ei = E_of_x(xi)
return max(Ei, 0.0) return np.maximum(Ei, 0.0)
def energy_distance(particle, Ei, Ef): def energy_distance(particle, Ei, Ef):
filename = f"E_vs_x_{particle}.dat"
data = pd.read_csv(
filename,
sep='\s+',
comment="#",
header=None,
skiprows=1
)
x = data.iloc[:, 0].values
E = data.iloc[:, 1].values
x_of_E = interp1d(
E[::-1], # reverse so energy increases
x[::-1],
bounds_error=False,
fill_value="extrapolate"
)
xi = float(x_of_E(Ei))
xf = float(x_of_E(Ef))
return abs(xf - xi) _, x_of_E = get_interpolators(particle)
xi = x_of_E(Ei)
xf = x_of_E(Ef)
return np.abs(xf - xi)
def resolve_particle(name): def resolve_particle(name):
@ -307,11 +275,15 @@ class MyInteractiveApp(cmd.Cmd):
def print_params(self): def print_params(self):
"""Helper method to display current state""" """Helper method to display current state"""
print(f"Current Parameters: T={self.T}, P={self.P}") print(f"Current Parameters: T={self.T} K, P={self.P} Torr")
def initialize_file(self): def initialize_file(self):
"""Load in default root file for anasen""" """Load in default root file for anasen"""
self.file = uproot.open(f"../Armory/{self.rootFile}") try:
self.file = uproot.open(f"../Armory/{self.rootFile}")
except:
self.file = None
print("\nATTENTION: Root file not found, continue without uproot functions or uproot file manually")
#intro = "Interactive Shell Started. Type 'help' to see commands." #intro = "Interactive Shell Started. Type 'help' to see commands."
@ -510,6 +482,7 @@ class MyInteractiveApp(cmd.Cmd):
def do_uproot_file(self, arg): def do_uproot_file(self, arg):
"""Open a specific root file for inspection""" """Open a specific root file for inspection"""
global rootfile
args = shlex.split(arg) args = shlex.split(arg)
if len(args) > 0: if len(args) > 0:
filename = args[0] filename = args[0]
@ -517,9 +490,12 @@ class MyInteractiveApp(cmd.Cmd):
filename = self.rootFile filename = self.rootFile
try: try:
print(f"Opening {filename}") print(f"Opening {filename}")
with uproot.open(f"../Armory/{filename}") as tempfile: with uproot.open(f"../Armory/{filename}") as rootfile:
self.file = tempfile self.file = rootfile
except FileNotFoundError: except FileNotFoundError:
with uproot.open(f"{filename}") as rootfile:
self.file = rootfile
except:
print("Error: file not found") print("Error: file not found")
def do_print_file(self, arg): def do_print_file(self, arg):
@ -535,11 +511,12 @@ class MyInteractiveApp(cmd.Cmd):
"""Set a specific tree from the file (default to 'tree')""" """Set a specific tree from the file (default to 'tree')"""
file = self.file file = self.file
if len(arg) > 0: if len(arg) > 0:
treeName = f"tree{arg}" treeName = f"{arg}"
else: else:
treeName = "tree" treeName = "tree"
try: try:
self.tree = file[treeName] self.tree = file[treeName]
global tree
tree = self.tree tree = self.tree
print(f"Tree: {tree}") print(f"Tree: {tree}")
print("Branches: ", tree.keys()) print("Branches: ", tree.keys())
@ -583,14 +560,10 @@ class MyInteractiveApp(cmd.Cmd):
while True: while True:
try: try:
entry = input(">>> ").strip() entry = input(">>> ").strip()
if entry.lower() in ["exit", "quit"]: if entry.lower() in ["exit", "quit"]:
break break
if not entry: if not entry:
continue continue
# Try eval first so expressions print naturally
try: try:
result = eval(entry, globals(), local_vars) result = eval(entry, globals(), local_vars)
@ -612,6 +585,131 @@ class MyInteractiveApp(cmd.Cmd):
"""Start up an in-program command line to use root tools with python, """Start up an in-program command line to use root tools with python,
look up 'uproot' for more details""" look up 'uproot' for more details"""
self.run_command_line() self.run_command_line()
def do_energy_analysis(self, arg):
args = shlex.split(arg)
try:
particle = args[0]
except IndexError:
print("Please indicate reactant for analysis")
return
try:
max_events = int(args[1])
except IndexError:
max_events = None
if self.tree is None:
self.do_set_tree("")
print(f"Using TTree: {self.tree}")
branches = [
"Tb",
"thetab"
]
if max_events:
n_events = max_events
else:
n_events = self.tree.num_entries
print(f"Loading {n_events} events...")
data = self.tree.arrays(
branches,
library="np",
entry_stop=max_events
)
Ei = data["Tb"]
theta = np.radians(data["thetab"])
# Remove theta = 0 events
mask = np.sin(theta) != 0
Ei = Ei[mask]
theta = theta[mask]
sin_theta = np.sin(theta)
radii = np.array([3.2, 4.2, 6.6])
dA = radii[0] / sin_theta
dC = radii[1] / sin_theta
dsx3 = radii[2] / sin_theta
print("Calculating energy losses...")
EA = energy_loss(particle, Ei, dA)
EC = energy_loss(particle, Ei, dC)
Esx3 = energy_loss(particle, Ei, dsx3)
Eprop = EA - EC
print("Analysis complete")
print(f"Processed events: {len(Ei)}")
print(f"Anode average energy: {np.mean(EA):.3f} MeV")
print(f"Cathode average energy: {np.mean(EC):.3f} MeV")
print(f"sx3 average energy: {np.mean(Esx3):.3f} MeV")
print(f"Proportion counter average energy difference: {np.mean(Eprop):.3f} MeV")
print(f"Maximum proportion counter energy difference: {np.max(Eprop):.3f} MeV")
print(f"Minimum proportion counter energy difference: {np.min(Eprop):.3f} MeV")
output_filename = "energy_analysis.root"
print(f"Writing new tree to {output_filename}")
# Load ALL original branches
all_data = self.tree.arrays(library="np", entry_stop=max_events)
# Create full-length arrays initialized to NaN
n_total = len(data["Tb"])
EA_full = np.full(n_total, np.nan)
EC_full = np.full(n_total, np.nan)
Esx3_full = np.full(n_total, np.nan)
Eprop_full = np.full(n_total, np.nan)
# Put values back into valid entries
EA_full[mask] = EA
EC_full[mask] = EC
Esx3_full[mask] = Esx3
Eprop_full[mask] = Eprop
# Add new branches
all_data["EA"] = EA_full
all_data["EC"] = EC_full
all_data["Esx3"] = Esx3_full
all_data["Eprop"] = Eprop_full
# Write new ROOT file
with uproot.recreate(output_filename) as fout:
branch_types = {
name: array.dtype
for name, array in all_data.items()
}
fout.mktree("tree", branch_types)
fout["tree"].extend(all_data)
print("Finished writing augmented ROOT file")
if __name__ == "__main__": if __name__ == "__main__":
MyInteractiveApp().cmdloop() MyInteractiveApp().cmdloop()

BIN
ELoss/PCEnergyAnalysis.zip Normal file

Binary file not shown.