From 58e86f4abc2fe6c09d14d7548b3eb4a7e988bb17 Mon Sep 17 00:00:00 2001
From: Dominik Demuth Uses simple latex syntax, does not support italic/math environment. Sub-/superscripts need curly brackets. Example: \\alpha^{123}
Uses simple latex syntax, does not support italic/math environment. Sub-/superscripts need curly brackets.
Example: \\alpha^{123}
")) self.label_7.setText(_translate("GraphWindow", "Y Axis")) + self.yaxis_linedit.setToolTip(_translate("GraphWindow", "Uses simple latex syntax, does not support italic/math environment. Sub-/superscripts need curly brackets.
Example: \\alpha^{123}
")) self.checkBox.setText(_translate("GraphWindow", "Show legend")) from ..lib.graph_items import NMRPlotWidget from ..lib.listwidget import QListWidgetSelect diff --git a/src/gui_qt/fit/result.py b/src/gui_qt/fit/result.py index 7c4b85c..01f44ee 100644 --- a/src/gui_qt/fit/result.py +++ b/src/gui_qt/fit/result.py @@ -1,6 +1,7 @@ from math import isnan from pyqtgraph import mkBrush, mkPen +from numpy import abs as np_abs, isfinite as np_isfinite from nmreval.utils.text import convert from ..lib.graph_items import logTickValues @@ -69,12 +70,16 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog): self.cmap = RdBuCMap(vmin=-1, vmax=1) - self.graph_checkBox.stateChanged.connect(lambda x: self.graph_comboBox.setEnabled(x == QtCore.Qt.Unchecked)) + self.graph_checkBox.stateChanged.connect( + lambda x: self.graph_comboBox.setEnabled(x == QtCore.Qt.CheckState.Unchecked) + ) self.logy_box.stateChanged.connect(lambda x: self.fit_plot.setLogMode(y=bool(x))) self.logx_box.stateChanged.connect(lambda x: self.fit_plot.setLogMode(x=bool(x))) self.resid_plot.setXLink(self.fit_plot) + self.buttonGroup.buttonToggled.connect(self._plot_residuals) + self.set_results(results) def __call__(self, results: list): @@ -112,7 +117,7 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog): @QtCore.pyqtSlot(int, name='on_sets_comboBox_currentIndexChanged') def set_parameter(self, idx: int): - set_id = self.sets_comboBox.itemData(idx, QtCore.Qt.UserRole) + set_id = self.sets_comboBox.itemData(idx, QtCore.Qt.ItemDataRole.UserRole) res = self._results[set_id] self.param_tableWidget.setRowCount(len(res.parameter)) @@ -138,11 +143,11 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog): def change_opts(self, _): idx = self.sets_comboBox.currentIndex() - self._opts[idx] = (self.reject_fit_checkBox.checkState() == QtCore.Qt.Checked, - self.del_prev_checkBox.checkState() == QtCore.Qt.Checked) + self._opts[idx] = (self.reject_fit_checkBox.checkState() == QtCore.Qt.CheckState.Checked, + self.del_prev_checkBox.checkState() == QtCore.Qt.CheckState.Checked) def show_results(self, idx): - set_id = self.sets_comboBox.itemData(idx, QtCore.Qt.UserRole) + set_id = self.sets_comboBox.itemData(idx, QtCore.Qt.ItemDataRole.UserRole) self.set_plot(set_id) self.set_correlation(set_id) self.set_statistics(set_id) @@ -184,8 +189,6 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog): self.data_graph_imag.setData(x=res.x_data, y=res.y_data.imag) self.fit_graph.setData(x=res.x, y=res.y.real) self.fit_graph_imag.setData(x=res.x, y=res.y.imag) - self.resid_graph.setData(x=res.x_data, y=res.residual.real) - self.resid_graph_imag.setData(x=res.x_data, y=res.residual.imag) for i, f in enumerate(sub_funcs): item = PlotItem(x=f.x, y=f.y.real, pen=mkPen({'color': i, 'style': 2})) @@ -194,8 +197,6 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog): self.fit_plot.addItem(item) else: - self.resid_graph.setData(x=res.x_data, y=res.residual) - self.resid_graph_imag.setData(x=[], y=[]) self.data_graph.setData(x=res.x_data, y=res.y_data) self.data_graph_imag.setData(x=[], y=[]) self.fit_graph.setData(x=res.x, y=res.y) @@ -205,6 +206,8 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog): item = PlotItem(x=f.x, y=f.y, pen=mkPen({'color': i, 'style': 2})) self.fit_plot.addItem(item) + self._plot_residuals(idx) + self.logx_box.blockSignals(True) self.logx_box.setChecked(res.islog) self.logx_box.blockSignals(False) @@ -225,6 +228,31 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog): else: self.fit_plot.enableAutoRange() + def _plot_residuals(self, idx: str = None): + print(idx) + if idx is None or isinstance(idx, QtWidgets.QAbstractButton): + idx = self.sets_comboBox.currentData(QtCore.Qt.ItemDataRole.UserRole) + + res = self._results[idx] + if res.iscomplex: + if self.rel_dev_button.isChecked(): + print('rel') + self.resid_graph.setData(x=res.x_data, y=res.residual.real/np_abs(res.y_data.real)) + print(res.y_data.imag) + if all(np_isfinite(res.y_data.imag)): + self.resid_graph_imag.setData(x=res.x_data, y=res.residual.imag/np_abs(res.y_data.imag)) + else: + print('abs') + self.resid_graph.setData(x=res.x_data, y=res.residual.real) + self.resid_graph_imag.setData(x=res.x_data, y=res.residual.imag) + + else: + if self.rel_dev_button.isChecked(): + self.resid_graph.setData(x=res.x_data, y=res.residual / np_abs(res.y_data)) + else: + self.resid_graph.setData(x=res.x_data, y=res.residual) + self.resid_graph_imag.setData(x=[], y=[]) + def set_correlation(self, idx: str): while self.corr_tableWidget.rowCount(): self.corr_tableWidget.removeRow(0) @@ -258,20 +286,20 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog): self.stats_tableWidget.setRowCount(len(res.statistics)+3) it = QtWidgets.QTableWidgetItem(f'{res.dof}') - it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable) + it.setFlags(it.flags() ^ QtCore.Qt.ItemFlag.ItemIsEditable) self.stats_tableWidget.setVerticalHeaderItem(0, QtWidgets.QTableWidgetItem('DoF')) self.stats_tableWidget.setItem(0, 0, it) for col, (name, _, dof) in enumerate(self._previous_fits[idx], start=1): self.stats_tableWidget.setHorizontalHeaderItem(0, QtWidgets.QTableWidgetItem(name)) it = QtWidgets.QTableWidgetItem(f'{dof}') - it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable) + it.setFlags(it.flags() ^ QtCore.Qt.ItemFlag.ItemIsEditable) self.stats_tableWidget.setItem(0, col, it) for row, (k, v) in enumerate(res.statistics.items(), start=1): self.stats_tableWidget.setVerticalHeaderItem(row, QtWidgets.QTableWidgetItem(k)) it = QtWidgets.QTableWidgetItem(f'{v:.4f}') - it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable) + it.setFlags(it.flags() ^ QtCore.Qt.ItemFlag.ItemIsEditable) self.stats_tableWidget.setItem(row, 0, it) best_idx = -1 @@ -282,7 +310,7 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog): else: best_idx = col if best_val > stats[k] else max(0, best_idx) it = QtWidgets.QTableWidgetItem(f'{stats[k]:.4f}') - it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable) + it.setFlags(it.flags() ^ QtCore.Qt.ItemFlag.ItemIsEditable) self.stats_tableWidget.setItem(row, col, it) if best_idx > -1: @@ -299,11 +327,11 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog): for col, (_, stats, dof) in enumerate(self._previous_fits[idx], start=1): f_value, prob_f = res.f_test(stats['chi^2'], dof) it = QtWidgets.QTableWidgetItem(f'{f_value:.4g}') - it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable) + it.setFlags(it.flags() ^ QtCore.Qt.ItemFlag.ItemIsEditable) self.corr_tableWidget.setItem(row, col, it) it = QtWidgets.QTableWidgetItem(f'{prob_f:.4g}') - it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable) + it.setFlags(it.flags() ^ QtCore.Qt.ItemFlag.ItemIsEditable) if prob_f < 0.05: it.setBackground(QtGui.QColor('green')) it.setForeground(QtGui.QColor('white')) @@ -319,14 +347,14 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog): elif button_type == self.buttonBox.Ok: graph = '-1' if self.parameter_checkbox.isChecked(): - if self.graph_checkBox.checkState() == QtCore.Qt.Checked: + if self.graph_checkBox.checkState() == QtCore.Qt.CheckState.Checked: graph = '' else: graph = self.graph_comboBox.currentData() plot_fits = self.curve_checkbox.isChecked() - parts = self.partial_checkBox.checkState() == QtCore.Qt.Checked + parts = self.partial_checkBox.checkState() == QtCore.Qt.CheckState.Checked extrapolate = [None, None, None] error = [] @@ -382,7 +410,7 @@ class FitExtension(QtWidgets.QDialog): gridLayout.addWidget(self.num_pts, 2, 1, 1, 1) self.buttonBox = QtWidgets.QDialogButtonBox() - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal) self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel | QtWidgets.QDialogButtonBox.Ok) gridLayout.addWidget(self.buttonBox, 3, 0, 1, 2) diff --git a/src/gui_qt/graphs/graphwindow.py b/src/gui_qt/graphs/graphwindow.py index 2d8c67f..9369bf3 100644 --- a/src/gui_qt/graphs/graphwindow.py +++ b/src/gui_qt/graphs/graphwindow.py @@ -73,7 +73,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow): self.scene = self.plotItem.scene() self.scene.sigMouseMoved.connect(self.move_mouse) - self.checkBox.stateChanged.connect(lambda x: self.legend.setVisible(x == QtCore.Qt.Checked)) + self.checkBox.stateChanged.connect(lambda x: self.legend.setVisible(x == QtCore.Qt.CheckState.Checked)) self.label_button.toggled.connect(lambda x: self.label_widget.setVisible(x)) self.limit_button.toggled.connect(lambda x: self.limit_widget.setVisible(x)) self.gridbutton.toggled.connect(lambda x: self.graphic.showGrid(x=x, y=x)) @@ -237,9 +237,13 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow): self.error_plots[n] = err_plot list_item = QtWidgets.QListWidgetItem(real_plot.opts.get('name', '')) - list_item.setData(QtCore.Qt.UserRole, n) - list_item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable) - list_item.setCheckState(QtCore.Qt.Checked) + list_item.setData(QtCore.Qt.ItemDataRole.UserRole, n) + list_item.setFlags( + QtCore.Qt.ItemFlag.ItemIsEnabled | + QtCore.Qt.ItemFlag.ItemIsSelectable | + QtCore.Qt.ItemFlag.ItemIsUserCheckable + ) + list_item.setCheckState(QtCore.Qt.CheckState.Checked) self.listWidget.addItem(list_item) toplevel += 1 @@ -271,7 +275,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow): for i in range(self.listWidget.count()-1, 0, -1): item = self.listWidget.item(i) - if item.data(QtCore.Qt.UserRole) in name: + if item.data(QtCore.Qt.ItemDataRole.UserRole) in name: self.listWidget.takeItem(i) self.listWidget.blockSignals(False) @@ -465,24 +469,37 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow): _y = pos.y() self.mousePositionChanged.emit(_x, _y) - @QtCore.pyqtSlot(name='on_title_lineedit_returnPressed') - @QtCore.pyqtSlot(name='on_xaxis_linedit_returnPressed') - @QtCore.pyqtSlot(name='on_yaxis_linedit_returnPressed') - def labels_changed(self): - label = {self.title_lineedit: 'title', self.xaxis_linedit: 'x', self.yaxis_linedit: 'y'}[self.sender()] - self.set_label(**{label: self.sender().text()}) + @QtCore.pyqtSlot(str, name='on_title_lineedit_textChanged') + @QtCore.pyqtSlot(str, name='on_xaxis_linedit_textChanged') + @QtCore.pyqtSlot(str, name='on_yaxis_linedit_textChanged') + def labels_changed(self, text: str): + label = { + self.title_lineedit: 'title', + self.xaxis_linedit: 'x', + self.yaxis_linedit: 'y', + }[self.sender()] + self.set_label(**{label: text}) - def set_label(self, x=None, y=None, title=None): + def set_label(self, x: str = None, y: str = None, title: str = None): if title is not None: - self.plotItem.setTitle(convert(title, old='tex', new='html'), **{'size': '10pt', 'color': self._fgcolor}) + self.plotItem.setTitle( + convert(title, old='tex', new='html'), + **{'size': '10pt', 'color': self._fgcolor}, + ) if x is not None: - self.plotItem.setLabel('bottom', convert(x, old='tex', new='html'), - **{'font-size': '10pt', 'color': self._fgcolor.name()}) + self.plotItem.setLabel( + 'bottom', + convert(x, old='tex', new='html'), + **{'font-size': '10pt', 'color': self._fgcolor.name()}, + ) if y is not None: - self.plotItem.setLabel('left', convert(y, old='tex', new='html'), - **{'font-size': '10pt', 'color': self._fgcolor.name()}) + self.plotItem.setLabel( + 'left', + convert(y, old='tex', new='html'), + **{'font-size': '10pt', 'color': self._fgcolor.name()}, + ) def set_logmode(self, xmode: bool = None, ymode: bool = None): r = self.ranges @@ -588,7 +605,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow): for i in range(self.listWidget.count()): item = self.listWidget.item(i) - if item.data(QtCore.Qt.UserRole) == sid: + if item.data(QtCore.Qt.ItemDataRole.UserRole) == sid: item.setText(convert(name, old='tex', new='html')) self.listWidget.blockSignals(False) diff --git a/src/gui_qt/io/fcbatchreader.py b/src/gui_qt/io/fcbatchreader.py index 124e2a6..4f678b5 100644 --- a/src/gui_qt/io/fcbatchreader.py +++ b/src/gui_qt/io/fcbatchreader.py @@ -84,11 +84,11 @@ class QFCReader(QtWidgets.QDialog, Ui_FCEval_dialog): if self.region_box.isChecked(): start = None if self.start_lineedit.text(): - start = float(self.start_lineedit.text()) + start = float(self.start_lineedit.text())*1e-6 stop = None if self.stop_lineedit.text(): - stop = float(self.stop_lineedit.text()) + stop = float(self.stop_lineedit.text())*1e-6 region = (start, stop) fc_eval = FCReader(items) diff --git a/src/gui_qt/lib/spinboxes.py b/src/gui_qt/lib/spinboxes.py index dfaced2..5ba041d 100644 --- a/src/gui_qt/lib/spinboxes.py +++ b/src/gui_qt/lib/spinboxes.py @@ -34,9 +34,9 @@ class SciSpinBox(QtWidgets.QDoubleSpinBox): new_value = self._prev_value if new_value != 0.0: - new_value *= 10**(step/19.) + new_value *= 10**(step/99.) else: - new_value = 0.001 + new_value = 0.00001 self.setValue(new_value) self.lineEdit().setText(f'{new_value:.3e}') diff --git a/src/nmreval/models/bds.py b/src/nmreval/models/bds.py index 9ccb922..9f6321d 100644 --- a/src/nmreval/models/bds.py +++ b/src/nmreval/models/bds.py @@ -1,5 +1,6 @@ import numpy as np +from . import PowerLaw from ..distributions import Debye, ColeCole, ColeDavidson, KWW, HavriliakNegami from ..utils.constants import epsilon0 @@ -232,8 +233,8 @@ class DCCondBDS: class DerivativeHavriliakNegami: - name = 'Derivative HN' - type = 'Dielectric Spectroscopy' + name = 'Havriliak-Negami (der.)' + type = 'Dielectric Spectroscopy (derivative)' params = [r'\Delta\epsilon', r'\tau', r'\alpha', r'\gamma'] choices = [ ('x axis', 'xaxis', {'Frequency': 'freq', 'Omega': 'omega'}) @@ -254,8 +255,8 @@ class DerivativeHavriliakNegami: class DerivativeColeCole: - name = 'Derivative CC' - type = 'Dielectric Spectroscopy' + name = 'Cole-Cole (der.)' + type = 'Dielectric Spectroscopy (derivative)' params = [r'\Delta\epsilon', r'\tau', r'\alpha'] bounds = [(0, None), (0, None), (0, 1)] choices = [ @@ -276,8 +277,8 @@ class DerivativeColeCole: class DerivativeColeDavidson: - name = 'Derivative CD' - type = 'Dielectric Spectroscopy' + name = 'Cole-Davidson (der.)' + type = 'Dielectric Spectroscopy (derivative)' params = [r'\Delta\epsilon', r'\tau', r'\gamma'] bounds = [(0, None), (0, None), (0, 1)] choices = [ @@ -295,8 +296,8 @@ class DerivativeColeDavidson: class _DerivativeHNWithHF: - name = 'Derivative (HN + HF wing)' - type = 'Dielectric Spectroscopy' + name = 'HN + HF wing (der.)' + type = 'Dielectric Spectroscopy (derivative)' 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)] choices = [ @@ -325,8 +326,8 @@ class _DerivativeHNWithHF: class DerivativeCCWithHF: - name = 'Derivative (CC + HF wing)' - type = 'Dielectric Spectroscopy' + name = 'CC + HF wing (der.)' + type = 'Dielectric Spectroscopy (derivative)' params = [r'\Delta\epsilon', r'\tau', r'\alpha', r'\tau_{c}', r'\delta'] bounds = [(0, None), (0, None), (0, 1), (0, None), (0, 1)] choices = [ @@ -339,8 +340,8 @@ class DerivativeCCWithHF: class DerivativeCDWithHF: - name = 'Derivative (CD + HF wing)' - type = 'Dielectric Spectroscopy' + name = 'CD + HF wing (der.)' + type = 'Dielectric Spectroscopy (derivative)' params = [r'\Delta\epsilon', r'\tau', r'\gamma', r'\tau_{c}', r'\delta'] bounds = [(0, None), (0, None), (0, 1), (0, None), (0, 1)] choices = [ @@ -352,6 +353,22 @@ class DerivativeCDWithHF: return _DerivativeHNWithHF.func(x, deps, tau, 1, gamma, tauc, delta, xaxis=xaxis) +class PowerLawBDSDer: + name = 'Power Law' + type = 'Dielectric Spectroscopy (derivative)' + equation = r'A * \omega^{n}' + params = ['A', 'n'] + bounds = [(None, None), (None, None)] + choices = [ + ('x axis', 'xaxis', {'Frequency': 'freq', 'Omega': 'omega'}) + ] + + @staticmethod + def func(x, a, n, xaxis: str = 'freq'): + _w = _convert_x_to_omega(x, xaxis=xaxis) + return a * _w ** n + + def _convert_x_to_omega(x, xaxis: str = 'freq'): if xaxis not in ['freq', 'omega']: raise ValueError(f'Argument `xaxis` is `freq` or `omega`, given is {xaxis!r}') diff --git a/src/resources/_ui/fitresult.ui b/src/resources/_ui/fitresult.ui index d821e3c..142a794 100644 --- a/src/resources/_ui/fitresult.ui +++ b/src/resources/_ui/fitresult.ui @@ -31,13 +31,13 @@