#!/usr/bin/env python3 import numpy as np from scipy.optimize import curve_fit from ExtractXsecPy import read_DWBA from PlotWindow import FitPlotWindow #======================================================== class Fitting(): def __init__(self): self.dataName_list = [] self.fitOption = [] self.expData = [] self.dataX = [] self.data = [] # is a 2D array self.headers = [] # fit parameters for a single data set self.para = [] self.para_err = [] self.chi_squared = [] self.plot = [] def read_data(self,file_path): self.headers, self.dataX, self.data = read_DWBA(file_path) self.headers = self.headers[1:] def read_expData(self, fileName): self.dataName_list = [] self.fitOption = [] self.expData = [] current_data = [] with open(fileName, "r") as file: for line in file: line = line.strip() if not line: continue if line.startswith("$"): continue # Check for dataSet lines if line.startswith("#="): # If there's an existing data block, save it if current_data: self.expData.append(np.array(current_data, dtype=float)) current_data = [] # Extract dataSet Name dataName = line.split()[1:] self.dataName_list.append(" ".join(dataName)) # 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 self.dataName_list = np.array(self.dataName_list) self.expData = [np.array(data) for data in self.expData] # Output the result print("=========== Number of data set:", len(self.dataName_list)) for i in range(0, len(self.dataName_list)): print("-------------------------") print(" data Name:", self.dataName_list[i]) print("Fit Options:", self.fitOption[i]) print(" Data List:\n", self.expData[i]) 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 # 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 lower_bounds = [1e-6] * len(xsecID) # Setting a small positive lower bound upper_bounds = [np.inf] * len(xsecID) # No upper bound # 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)) 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 = [] for k , dN in enumerate(self.dataName_list): self.FitSingleData(k) self.plot.append( FitPlotWindow(f"Data-{k}")) 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) self.plot[-1].plot_Fit() self.plot[-1].show() def close_plots(self): for plot in self.plot: plot.close()