2024-12-29 19:27:14 -05:00
|
|
|
#!/usr/bin/env python3
|
2024-11-07 14:05:03 -05:00
|
|
|
|
|
|
|
import numpy as np
|
|
|
|
from scipy.optimize import curve_fit
|
2024-11-07 16:17:28 -05:00
|
|
|
|
2024-11-07 14:05:03 -05:00
|
|
|
from ExtractXsecPy import read_DWBA
|
2024-11-08 14:23:49 -05:00
|
|
|
from PlotWindow import FitPlotWindow
|
2024-11-07 14:05:03 -05:00
|
|
|
|
2024-11-08 14:23:49 -05:00
|
|
|
#========================================================
|
2024-11-07 16:17:28 -05:00
|
|
|
class Fitting():
|
2024-11-07 14:05:03 -05:00
|
|
|
def __init__(self):
|
|
|
|
|
2024-11-08 00:20:44 -05:00
|
|
|
self.dataName_list = []
|
2024-11-07 14:05:03 -05:00
|
|
|
self.fitOption = []
|
|
|
|
self.expData = []
|
|
|
|
|
|
|
|
self.dataX = []
|
|
|
|
self.data = [] # is a 2D array
|
|
|
|
self.headers = []
|
|
|
|
|
2024-11-08 14:23:49 -05:00
|
|
|
# fit parameters for a single data set
|
|
|
|
self.para = []
|
|
|
|
self.para_err = []
|
|
|
|
self.chi_squared = []
|
|
|
|
|
|
|
|
self.plot = []
|
|
|
|
|
2024-11-07 14:05:03 -05:00
|
|
|
def read_data(self,file_path):
|
|
|
|
self.headers, self.dataX, self.data = read_DWBA(file_path)
|
2024-11-08 14:23:49 -05:00
|
|
|
self.headers = self.headers[1:]
|
2024-11-07 14:05:03 -05:00
|
|
|
|
|
|
|
def read_expData(self, fileName):
|
2024-11-08 00:20:44 -05:00
|
|
|
self.dataName_list = []
|
2024-11-07 14:05:03 -05:00
|
|
|
self.fitOption = []
|
|
|
|
self.expData = []
|
|
|
|
|
|
|
|
current_data = []
|
|
|
|
|
|
|
|
with open(fileName, "r") as file:
|
|
|
|
for line in file:
|
|
|
|
line = line.strip()
|
|
|
|
|
|
|
|
if not line:
|
|
|
|
continue
|
2024-11-07 17:18:02 -05:00
|
|
|
|
|
|
|
if line.startswith("$"):
|
|
|
|
continue
|
2024-11-07 14:05:03 -05:00
|
|
|
|
2024-11-07 22:55:42 -05:00
|
|
|
# Check for dataSet lines
|
2024-11-07 16:17:28 -05:00
|
|
|
if line.startswith("#="):
|
2024-11-07 14:05:03 -05:00
|
|
|
# If there's an existing data block, save it
|
|
|
|
if current_data:
|
|
|
|
self.expData.append(np.array(current_data, dtype=float))
|
|
|
|
current_data = []
|
|
|
|
|
2024-11-07 22:55:42 -05:00
|
|
|
# Extract dataSet Name
|
2024-11-08 14:23:49 -05:00
|
|
|
dataName = line.split()[1:]
|
|
|
|
self.dataName_list.append(" ".join(dataName))
|
2024-11-07 14:05:03 -05:00
|
|
|
|
|
|
|
# Check for fit option lines
|
|
|
|
elif line.startswith("fit"):
|
|
|
|
# Add fit option parameters
|
|
|
|
fit_params = [x.strip(',') for x in line.split()[1:]]
|
|
|
|
self.fitOption.append(fit_params)
|
|
|
|
|
|
|
|
# Parse data lines
|
|
|
|
elif not line.startswith("#"):
|
|
|
|
values = [float(x) for x in line.split()]
|
|
|
|
current_data.append(values)
|
|
|
|
|
|
|
|
# Append the last block
|
|
|
|
if current_data:
|
|
|
|
self.expData.append(np.array(current_data, dtype=float))
|
|
|
|
|
|
|
|
# Convert to numpy arrays
|
2024-11-08 00:20:44 -05:00
|
|
|
self.dataName_list = np.array(self.dataName_list)
|
2024-11-07 14:05:03 -05:00
|
|
|
self.expData = [np.array(data) for data in self.expData]
|
|
|
|
|
|
|
|
# Output the result
|
2024-11-08 00:20:44 -05:00
|
|
|
print("=========== Number of data set:", len(self.dataName_list))
|
|
|
|
for i in range(0, len(self.dataName_list)):
|
2024-11-07 14:05:03 -05:00
|
|
|
print("-------------------------")
|
2024-11-08 16:54:04 -05:00
|
|
|
print(" data Name:", self.dataName_list[i])
|
2024-11-07 14:05:03 -05:00
|
|
|
print("Fit Options:", self.fitOption[i])
|
|
|
|
print(" Data List:\n", self.expData[i])
|
|
|
|
|
2024-11-08 14:23:49 -05:00
|
|
|
def FitSingleData(self, expDataID):
|
|
|
|
print("============================================")
|
|
|
|
|
|
|
|
# Get the number of fit options and cross-sections
|
|
|
|
nFit = len(self.fitOption[expDataID])
|
|
|
|
nXsec = len(self.data)
|
|
|
|
|
|
|
|
# Extract experimental data (x, y, and errors)
|
|
|
|
x_exp = self.expData[expDataID][:, 0] # x positions
|
|
|
|
y_exp = self.expData[expDataID][:, 2] # y positions
|
|
|
|
y_err = self.expData[expDataID][:, 3] # y errors
|
|
|
|
|
|
|
|
self.para = []
|
|
|
|
self.para_err = []
|
|
|
|
self.chi_squared = []
|
|
|
|
|
|
|
|
for k in range(nFit):
|
|
|
|
# Get the cross-section IDs for the current fit option and strip extra spaces
|
|
|
|
xsecIDStr = self.fitOption[expDataID][k].strip()
|
|
|
|
xsecID = [int(part.strip()) for part in xsecIDStr.split('+')] if '+' in xsecIDStr else [int(xsecIDStr)]
|
|
|
|
|
|
|
|
# Ensure all cross-section IDs are valid
|
|
|
|
processFlag = True
|
|
|
|
for id in range(len(xsecID)):
|
|
|
|
if xsecID[id] >= nXsec:
|
|
|
|
print(f"Error: Requested Xsec-{xsecID[id]} exceeds the number of available cross-sections ({nXsec})")
|
|
|
|
processFlag = False
|
|
|
|
|
|
|
|
if processFlag == False :
|
|
|
|
continue
|
2024-11-07 14:05:03 -05:00
|
|
|
|
2024-11-08 14:23:49 -05:00
|
|
|
# Define the fitting function: a weighted sum of the selected data
|
|
|
|
def fit_func(x, *scale):
|
|
|
|
y = np.zeros_like(x)
|
|
|
|
for p, id in enumerate(xsecID):
|
|
|
|
y += scale[p] * np.interp(x, self.dataX, self.data[id])
|
|
|
|
return y
|
2024-11-07 14:05:03 -05:00
|
|
|
|
2024-11-08 14:23:49 -05:00
|
|
|
lower_bounds = [1e-6] * len(xsecID) # Setting a small positive lower bound
|
|
|
|
upper_bounds = [np.inf] * len(xsecID) # No upper bound
|
2024-11-07 14:05:03 -05:00
|
|
|
|
2024-11-08 14:23:49 -05:00
|
|
|
# Perform curve fitting using the fit_func and experimental data with y-errors as weights
|
|
|
|
popt, pcov = curve_fit(fit_func, x_exp, y_exp, sigma=y_err, absolute_sigma=True,
|
|
|
|
p0=np.ones(len(xsecID)), # Initial guess for scale parameters
|
|
|
|
bounds=(lower_bounds, upper_bounds))
|
2024-11-08 00:20:44 -05:00
|
|
|
|
2024-11-08 14:23:49 -05:00
|
|
|
self.para.append(popt)
|
|
|
|
perr = np.sqrt(np.diag(pcov))# Standard deviation of the parameters
|
|
|
|
self.para_err.append(perr)
|
|
|
|
|
|
|
|
# Get the fitted model values
|
|
|
|
y_fit = fit_func(x_exp, *popt)
|
|
|
|
residuals = y_exp - y_fit
|
|
|
|
self.chi_squared.append(np.sum((residuals / y_err) ** 2))
|
|
|
|
|
|
|
|
print(f"Fitted scale for fit {k}: {', '.join([f'{x:.3f}' for x in popt])} +/- {', '.join([f'{x:.3f}' for x in perr])} | Chi^2 : {self.chi_squared[-1]:.4f}")
|
|
|
|
# print(f"Fitted scale for fit {k}: {popt} +/- {perr} | Chi^2 : {chi_squared[-1]:.4f}")
|
|
|
|
|
|
|
|
return self.para, self.para_err, self.chi_squared
|
|
|
|
|
|
|
|
|
|
|
|
def plot_fits(self):
|
|
|
|
|
|
|
|
self.plot = []
|
2024-11-08 16:54:04 -05:00
|
|
|
|
2024-11-08 14:23:49 -05:00
|
|
|
for k , dN in enumerate(self.dataName_list):
|
|
|
|
self.FitSingleData(k)
|
|
|
|
self.plot.append( FitPlotWindow(f"Data-{k}"))
|
2024-11-08 16:54:04 -05:00
|
|
|
self.plot[-1].set_data(k, self.expData, self.fitOption, dN,
|
|
|
|
self.dataX, self.data, self.headers,
|
|
|
|
self.para, self.para_err, self.chi_squared)
|
2024-11-08 14:23:49 -05:00
|
|
|
self.plot[-1].plot_Fit()
|
|
|
|
self.plot[-1].show()
|
2024-11-08 16:54:04 -05:00
|
|
|
|
|
|
|
def close_plots(self):
|
|
|
|
for plot in self.plot:
|
|
|
|
plot.close()
|