diff --git a/nmreval/data/nmr.py b/nmreval/data/nmr.py index f9c18d8..23d501a 100644 --- a/nmreval/data/nmr.py +++ b/nmreval/data/nmr.py @@ -8,7 +8,7 @@ from .signals import Signal class FID(Signal): """ - Extensions if :class:`Signal` for NMR timesignals + Extensions of :class:`Signal` for NMR timesignals """ def __init__(self, x, y, **kwargs): super().__init__(x, y, **kwargs) diff --git a/nmreval/data/points.py b/nmreval/data/points.py index 8ea4630..a9c4fc3 100644 --- a/nmreval/data/points.py +++ b/nmreval/data/points.py @@ -117,8 +117,6 @@ class Points: Args: value: array of the same length as full or masked y - Returns: - """ value = np.asarray(value, dtype=self._y.dtype) if value.shape == self._y.shape: @@ -169,8 +167,6 @@ class Points: Args: opts: - Returns: - """ self.meta.update(opts) @@ -278,7 +274,7 @@ class Points: avg_range: Tuple[int, int] = (0, 0), avg_mode: str = 'mean', pts: Optional[list] = None) -> List[tuple]: """ - Return (x, y) values at specific positions. + Return (x, y) values at specified positions. Args: idx (list, optional) : List of indexes to evaluate. diff --git a/nmreval/distributions/intermolecular.py b/nmreval/distributions/intermolecular.py index e736320..8c279ef 100644 --- a/nmreval/distributions/intermolecular.py +++ b/nmreval/distributions/intermolecular.py @@ -26,7 +26,8 @@ class FFHS(Distribution): @staticmethod def specdens(omega, tau0, *args): def integrand(u, o, tau0): - return FFHS.distribution(u, tau0) * u / (1+o**2 * u**2) + return u**4 * tau0 / (81 + 9*u**2 - 2*u**4 + u**6) / (u**4 + (o*tau0)**2) + # return FFHS.distribution(u, tau0) * u / (1+o**2 * u**2) ret_val = np.array([quad(integrand, 0, np.infty, args=(o, tau0), epsabs=1e-12, epsrel=1e-12)[0] for o in omega]) diff --git a/nmreval/distributions/loggaussian.py b/nmreval/distributions/loggaussian.py index c6bc1c6..62cf0b3 100644 --- a/nmreval/distributions/loggaussian.py +++ b/nmreval/distributions/loggaussian.py @@ -35,7 +35,7 @@ class LogGaussian(Distribution): integration_ranges = [(omega_i, tau_j, sigma) for (omega_i, tau_j) in product(_t, _tau)] with np.errstate(divide='ignore'): - res = np.array(pool.map(_integrate_process_3, integration_ranges)) + res = np.array(pool.map(_integrate_process_time, integration_ranges)) ret_val = res.reshape((_t.shape[0], _tau.shape[0])) return ret_val.squeeze() @@ -49,8 +49,8 @@ class LogGaussian(Distribution): integration_ranges = [(omega_i, tau_j, sigma) for (omega_i, tau_j) in product(_omega, _tau)] with np.errstate(divide='ignore'): - res_real = np.array(pool.map(_integrate_process_1, integration_ranges)) - res_imag = np.array(pool.map(_integrate_process_2, integration_ranges)) + res_real = np.array(pool.map(_integrate_process_imag, integration_ranges)) + res_imag = np.array(pool.map(_integrate_process_real, integration_ranges)) ret_val = (res_real+1j*res_imag).reshape((_omega.shape[0], _tau.shape[0])) return ret_val.squeeze() @@ -64,7 +64,7 @@ class LogGaussian(Distribution): integration_ranges = [(omega_i, tau_j, sigma) for (omega_i, tau_j) in product(_omega, _tau)] with np.errstate(divide='ignore'): - res = np.array(pool.map(_integrate_process_1, integration_ranges)) + res = np.array(pool.map(_integrate_process_imag, integration_ranges)) ret_val = res.reshape((_omega.shape[0], _tau.shape[0])) ret_val /= _omega[:, None] @@ -75,15 +75,15 @@ class LogGaussian(Distribution): return args[0]*np.exp(args[1]**2 / 2) -def _integrate_process_1(args): +def _integrate_process_imag(args): omega_i, tau_j, sigma = args - area, _ = quad(_integrand_freq_imag_high, 0, 50, args=(omega_i, tau_j, sigma)) + area = quad(_integrand_freq_imag_high, 0, 50, args=(omega_i, tau_j, sigma))[0] area += quad(_integrand_freq_imag_low, -50, 0, args=(omega_i, tau_j, sigma))[0] return area -def _integrate_process_2(args): +def _integrate_process_real(args): omega_i, tau_j, sigma = args area = quad(_integrand_freq_real_high, 0, 50, args=(omega_i, tau_j, sigma))[0] area += quad(_integrand_freq_real_low, -50, 0, args=(omega_i, tau_j, sigma))[0] @@ -91,7 +91,7 @@ def _integrate_process_2(args): return area -def _integrate_process_3(args): +def _integrate_process_time(args): omega_i, tau_j, sigma = args return quad(_integrand_time, -50, 50, args=(omega_i, tau_j, sigma))[0] diff --git a/nmreval/dsc/calibration.py b/nmreval/dsc/calibration.py index 965605f..1b293f7 100644 --- a/nmreval/dsc/calibration.py +++ b/nmreval/dsc/calibration.py @@ -26,7 +26,7 @@ def evaluate(sample, empty, reference, ref_points=Cyclohexane, show_cooling=Fals if show_cooling: fig, ax = plt.subplots() print('\n') - for k, v in sample.heating.items(): + for k, v in sample.cooling.items(): print('Plot run {} with cooling rate {} K/min'.format(k, v)) c = sample.flow_data(v, mode='c') ax.plot(c[0], c[1], label=str(v)+' K/min') diff --git a/nmreval/gui_qt/_py/editsignalwidget.py b/nmreval/gui_qt/_py/editsignalwidget.py index 1372ab5..1dacbd5 100644 --- a/nmreval/gui_qt/_py/editsignalwidget.py +++ b/nmreval/gui_qt/_py/editsignalwidget.py @@ -2,9 +2,10 @@ # Form implementation generated from reading ui file 'resources/_ui/editsignalwidget.ui' # -# Created by: PyQt5 UI code generator 5.12.3 +# Created by: PyQt5 UI code generator 5.15.4 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. from PyQt5 import QtCore, QtGui, QtWidgets @@ -73,6 +74,7 @@ class Ui_Form(object): self.ph1slider = QtWidgets.QDoubleSpinBox(self.groupBox_2) self.ph1slider.setMinimum(-360.0) self.ph1slider.setMaximum(360.0) + self.ph1slider.setWrapping(True) self.ph1slider.setObjectName("ph1slider") self.gridLayout_4.addWidget(self.ph1slider, 5, 1, 1, 1) self.label_6 = QtWidgets.QLabel(self.groupBox_2) @@ -101,6 +103,7 @@ class Ui_Form(object): self.ph0slider.setSizePolicy(sizePolicy) self.ph0slider.setMinimum(-180.0) self.ph0slider.setMaximum(180.0) + self.ph0slider.setWrapping(True) self.ph0slider.setObjectName("ph0slider") self.gridLayout_4.addWidget(self.ph0slider, 2, 1, 1, 1) self.label_8 = QtWidgets.QLabel(self.groupBox_2) diff --git a/nmreval/gui_qt/_py/hdftree.py b/nmreval/gui_qt/_py/hdftree.py index b0e89f1..adaafcf 100644 --- a/nmreval/gui_qt/_py/hdftree.py +++ b/nmreval/gui_qt/_py/hdftree.py @@ -2,9 +2,10 @@ # Form implementation generated from reading ui file 'resources/_ui/hdftree.ui' # -# Created by: PyQt5 UI code generator 5.12.3 +# Created by: PyQt5 UI code generator 5.15.4 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. from PyQt5 import QtCore, QtGui, QtWidgets @@ -14,25 +15,37 @@ class Ui_Hdf_Dialog(object): def setupUi(self, Hdf_Dialog): Hdf_Dialog.setObjectName("Hdf_Dialog") Hdf_Dialog.resize(460, 772) - self.verticalLayout = QtWidgets.QVBoxLayout(Hdf_Dialog) - self.verticalLayout.setContentsMargins(4, 4, 4, 4) - self.verticalLayout.setSpacing(3) + self.verticalLayout_2 = QtWidgets.QVBoxLayout(Hdf_Dialog) + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.label_3 = QtWidgets.QLabel(Hdf_Dialog) + self.label_3.setTextFormat(QtCore.Qt.RichText) + self.label_3.setObjectName("label_3") + self.verticalLayout_2.addWidget(self.label_3) + self.splitter = QtWidgets.QSplitter(Hdf_Dialog) + self.splitter.setOrientation(QtCore.Qt.Vertical) + self.splitter.setChildrenCollapsible(False) + self.splitter.setObjectName("splitter") + self.horizontalLayoutWidget = QtWidgets.QWidget(self.splitter) + self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget") + self.verticalLayout = QtWidgets.QVBoxLayout(self.horizontalLayoutWidget) + self.verticalLayout.setContentsMargins(0, 0, 0, 0) + self.verticalLayout.setSpacing(0) self.verticalLayout.setObjectName("verticalLayout") self.verticalLayout_3 = QtWidgets.QVBoxLayout() self.verticalLayout_3.setObjectName("verticalLayout_3") self.verticalLayout.addLayout(self.verticalLayout_3) - self.widget = QtWidgets.QWidget(Hdf_Dialog) + self.widget = QtWidgets.QWidget(self.horizontalLayoutWidget) self.widget.setObjectName("widget") self.gridLayout = QtWidgets.QGridLayout(self.widget) self.gridLayout.setContentsMargins(0, 0, 0, 0) self.gridLayout.setSpacing(2) self.gridLayout.setObjectName("gridLayout") - self.comboBox_2 = QtWidgets.QComboBox(self.widget) - self.comboBox_2.setObjectName("comboBox_2") - self.gridLayout.addWidget(self.comboBox_2, 1, 1, 1, 1) self.label = QtWidgets.QLabel(self.widget) self.label.setObjectName("label") self.gridLayout.addWidget(self.label, 0, 0, 1, 1) + self.comboBox_2 = QtWidgets.QComboBox(self.widget) + self.comboBox_2.setObjectName("comboBox_2") + self.gridLayout.addWidget(self.comboBox_2, 1, 1, 1, 1) self.label_2 = QtWidgets.QLabel(self.widget) self.label_2.setObjectName("label_2") self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1) @@ -40,13 +53,18 @@ class Ui_Hdf_Dialog(object): self.comboBox.setObjectName("comboBox") self.gridLayout.addWidget(self.comboBox, 0, 1, 1, 1) self.verticalLayout.addWidget(self.widget) - self.widget_2 = ExpandableWidget(Hdf_Dialog) + self.widget_2 = ExpandableWidget(self.splitter) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.MinimumExpanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.widget_2.sizePolicy().hasHeightForWidth()) + self.widget_2.setSizePolicy(sizePolicy) self.widget_2.setObjectName("widget_2") - self.verticalLayout.addWidget(self.widget_2) + self.verticalLayout_2.addWidget(self.splitter) self.buttonBox = QtWidgets.QDialogButtonBox(Hdf_Dialog) self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) self.buttonBox.setObjectName("buttonBox") - self.verticalLayout.addWidget(self.buttonBox) + self.verticalLayout_2.addWidget(self.buttonBox) self.retranslateUi(Hdf_Dialog) self.buttonBox.rejected.connect(Hdf_Dialog.close) @@ -56,6 +74,7 @@ class Ui_Hdf_Dialog(object): def retranslateUi(self, Hdf_Dialog): _translate = QtCore.QCoreApplication.translate Hdf_Dialog.setWindowTitle(_translate("Hdf_Dialog", "View HDF file")) + self.label_3.setText(_translate("Hdf_Dialog", "

Colors: Time signals and spectra, accumulations

")) self.label.setText(_translate("Hdf_Dialog", "Label")) self.label_2.setText(_translate("Hdf_Dialog", "Group")) from ..lib.expandablewidget import ExpandableWidget diff --git a/nmreval/gui_qt/_py/phase_corr_dialog.py b/nmreval/gui_qt/_py/phase_corr_dialog.py index 02c5898..a8c9c51 100644 --- a/nmreval/gui_qt/_py/phase_corr_dialog.py +++ b/nmreval/gui_qt/_py/phase_corr_dialog.py @@ -2,9 +2,10 @@ # Form implementation generated from reading ui file 'resources/_ui/phase_corr_dialog.ui' # -# Created by: PyQt5 UI code generator 5.12.3 +# Created by: PyQt5 UI code generator 5.15.4 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. from PyQt5 import QtCore, QtGui, QtWidgets @@ -46,6 +47,7 @@ class Ui_SignalEdit(object): sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.ph0slider.sizePolicy().hasHeightForWidth()) self.ph0slider.setSizePolicy(sizePolicy) + self.ph0slider.setWrapping(True) self.ph0slider.setMinimum(-180.0) self.ph0slider.setMaximum(180.0) self.ph0slider.setObjectName("ph0slider") @@ -61,6 +63,7 @@ class Ui_SignalEdit(object): sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.ph1slider.sizePolicy().hasHeightForWidth()) self.ph1slider.setSizePolicy(sizePolicy) + self.ph1slider.setWrapping(True) self.ph1slider.setMinimum(-360.0) self.ph1slider.setMaximum(360.0) self.ph1slider.setObjectName("ph1slider") diff --git a/nmreval/gui_qt/_py/valueeditor.py b/nmreval/gui_qt/_py/valueeditor.py index 1425e0e..f2e2aeb 100644 --- a/nmreval/gui_qt/_py/valueeditor.py +++ b/nmreval/gui_qt/_py/valueeditor.py @@ -2,9 +2,10 @@ # Form implementation generated from reading ui file 'resources/_ui/valueeditor.ui' # -# Created by: PyQt5 UI code generator 5.12.3 +# Created by: PyQt5 UI code generator 5.15.4 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. from PyQt5 import QtCore, QtGui, QtWidgets @@ -47,30 +48,47 @@ class Ui_MaskDialog(object): spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) self.horizontalLayout.addItem(spacerItem) self.verticalLayout.addLayout(self.horizontalLayout) - self.gridLayout = QtWidgets.QGridLayout() - self.gridLayout.setContentsMargins(-1, 0, -1, -1) - self.gridLayout.setSpacing(3) - self.gridLayout.setObjectName("gridLayout") + self.line = QtWidgets.QFrame(MaskDialog) + self.line.setFrameShadow(QtWidgets.QFrame.Raised) + self.line.setLineWidth(3) + self.line.setFrameShape(QtWidgets.QFrame.HLine) + self.line.setObjectName("line") + self.verticalLayout.addWidget(self.line) + self.horizontalLayout_2 = QtWidgets.QHBoxLayout() + self.horizontalLayout_2.setContentsMargins(-1, 0, -1, -1) + self.horizontalLayout_2.setSpacing(3) + self.horizontalLayout_2.setObjectName("horizontalLayout_2") self.add_button = QtWidgets.QPushButton(MaskDialog) icon = QtGui.QIcon.fromTheme("list-add") self.add_button.setIcon(icon) self.add_button.setObjectName("add_button") - self.gridLayout.addWidget(self.add_button, 0, 0, 1, 2) + self.horizontalLayout_2.addWidget(self.add_button) self.delete_button = QtWidgets.QPushButton(MaskDialog) icon = QtGui.QIcon.fromTheme("list-remove") self.delete_button.setIcon(icon) self.delete_button.setObjectName("delete_button") - self.gridLayout.addWidget(self.delete_button, 0, 2, 1, 2) + self.horizontalLayout_2.addWidget(self.delete_button) self.split_button = QtWidgets.QPushButton(MaskDialog) self.split_button.setObjectName("split_button") - self.gridLayout.addWidget(self.split_button, 0, 4, 1, 2) + self.horizontalLayout_2.addWidget(self.split_button) + self.verticalLayout.addLayout(self.horizontalLayout_2) + self.line_2 = QtWidgets.QFrame(MaskDialog) + self.line_2.setLineWidth(3) + self.line_2.setFrameShape(QtWidgets.QFrame.HLine) + self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line_2.setObjectName("line_2") + self.verticalLayout.addWidget(self.line_2) + self.horizontalLayout_3 = QtWidgets.QHBoxLayout() + self.horizontalLayout_3.setContentsMargins(-1, 0, -1, -1) + self.horizontalLayout_3.setSpacing(3) + self.horizontalLayout_3.setObjectName("horizontalLayout_3") self.mask_button = QtWidgets.QPushButton(MaskDialog) self.mask_button.setObjectName("mask_button") - self.gridLayout.addWidget(self.mask_button, 1, 0, 1, 3) + self.horizontalLayout_3.addWidget(self.mask_button) self.unmaskbutton = QtWidgets.QPushButton(MaskDialog) self.unmaskbutton.setObjectName("unmaskbutton") - self.gridLayout.addWidget(self.unmaskbutton, 1, 3, 1, 3) - self.verticalLayout.addLayout(self.gridLayout) + self.horizontalLayout_3.addWidget(self.unmaskbutton) + self.verticalLayout.addLayout(self.horizontalLayout_3) self.label.setBuddy(self.spinBox) self.retranslateUi(MaskDialog) diff --git a/nmreval/gui_qt/data/shift_graphs.py b/nmreval/gui_qt/data/shift_graphs.py index fc49332..3ea8201 100644 --- a/nmreval/gui_qt/data/shift_graphs.py +++ b/nmreval/gui_qt/data/shift_graphs.py @@ -7,7 +7,7 @@ from ...lib.colors import Tab10 from ..Qt import QtGui, QtCore, QtWidgets from .._py.shift_scale_dialog import Ui_shift_dialog from ..lib.pg_objects import PlotItem -from ..lib.utils import SciSpinBox +from ..lib.spinboxes import SciSpinBox class QShift(QtWidgets.QDialog, Ui_shift_dialog): diff --git a/nmreval/gui_qt/data/signaledit/phase_dialog.py b/nmreval/gui_qt/data/signaledit/phase_dialog.py index 0f89208..6bd670f 100644 --- a/nmreval/gui_qt/data/signaledit/phase_dialog.py +++ b/nmreval/gui_qt/data/signaledit/phase_dialog.py @@ -1,8 +1,9 @@ import numpy as np -import pyqtgraph as pg +from pyqtgraph import mkPen from numpy import inf, linspace from numpy.fft import fft, fftfreq, fftshift +from ...lib.pg_objects import PlotItem, LogInfiniteLine from ....lib.importer import find_models from ....math import apodization as apodization from ....utils.text import convert @@ -24,10 +25,19 @@ class QPreviewDialogs(QtWidgets.QDialog): self.mode = '' + def setRange(self, xlim: list, ylim: list, logmode: list[bool]): + self.graphicsView.getPlotItem().setLogMode(x=logmode[0], y=logmode[1]) + if logmode[0]: + xlim = [np.log10(x) for x in xlim] + if logmode[1]: + ylim = [np.log10(y) for y in ylim] + + self.graphicsView.setRange(xRange=xlim, yRange=ylim, padding=0, disableAutoRange=True) + def add_data(self, x, y): self.data.append((x, y)) - real_plt = pg.PlotDataItem(x=x, y=y.real, pen=pg.mkColor('b')) - imag_plt = pg.PlotDataItem(x=x, y=y.imag, pen=pg.mkColor('r')) + real_plt = PlotItem(x=x, y=y.real, pen=mkPen('b')) + imag_plt = PlotItem(x=x, y=y.imag, pen=mkPen('r')) self.graphs.append((real_plt, imag_plt)) self.graphicsView.addItem(real_plt) self.graphicsView.addItem(imag_plt) @@ -70,7 +80,7 @@ class QPhasedialog(QPreviewDialogs, Ui_SignalEdit): self.mode = 'ph' - self.pvt_line = pg.InfiniteLine(pos=0, movable=True) + self.pvt_line = LogInfiniteLine(pos=0, movable=True) self.graphicsView.addItem(self.pvt_line) self.pvt_line.sigPositionChanged.connect(self.move_line) @@ -109,7 +119,7 @@ class QApodDialog(QPreviewDialogs, Ui_ApodEdit): self.apodcombobox.addItem(ap().name) self.apodcombobox.blockSignals(False) - self.apod_graph = pg.PlotDataItem(x=[], y=[]) + self.apod_graph = PlotItem(x=[], y=[]) self.graphicsView.addItem(self.apod_graph) self.mode = 'ap' @@ -117,14 +127,14 @@ class QApodDialog(QPreviewDialogs, Ui_ApodEdit): self.change_apodization(0) def add_data(self, x, y): - real_plt = pg.PlotDataItem(x=x, y=y.real, pen=pg.mkPen('b')) - # imag_plt = pg.PlotDataItem(x=x, y=y.imag, pen=pg.mkPen('r')) + real_plt = PlotItem(x=x, y=y.real, pen=mkPen('b')) + # imag_plt = (x=x, y=y.imag, pen=pg.mkPen('r')) self.graphicsView.addItem(real_plt) # self.graphicsView.addItem(imag_plt) y_fft = fftshift(fft(y)) x_fft = fftshift(fftfreq(len(x), d=x[1]-x[0])) - real_plt_fft = pg.PlotDataItem(x=x_fft, y=y_fft.real, pen=pg.mkPen('b')) + real_plt_fft = PlotItem(x=x_fft, y=y_fft.real, pen=mkPen('b')) # imag_plt_fft = pg.PlotDataItem(x=x_fft, y=y_fft.imag, pen=pg.mkPen('b')) self.graphicsView_2.addItem(real_plt_fft) # self.graphicsView_2.addItem(imag_plt_fft) diff --git a/nmreval/gui_qt/io/hdfreader.py b/nmreval/gui_qt/io/hdfreader.py index 9a042c2..d272413 100644 --- a/nmreval/gui_qt/io/hdfreader.py +++ b/nmreval/gui_qt/io/hdfreader.py @@ -61,10 +61,10 @@ class QHdfViewer(QtWidgets.QDialog, Ui_Hdf_Dialog): label_item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsSelectable) label_item.setCheckState(0, QtCore.Qt.Unchecked) if child.type == 'signal': - label_item.setBackground(0, QtGui.QBrush(QtGui.QColor('cyan'))) + label_item.setBackground(0, QtGui.QBrush(QtGui.QColor(31, 119, 180))) label_item.setCheckState(1, QtCore.Qt.Unchecked) elif child.type == 'points': - label_item.setBackground(0, QtGui.QBrush(QtGui.QColor('red'))) + label_item.setBackground(0, QtGui.QBrush(QtGui.QColor(255, 127, 14))) item.addChild(label_item) self._populate_tree(child, label_item) diff --git a/nmreval/gui_qt/lib/namespace.py b/nmreval/gui_qt/lib/namespace.py index 9229176..0083683 100644 --- a/nmreval/gui_qt/lib/namespace.py +++ b/nmreval/gui_qt/lib/namespace.py @@ -137,6 +137,7 @@ class Namespace: class QNamespaceWidget(QtWidgets.QWidget, Ui_Form): selected = QtCore.pyqtSignal(str) + sendKey = QtCore.pyqtSignal(str) def __init__(self, parent=None): super().__init__(parent=parent) @@ -186,7 +187,9 @@ class QNamespaceWidget(QtWidgets.QWidget, Ui_Form): value_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) key_item.setData(QtCore.Qt.UserRole, alias) + key_item.setData(QtCore.Qt.UserRole+1, entry) value_item.setData(QtCore.Qt.UserRole, alias) + value_item.setData(QtCore.Qt.UserRole+1, entry) row = self.namespace_table.rowCount() self.namespace_table.setRowCount(row+1) @@ -209,3 +212,4 @@ class QNamespaceWidget(QtWidgets.QWidget, Ui_Form): @QtCore.pyqtSlot(QtWidgets.QTableWidgetItem, name='on_namespace_table_itemDoubleClicked') def item_selected(self, item: QtWidgets.QTableWidgetItem): self.selected.emit(item.data(QtCore.Qt.UserRole)) + self.sendKey.emit(item.data(QtCore.Qt.UserRole+1)) diff --git a/nmreval/gui_qt/lib/spinboxes.py b/nmreval/gui_qt/lib/spinboxes.py new file mode 100644 index 0000000..dfaced2 --- /dev/null +++ b/nmreval/gui_qt/lib/spinboxes.py @@ -0,0 +1,45 @@ +from math import inf + +from ..Qt import QtWidgets, QtGui + + +class SciSpinBox(QtWidgets.QDoubleSpinBox): + def __init__(self, parent=None): + super().__init__(parent=parent) + + self.validator = QtGui.QDoubleValidator(self) + + self.setMinimum(-inf) + self.setMaximum(inf) + self.setDecimals(1000) + self.precision = 0.001 + + self._prev_value = float(self.lineEdit().text()) + + def valueFromText(self, text: str) -> float: + try: + self._prev_value = float(self.cleanText()) + except ValueError: + pass + return self._prev_value + + def textFromValue(self, value: float) -> str: + if value == 0: + return '0' + else: + return f'{value:.3e}' + + def stepBy(self, step: int): + self._prev_value = self.value() + + new_value = self._prev_value + if new_value != 0.0: + new_value *= 10**(step/19.) + else: + new_value = 0.001 + + self.setValue(new_value) + self.lineEdit().setText(f'{new_value:.3e}') + + def validate(self, text, pos): + return self.validator.validate(text, pos) diff --git a/nmreval/gui_qt/lib/utils.py b/nmreval/gui_qt/lib/utils.py index f5a49c6..32b292b 100644 --- a/nmreval/gui_qt/lib/utils.py +++ b/nmreval/gui_qt/lib/utils.py @@ -1,4 +1,3 @@ -from math import inf from contextlib import contextmanager from numpy import linspace from scipy.interpolate import interp1d @@ -17,48 +16,6 @@ def busy_cursor(): QtWidgets.QApplication.restoreOverrideCursor() -class SciSpinBox(QtWidgets.QDoubleSpinBox): - def __init__(self, parent=None): - super().__init__(parent=parent) - - self.validator = QtGui.QDoubleValidator(self) - - self.setMinimum(-inf) - self.setMaximum(inf) - self.setDecimals(1000) - self.precision = 0.001 - - self._prev_value = float(self.lineEdit().text()) - - def valueFromText(self, text: str) -> float: - try: - self._prev_value = float(self.cleanText()) - except ValueError: - pass - return self._prev_value - - def textFromValue(self, value: float) -> str: - if value == 0: - return '0' - else: - return f'{value:.3e}' - - def stepBy(self, step: int): - self._prev_value = self.value() - - new_value = self._prev_value - if new_value != 0.0: - new_value *= 10**(step/19.) - else: - new_value = 0.001 - - self.setValue(new_value) - self.lineEdit().setText(f'{new_value:.3e}') - - def validate(self, text, pos): - return self.validator.validate(text, pos) - - class RdBuCMap: # taken from Excel sheet from colorbrewer.org _rdbu = [ diff --git a/nmreval/gui_qt/main/mainwindow.py b/nmreval/gui_qt/main/mainwindow.py index dc0a950..c0090b3 100644 --- a/nmreval/gui_qt/main/mainwindow.py +++ b/nmreval/gui_qt/main/mainwindow.py @@ -714,6 +714,7 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): @QtCore.pyqtSlot(str) def do_preview(self, mode): + if mode == 'ap': dialog = QApodDialog(parent=self) elif mode == 'ph': @@ -721,6 +722,8 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): else: raise ValueError('Unknown preview mode %s' % str(mode)) + dialog.setRange(*self.current_graph_widget.ranges, self.current_graph_widget.log) + for sid in self.current_graph_widget.active: data_mode = self.management[sid].mode tobeadded = False diff --git a/nmreval/io/hdfreader.py b/nmreval/io/hdfreader.py index 889c9b0..2820ef1 100644 --- a/nmreval/io/hdfreader.py +++ b/nmreval/io/hdfreader.py @@ -51,6 +51,13 @@ class HdfNode: self.children = OrderedDict() self.children[key] = value + def __contains__(self, key): + if self.children is None: + return False + + return key in self.children + + def clear(self): self.name = '' self.type = 'group' @@ -386,3 +393,7 @@ class HdfReader(HdfNode): pass return value + + def get_scripts(self) -> tuple[str, str]: + return unicode_(self.file['scripts/experiment_script'][()]), \ + unicode_(self.file['scripts/result_script'][()]) diff --git a/nmreval/math/kww.py b/nmreval/math/kww.py index 48bf395..fb02ed5 100644 --- a/nmreval/math/kww.py +++ b/nmreval/math/kww.py @@ -296,8 +296,8 @@ def _kww_sin_single(w, tau, beta): sign = np.sign(_w) - low_approx = _w < _kwwc_lim_low(beta) - high_approx = _w > _kwwc_lim_hig(beta) + low_approx = _w < _kwws_lim_low(beta) + high_approx = _w > _kwws_lim_hig(beta) mid_integration = ~(low_approx | high_approx) ret_val = np.zeros_like(_w, dtype=float) diff --git a/nmreval/math/logfourier.py b/nmreval/math/logfourier.py index 8b5c1c8..1b717e0 100644 --- a/nmreval/math/logfourier.py +++ b/nmreval/math/logfourier.py @@ -20,7 +20,7 @@ and \sum_{i=0}^{N-1} \frac{f(t_{i+i}) - f(t_i)}{f(t_{i+i}) - f(t_i)}{t_{i+1} - t_i} \frac{\sin(\omega t_{i+1}) - \sin(\omega t_i)}{\omega^2} -More details can befound in [Blo] +More details can be found in [Blo] [Blo] Blochowicz, Th.: Ph.D. Thesis, Universität Bayreuth (2003) @@ -30,7 +30,7 @@ More details can befound in [Blo] def logft_cos(x, y, new_x=None): n = x.size if new_x is None: - new_x = 2 * np.pi * np.geomspace(1 / np.max(x), 1 / np.min(x), num=n) + new_x = 2*np.pi * np.geomspace(1/np.max(x), 1/np.min(x), num=n) dydx = np.diff(y) / np.diff(x) @@ -54,7 +54,7 @@ def logft_cos(x, y, new_x=None): def logft_sin(x, y, new_x=None): n = x.size if new_x is None: - new_x = 2 * np.pi * np.geomspace(1 / np.max(x), 1 / np.min(x), num=n) + new_x = 2*np.pi * np.geomspace(1/np.max(x), 1/np.min(x), num=n) dydx = np.diff(y) / np.diff(x) @@ -79,9 +79,10 @@ def logft_sin(x, y, new_x=None): def logft_cmplx(x, y, new_x=None, backward: bool = False): n = x.size if new_x is None: - new_x = 2 * np.pi * np.logspace(-np.log10(np.max(x)), -np.log10(np.min(x)), num=n) + new_x = 2 * np.pi * np.geomspace(1/np.max(x), 1/np.min(x), num=n) dydx = np.diff(y) / np.diff(x) + sign = 2*backward - 1 if n < 5000: wt = new_x[:, None] * x[None, :] diff --git a/nmreval/models/__init__.py b/nmreval/models/__init__.py index 2a1aa1e..f9ed641 100755 --- a/nmreval/models/__init__.py +++ b/nmreval/models/__init__.py @@ -5,6 +5,9 @@ Fit functions (:mod:`nmreval.models`) .. currentmodule:: nmreval.models +Basic functions +^^^^^^^^^^^^^^^ + .. autosummary:: :toctree: generated :nosignatures: @@ -20,6 +23,14 @@ Fit functions (:mod:`nmreval.models`) Log Sine +Correlation functions +^^^^^^^^^^^^^^^^^^^^^ + +.. autosummary:: + :toctree: generated + + Exponential + """ from .basic import * diff --git a/nmreval/models/bds.py b/nmreval/models/bds.py index 3fefd0a..1594652 100644 --- a/nmreval/models/bds.py +++ b/nmreval/models/bds.py @@ -88,7 +88,33 @@ class EpsInfty: return ret_val -class CCwithHFW: +class _HNWithHF: + name = 'HN + HF wing' + type = 'Dielectric Spectroscopy' + equation = r'\Delta\epsilon HN(\omega, \tau, \alpha, \gamma) / CD(\omega, \tau_{c}, \alpha\gamma-\delta)' + params = [r'\Delta\epsilon', r'\tau', r'\alpha', r'\gamma', r'\tau_{c}', '\delta'] + bounds = [(0, None), (0, None), (0, 1), (0, 1), (0, None), (0, 1)] + iscomplex = True + + @staticmethod + def func(x, deps, tau, alpha, gamma, tauc, delta, complex_mode: int = 0): + w = 2 * np.pi * x + numerator = (1 + 1j*w*tauc) ** (gamma-delta) + denominator = (1 + (1j*w*tau)**alpha) ** gamma + + epsilon = deps * np.conjugate(numerator / denominator) + + if complex_mode == 0: + return epsilon + elif complex_mode == 1: + return epsilon.real + elif complex_mode == 2: + return epsilon.imag + else: + raise ValueError(f'{complex_mode!r} has not value 0, 1, or 2') + + +class CCWithHF: name = 'CC + HF wing' type = 'Dielectric Spectroscopy' equation = r'\Delta\epsilon CC(\omega, \tau, \alpha) / CD(\omega, \tau_{c}, \beta-\delta)' @@ -98,23 +124,10 @@ class CCwithHFW: @staticmethod def func(x, deps, tau, alpha, tauc, delta, complex_mode: int = 0): - w = 2*np.pi*x - numerator = (1 + 1j*w*tauc)**(alpha-delta) - denominator = 1 + (1j*w*tau)**alpha - - epsilon = deps * np.conjugate(numerator / denominator) - - if complex_mode == 0: - return epsilon - elif complex_mode == 1: - return epsilon.real - elif complex_mode == 2: - return epsilon.imag - else: - raise ValueError(f'{complex_mode!r} has not value 0, 1, or 2') + return _HNWithHF.func(x, deps, tau, alpha, 1, tauc, delta, complex_mode=complex_mode) -class CDwithHFW: +class CDWithHF: name = 'CD + HF wing' type = 'Dielectric Spectroscopy' equation = r'\Delta\epsilon CD(\omega, \tau, \gamma) / CD(\omega, \tau_{c}, \gamma-\delta)' @@ -124,20 +137,7 @@ class CDwithHFW: @staticmethod def func(x, deps, tau, gamma, tauc, delta, complex_mode: int = 0): - w = 2*np.pi*x - numerator = (1 + 1j*w*tauc)**(gamma-delta) - denominator = (1 + 1j*w*tau)**gamma - - epsilon = deps * np.conjugate(numerator / denominator) - - if complex_mode == 0: - return epsilon - elif complex_mode == 1: - return epsilon.real - elif complex_mode == 2: - return epsilon.imag - else: - raise ValueError(f'{complex_mode!r} has not value 0, 1, or 2') + return _HNWithHF.func(x, deps, tau, 1, gamma, tauc, delta, complex_mode=complex_mode) class PowerLawBDS: @@ -232,3 +232,52 @@ class DerivativeColeDavidson: denom = (1 + omtau**2)**((1+g)/2.) return eps * np.pi * numer / denom / 2. + + +class _DerivativeHNWithHF: + name = 'Derivative (HN + HF wing)' + type = 'Dielectric Spectroscopy' + params = [r'\Delta\epsilon', r'\tau', r'\alpha', r'\gamma', r'\tau_{c}', r'\delta'] + bounds = [(0, None), (0, None), (0, 1), (0, 1), (0, None), (0, 1)] + + @staticmethod + def func(x, deps, tau, alpha, gamma, tauc, delta): + w = 2*np.pi*x + + a_pi2 = alpha*np.pi/2 + w_lamb = (w*tau)**alpha + w_mu = w*tauc + + ag_d = alpha*gamma - delta + + phi = np.sin(a_pi2) * w_lamb / (1+np.cos(a_pi2) * w_lamb) + chi = ag_d*np.arctan(w_mu) - gamma*np.arctan(phi) + m_squared = 1 + w_mu**2 + l_squared = 1 + 2*np.cos(a_pi2)*w_lamb + w_lamb**2 + + term1 = (ag_d * w_mu**2 / m_squared - alpha*gamma * w_lamb * (w_lamb + np.cos(a_pi2)) / l_squared) * np.cos(chi) + term2 = (ag_d * w_mu / m_squared - alpha*gamma*np.sin(a_pi2) * w_lamb / l_squared) * np.sin(chi) + + return deps * (term2 - term1) * m_squared**(ag_d/2) / l_squared**(gamma/2) + + +class DerivativeCCWithHF: + name = 'Derivative (CC + HF wing)' + type = 'Dielectric Spectroscopy' + params = [r'\Delta\epsilon', r'\tau', r'\alpha', r'\tau_{c}', r'\delta'] + bounds = [(0, None), (0, None), (0, 1), (0, None), (0, 1)] + + @staticmethod + def func(x, deps, tau, alpha, tauc, delta): + return _DerivativeHNWithHF.func(x, deps, tau, alpha, 1, tauc, delta) + + +class DerivativeCDWithHF: + name = 'Derivative (CD + HF wing)' + type = 'Dielectric Spectroscopy' + params = [r'\Delta\epsilon', r'\tau', r'\gamma', r'\tau_{c}', r'\delta'] + bounds = [(0, None), (0, None), (0, 1), (0, None), (0, 1)] + + @staticmethod + def func(x, deps, tau, gamma, tauc, delta): + return _DerivativeHNWithHF.func(x, deps, tau, 1, gamma, tauc, delta) diff --git a/nmreval/models/correlationfuncs.py b/nmreval/models/correlationfuncs.py index 575e8b2..4bb707c 100644 --- a/nmreval/models/correlationfuncs.py +++ b/nmreval/models/correlationfuncs.py @@ -1,3 +1,11 @@ +""" +********************* +Correlation functions +********************* + +Decay functions (KWW, ML,...) +""" + import numpy as np from scipy.special import gammaincc @@ -5,6 +13,9 @@ from ..math.mittagleffler import mlf class Exponential: + """ + Stretched exponential function + """ type = 'Correlation function' name = 'Stretched Exponential' equation = r'C*exp(-(x/\tau)^{\beta})' @@ -12,6 +23,18 @@ class Exponential: @staticmethod def func(x, c, x0, beta): + r""" + Stretched exponential function + + .. math:: + x = c\exp(-x/x_0) + + Args: + x (array-like): Input values + c (float): Amplitude + x0 (float): + beta (float): Stretching parameter + """ return c*np.exp(-(x/x0)**beta) diff --git a/nmreval/models/fieldcycling.py b/nmreval/models/fieldcycling.py index dbc2ebd..1da10c5 100644 --- a/nmreval/models/fieldcycling.py +++ b/nmreval/models/fieldcycling.py @@ -49,7 +49,7 @@ class ColeColeFC(_AbstractFC): relax = Relaxation(distribution=ColeCole) -class ColeDavidsionFC(_AbstractFC): +class ColeDavidsonFC(_AbstractFC): name = 'Cole-Davidson' params = _AbstractFC.params + [r'\gamma'] bounds = _AbstractFC.bounds + [(0, 1)] diff --git a/requirements.txt b/requirements.txt index 5e8228b..96d9f90 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,5 +4,5 @@ scipy pyqtgraph bsddb3 h5py -PyQt5 +pyqt diff --git a/resources/_ui/editsignalwidget.ui b/resources/_ui/editsignalwidget.ui index 8e109d1..e9a6e63 100644 --- a/resources/_ui/editsignalwidget.ui +++ b/resources/_ui/editsignalwidget.ui @@ -176,6 +176,9 @@ + + true + -360.000000000000000 @@ -229,6 +232,9 @@ 0 + + true + -180.000000000000000 diff --git a/resources/_ui/hdftree.ui b/resources/_ui/hdftree.ui index 5ab5c3c..e3ea9d8 100755 --- a/resources/_ui/hdftree.ui +++ b/resources/_ui/hdftree.ui @@ -13,68 +13,88 @@ View HDF file - - - 3 - - - 4 - - - 4 - - - 4 - - - 4 - + - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 2 - - - - - - - - Label - - - - - - - Group - - - - - - - + + + <html><head/><body><p><span style=" color:#000000;">Colors: </span><span style=" color:#1f77b4;">Time signals and spectra</span><span style=" color:#000000;">, </span><span style=" color:#ff7f0e;">accumulations</span></p></body></html> + + + Qt::RichText + - + + + Qt::Vertical + + + false + + + + + 0 + + + 0 + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 2 + + + + + Label + + + + + + + + + + Group + + + + + + + + + + + + + + + 0 + 0 + + + + diff --git a/resources/_ui/phase_corr_dialog.ui b/resources/_ui/phase_corr_dialog.ui index 20d14da..caec699 100644 --- a/resources/_ui/phase_corr_dialog.ui +++ b/resources/_ui/phase_corr_dialog.ui @@ -69,6 +69,9 @@ 0 + + true + -180.000000000000000 @@ -95,6 +98,9 @@ 0 + + true + -360.000000000000000 diff --git a/resources/_ui/valueeditor.ui b/resources/_ui/valueeditor.ui index 89cf64a..01452ab 100644 --- a/resources/_ui/valueeditor.ui +++ b/resources/_ui/valueeditor.ui @@ -96,14 +96,27 @@ - - - 0 + + + QFrame::Raised + + 3 + + + Qt::Horizontal + + + + + 3 - + + 0 + + Add row @@ -114,7 +127,7 @@ - + Delete rows @@ -125,14 +138,34 @@ - + Split after selection - + + + + + + 3 + + + Qt::Horizontal + + + + + + + 3 + + + 0 + + <html><head/><body><p>Masked rows are shown in green</p></body></html> @@ -142,7 +175,7 @@ - + Show all diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 9eb7cc4..0000000 --- a/setup.cfg +++ /dev/null @@ -1,47 +0,0 @@ -[metadata] -name = nmreval -version = 0.1 -description = Evaluation of data -long_description = file: README.md -author = Dominik Demuth -author_email = dominik.demuth@physik.tu-darmstadt.de -install_requires = - numpy - scipy - matplotlib - bsddb3 - pyqtgraph - pyqt - h5py -keywords = nmr, physics, science -classifiers = - Development Status :: 3 - Alpha - Intended Audience :: End Users/Desktop - Intended Audience :: Science/Research - Topic :: Scientific/Engineering :: Physics - Topic :: Scientific/Engineering :: Visualization - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 - Programming Language :: Python :: 3 :: Only -license = BSD 3-Clause License - -[options] -include_package_data = True -packages = find: -scripts = bin/evaluate.py - -[options.packages.find] -include = - nmreval* - resources* - -[options.package_data] -* = *.txt, *.npz, *.png, *.json - - - - - diff --git a/setup.py b/setup.py index e730e9c..b0aecbd 100755 --- a/setup.py +++ b/setup.py @@ -1,74 +1,4 @@ -"""A setuptools based setup module. - -See: -https://packaging.python.org/guides/distributing-packages-using-setuptools/ -https://github.com/pypa/sampleproject -""" - # Always prefer setuptools over distutils -from setuptools import setup, find_packages -import pathlib +from setuptools import setup -here = pathlib.Path(__file__).parent.resolve() - -# Get the long description from the README file -long_description = (here / 'README.md').read_text(encoding='utf-8') - -# Arguments marked as "Required" below must be included for upload to PyPI. -# Fields marked as "Optional" may be commented out. - -setup( - # name='nmreval', # Required - # version='0.1', # Required - # description='A sample Python project', # Optional - # long_description=long_description, # Optional - # long_description_content_type='text/markdown', # Optional (see note above) - # url='https://chaos3.fkp.physik.tu-darmstadt.de/source/nmreval/', # Optional - # author='Dominik Demuth', # Optional - # author_email='dominik.demuth@physik.tu-darmstadt.de', # Optional - # classifiers=[ # Optional - # 'Development Status :: 3 - Alpha', - # 'Intended Audience :: End Users/Desktop', - # 'Intended Audience :: Science/Research', - # 'Topic :: Scientific/Engineering :: Physics', - # 'Topic :: Scientific/Engineering :: Visualization', - # 'License :: OSI Approved :: BSD 3-Clause License', - # 'Programming Language :: Python :: 3', - # 'Programming Language :: Python :: 3.7', - # 'Programming Language :: Python :: 3.8', - # 'Programming Language :: Python :: 3.9', - # "Programming Language :: Python :: 3.10", - # 'Programming Language :: Python :: 3 :: Only', - # ], - # - # keywords='nmr, physics, science', # Optional - # package_dir={'': '.'}, # Optional - # packages=find_packages(where='.'), # Required - # include_package_data=True, - # python_requires='>=3.7', - # install_requires=[ - # 'numpy', - # 'scipy', - # 'matplotlib', - # 'bsddb3', - # 'pyqtgraph', - # 'pyqt', - # 'h5py' - # ], # Optional - # # extras_require={ # Optional - # # 'dev': ['check-manifest'], - # # 'test': ['coverage'], - # # }, - # package_data={ # Optional - # 'sample': ['package_data.dat'], - # }, - # data_files=[('my_data', ['data/data_file'])], # Optional - # entry_points={ # Optional - # 'console_scripts': [ - # 'evaluate=bin:evaluate', - # ], - # }, - # project_urls={ # Optional - # 'Source': 'https://chaos3.fkp.physik.tu-darmstadt.de/source/nmreval/', - # }, -) +setup()