mirror of
https://github.com/gwm17/spspy.git
synced 2025-12-17 09:55:51 -05:00
Merge b2906cf5a8 into 97acc1659b
This commit is contained in:
commit
ee9b3d482f
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -5,4 +5,5 @@ __pycache__/
|
|||
.vs/
|
||||
*.sps
|
||||
*.spanc
|
||||
*.csv
|
||||
*.csv
|
||||
*.DS_Store
|
||||
|
|
@ -2,9 +2,7 @@
|
|||
SPSPy is a Python based package of tools for use with the Super-Enge Split-Pole Spectrograph at FSU. Much of the code here is based on Java programs originally written at Yale University by D.W. Visser, C.M. Deibel, and others. Currently the package contains SPSPlot, a tool aimed at informing users which states should appear at the focal plane of the SESPS, and SPANC, a tool for calibrating the position spectra from the focal plane.
|
||||
|
||||
## Depencencies and Requirements
|
||||
The requirements for running SPSPy are outlined in the requirements.txt file located in the repository. It is recommended to install these to a local virtual environment using `pip install -r requirements.txt`. For conda use the environments.yml file to create a conda environment for SPSPy. Simply run `conda env create -f environment.yml` from the SPSPy directory. conda will make a new virtual environment named spsenv with the dependencies outlined in environments.yml. If you already have an environment named spsenv or would like to change the name simply edit the first line of the enviornments.yml.
|
||||
|
||||
The recommended install for SPSPy dependencies is via pip.
|
||||
To ensure capability, use python 3.14. The requirements for running SPSPy are outlined in the requirements.txt file located in the repository. It is recommended to install these to a local virtual environment using `pip install -r requirements.txt`.
|
||||
|
||||
### Creating a virtual environment with pip
|
||||
To create a virtual environment with pip in the terminal for MacOS or Linux use `python3 -m venv env` to create a local virtual environment named `env` (or whatever name you'd like), or on Windows use `py -m venv env` to do the same. To activate your new environment run `source env/bin/activate` in MacOS or Linux, or `.\env\Scripts\activate`. Now you can run the above `pip` command to install all dependencies to the virtual environment. To leave the virtual environment use the command `deactivate` in your terminal.
|
||||
|
|
|
|||
|
|
@ -1,51 +0,0 @@
|
|||
name: spsenv
|
||||
channels:
|
||||
- defaults
|
||||
dependencies:
|
||||
- _libgcc_mutex=0.1=main
|
||||
- _openmp_mutex=5.1=1_gnu
|
||||
- bzip2=1.0.8=h7b6447c_0
|
||||
- ca-certificates=2022.10.11=h06a4308_0
|
||||
- certifi=2022.9.24=py310h06a4308_0
|
||||
- ld_impl_linux-64=2.38=h1181459_1
|
||||
- libffi=3.4.2=h6a678d5_6
|
||||
- libgcc-ng=11.2.0=h1234567_1
|
||||
- libgomp=11.2.0=h1234567_1
|
||||
- libstdcxx-ng=11.2.0=h1234567_1
|
||||
- libuuid=1.41.5=h5eee18b_0
|
||||
- ncurses=6.3=h5eee18b_3
|
||||
- openssl=1.1.1s=h7f8727e_0
|
||||
- pip=22.2.2=py310h06a4308_0
|
||||
- python=3.10.8=h7a1cb2a_1
|
||||
- readline=8.2=h5eee18b_0
|
||||
- setuptools=65.5.0=py310h06a4308_0
|
||||
- sqlite=3.40.0=h5082296_0
|
||||
- tk=8.6.12=h1ccaba5_0
|
||||
- tzdata=2022f=h04d1e81_0
|
||||
- wheel=0.37.1=pyhd3eb1b0_0
|
||||
- xz=5.2.8=h5eee18b_0
|
||||
- zlib=1.2.13=h5eee18b_0
|
||||
- pip:
|
||||
- charset-normalizer==2.1.1
|
||||
- contourpy==1.0.6
|
||||
- cycler==0.11.0
|
||||
- fonttools==4.38.0
|
||||
- idna==3.4
|
||||
- kiwisolver==1.4.4
|
||||
- lxml==4.9.1
|
||||
- matplotlib==3.6.2
|
||||
- numpy==1.23.5
|
||||
- packaging==21.3
|
||||
- pillow==9.3.0
|
||||
- pycatima==1.71
|
||||
- pyparsing==3.0.9
|
||||
- pyqtdarktheme==1.2.1
|
||||
- pyside6==6.4.1
|
||||
- pyside6-addons==6.4.1
|
||||
- pyside6-essentials==6.4.1
|
||||
- python-dateutil==2.8.2
|
||||
- requests==2.28.1
|
||||
- scipy==1.9.3
|
||||
- shiboken6==6.4.1
|
||||
- six==1.16.0
|
||||
- urllib3==1.26.13
|
||||
|
|
@ -1,8 +1,25 @@
|
|||
lxml==4.9.1
|
||||
matplotlib==3.6.2
|
||||
numpy==1.23.5
|
||||
pycatima==1.71
|
||||
pyqtdarktheme==1.2.1
|
||||
PySide6==6.4.1
|
||||
requests==2.28.1
|
||||
scipy==1.9.3
|
||||
certifi==2025.11.12
|
||||
charset-normalizer==3.4.4
|
||||
contourpy==1.3.3
|
||||
cycler==0.12.1
|
||||
fonttools==4.61.0
|
||||
idna==3.11
|
||||
kiwisolver==1.4.9
|
||||
lxml==6.0.2
|
||||
matplotlib==3.10.7
|
||||
numpy==2.3.5
|
||||
packaging==25.0
|
||||
pillow==12.0.0
|
||||
pycatima==1.981
|
||||
pyparsing==3.2.5
|
||||
PySide6==6.10.1
|
||||
PySide6_Addons==6.10.1
|
||||
PySide6_Essentials==6.10.1
|
||||
python-dateutil==2.9.0.post0
|
||||
QDarkStyle==3.2.3
|
||||
QtPy==2.4.3
|
||||
requests==2.32.5
|
||||
scipy==1.16.3
|
||||
shiboken6==6.10.1
|
||||
six==1.17.0
|
||||
urllib3==2.5.0
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from .SpancUI import run_spanc_ui, SpancGUI
|
|||
|
||||
import sys
|
||||
import matplotlib as mpl
|
||||
from qdarktheme import load_stylesheet
|
||||
import qdarkstyle
|
||||
|
||||
class Launcher(QMainWindow):
|
||||
def __init__(self, parent=None):
|
||||
|
|
@ -43,6 +43,7 @@ def run_launcher() -> None:
|
|||
app = QApplication.instance()
|
||||
if not app:
|
||||
app = QApplication(sys.argv)
|
||||
app.setStyleSheet(load_stylesheet())
|
||||
# app.setStyleSheet(load_stylesheet())
|
||||
app.setStyleSheet(qdarkstyle.load_stylesheet())
|
||||
window = Launcher()
|
||||
sys.exit(app.exec_())
|
||||
|
|
@ -13,7 +13,7 @@ from PySide6.QtWidgets import QDoubleSpinBox
|
|||
from PySide6.QtWidgets import QFileDialog
|
||||
from PySide6.QtGui import QAction
|
||||
|
||||
from qdarktheme import load_stylesheet
|
||||
import qdarkstyle
|
||||
from enum import Enum, auto
|
||||
import matplotlib as mpl
|
||||
import sys
|
||||
|
|
@ -299,6 +299,7 @@ def run_spsplot_ui():
|
|||
app = QApplication.instance()
|
||||
if not app:
|
||||
app = QApplication(sys.argv)
|
||||
app.setStyleSheet(load_stylesheet())
|
||||
# app.setStyleSheet(load_stylesheet())
|
||||
app.setStyleSheet(qdarkstyle.load_stylesheet())
|
||||
window = SPSPlotGUI()
|
||||
sys.exit(app.exec_())
|
||||
|
|
@ -126,4 +126,82 @@ class Spanc:
|
|||
for calibration in self.calibrations.values():
|
||||
rxn = self.reactions[calibration.rxnName]
|
||||
calibration.rho = rxn.convert_ejectile_KE_2_rho(rxn.calculate_ejectile_KE(calibration.excitation))
|
||||
calibration.rhoErr = np.abs(rxn.convert_ejectile_KE_2_rho(rxn.calculate_ejectile_KE(calibration.excitation + calibration.excitationErr)) - calibration.rho)
|
||||
calibration.rhoErr = np.abs(rxn.convert_ejectile_KE_2_rho(rxn.calculate_ejectile_KE(calibration.excitation + calibration.excitationErr)) - calibration.rho)
|
||||
|
||||
def get_excitation_curves(self,
|
||||
x_min: float = -300.0,
|
||||
x_max: float = 300.0,
|
||||
n_bins: int = 601):
|
||||
"""
|
||||
Compute excitation-energy curves for every reaction by evaluating
|
||||
Ex = reaction.calculate_excitation( rho(x) ) across a grid of
|
||||
focal-plane positions.
|
||||
|
||||
Returns:
|
||||
x_vals: array of FP bin centers
|
||||
ex_curves: { rxn_name: Ex_array_in_MeV }
|
||||
"""
|
||||
# Bin centers (e.g. 601 values from -300 to 300)
|
||||
x_vals = np.linspace(x_min, x_max, n_bins)
|
||||
|
||||
# ρ(x) uses your fitted polynomial a0 + a1 x + ... + aN x^N
|
||||
params = self.fitter.get_parameters() # [a0, a1, ..., aN]
|
||||
rho_vals = np.polyval(params[::-1], x_vals) # reverse order for numpy poly
|
||||
|
||||
ex_curves: dict[str, np.ndarray] = {}
|
||||
|
||||
for rxn_name, rxn in self.reactions.items():
|
||||
ex_vals = np.array(
|
||||
[rxn.calculate_excitation(rho) for rho in rho_vals],
|
||||
dtype=float,
|
||||
)
|
||||
ex_curves[rxn_name] = ex_vals
|
||||
|
||||
return x_vals, ex_curves
|
||||
|
||||
def export_excitation_csv(self,
|
||||
filename: str,
|
||||
x_min: float = -300.0,
|
||||
x_max: float = 300.0,
|
||||
n_bins: int = 601):
|
||||
"""
|
||||
Export excitation-energy calibration table as CSV.
|
||||
|
||||
CSV format:
|
||||
FP_x_mm, Ex_rxn1_MeV, Ex_rxn2_MeV, ...
|
||||
|
||||
FP_x_mm runs from x_min to x_max with n_bins steps (inclusive).
|
||||
"""
|
||||
import csv
|
||||
import numpy as np
|
||||
|
||||
# Generate FP positions (601 values from -300 to 300)
|
||||
x_vals = np.linspace(x_min, x_max, n_bins)
|
||||
|
||||
# Compute rho(x) using fitted polynomial
|
||||
params = self.fitter.get_parameters() # [a0, a1, ..., aN]
|
||||
rho_vals = np.polyval(params[::-1], x_vals)
|
||||
|
||||
# Compute excitation for each reaction
|
||||
rxn_names = list(self.reactions.keys())
|
||||
ex_mev = {rxn: [] for rxn in rxn_names}
|
||||
|
||||
for rho in rho_vals:
|
||||
for rxn_name, rxn in self.reactions.items():
|
||||
ex = rxn.calculate_excitation(rho)
|
||||
ex_mev[rxn_name].append(ex)
|
||||
|
||||
# Write CSV
|
||||
with open(filename, "w", newline="") as csvfile:
|
||||
writer = csv.writer(csvfile)
|
||||
|
||||
# Header row
|
||||
header = ["x_mm"] + [f"Ex_{rxn}_MeV" for rxn in rxn_names]
|
||||
writer.writerow(header)
|
||||
|
||||
# Data rows
|
||||
for i, x in enumerate(x_vals):
|
||||
row = [f"{x:.6f}"]
|
||||
for rxn in rxn_names:
|
||||
row.append(f"{ex_mev[rxn][i]:.9f}")
|
||||
writer.writerow(row)
|
||||
|
|
@ -12,7 +12,7 @@ from PySide6.QtWidgets import QPushButton, QTextEdit, QSpinBox
|
|||
from PySide6.QtWidgets import QFileDialog
|
||||
from PySide6.QtGui import QAction
|
||||
|
||||
from qdarktheme import load_stylesheet, load_palette
|
||||
import qdarkstyle
|
||||
import matplotlib as mpl
|
||||
import numpy as np
|
||||
from numpy.typing import NDArray
|
||||
|
|
@ -95,6 +95,17 @@ class SpancGUI(QMainWindow):
|
|||
fitOptionLayout.addWidget(QLabel("Polynomial Order", self.fitOptionGroup))
|
||||
fitOptionLayout.addWidget(self.fitOrderBox)
|
||||
fitOptionLayout.addWidget(self.fitButton)
|
||||
|
||||
# NEW: button to plot excitation curves for all reactions
|
||||
self.exCurveButton = QPushButton("Plot Ex vs x", self.fitOptionGroup)
|
||||
self.exCurveButton.clicked.connect(self.plot_excitation_curves)
|
||||
fitOptionLayout.addWidget(self.exCurveButton)
|
||||
|
||||
# Button to export excitation calibration CSV
|
||||
self.exportCSVButton = QPushButton("Export Ex(x) CSV", self.fitOptionGroup)
|
||||
self.exportCSVButton.clicked.connect(self.handle_export_excitation_csv)
|
||||
fitOptionLayout.addWidget(self.exportCSVButton)
|
||||
|
||||
self.fitOptionGroup.setLayout(fitOptionLayout)
|
||||
|
||||
fitLayout.addWidget(QLabel("Fit", self.fitCanvas))
|
||||
|
|
@ -374,13 +385,48 @@ class SpancGUI(QMainWindow):
|
|||
f"## Parameter Uncertanties (ua0 -> uaN): {np.array_str(self.spanc.fitter.get_parameter_errors(), precision=3)} \n \n"
|
||||
f"## Residuals (x0 -> xN): {np.array_str(residuals, precision=3)} \n \n"
|
||||
f"## Studentized Residuals (x0 -> xN): {np.array_str(studentizedResiduals, precision=3)} \n \n")
|
||||
|
||||
self.fitResultText.setMarkdown(markdownString)
|
||||
|
||||
def plot_excitation_curves(self):
|
||||
# Require a fit so rho(x) is defined
|
||||
if not self.spanc.isFit:
|
||||
print("Run the calibration fit first before plotting Ex vs x.")
|
||||
return
|
||||
|
||||
# Compute Ex at 600 bin centers from -300 to 300 for all reactions
|
||||
x_vals, ex_curves = self.spanc.get_excitation_curves(
|
||||
x_min=-300.0,
|
||||
x_max=300.0,
|
||||
n_bins=600,
|
||||
)
|
||||
|
||||
self.fitCanvas.axes.cla()
|
||||
for rxn_name, ex_vals in ex_curves.items():
|
||||
self.fitCanvas.axes.plot(x_vals, ex_vals, label=rxn_name)
|
||||
|
||||
self.fitCanvas.axes.set_xlabel(r"$x$ (mm)")
|
||||
self.fitCanvas.axes.set_ylabel(r"$E_x$ (MeV)")
|
||||
self.fitCanvas.axes.set_title("Excitation energy vs focal-plane position")
|
||||
self.fitCanvas.axes.grid(True)
|
||||
self.fitCanvas.axes.legend()
|
||||
self.fitCanvas.fig.tight_layout()
|
||||
self.fitCanvas.draw()
|
||||
|
||||
def handle_export_excitation_csv(self):
|
||||
fileName = QFileDialog.getSaveFileName(
|
||||
self, "Export Excitation CSV", "./", "CSV Files (*.csv)"
|
||||
)
|
||||
if fileName[0]:
|
||||
self.spanc.export_excitation_csv(fileName[0])
|
||||
print(f"Exported excitation calibration to {fileName[0]}")
|
||||
|
||||
def run_spanc_ui() :
|
||||
mpl.use("Qt5Agg")
|
||||
app = QApplication.instance()
|
||||
if not app:
|
||||
app = QApplication(sys.argv)
|
||||
app.setStyleSheet(load_stylesheet())
|
||||
# app.setStyleSheet(load_stylesheet())
|
||||
app.setStyleSheet(qdarkstyle.load_stylesheet())
|
||||
window = SpancGUI()
|
||||
sys.exit(app.exec_())
|
||||
Loading…
Reference in New Issue
Block a user