From 5120c9d57cc2b0c8b4755c218e25191b019f104c Mon Sep 17 00:00:00 2001 From: dominik Date: Wed, 10 Aug 2022 20:00:10 +0200 Subject: [PATCH] delete graphs should be less leaky; BDS: added CD/CC+HF --- nmreval/fit/_meta.py | 4 +- nmreval/fit/result.py | 16 ++++---- nmreval/gui_qt/data/valueeditwidget.py | 20 ++++++++-- nmreval/gui_qt/fit/fit_parameter.py | 3 +- nmreval/gui_qt/fit/result.py | 4 +- nmreval/gui_qt/graphs/graphwindow.py | 26 ++++++------- nmreval/gui_qt/main/mainwindow.py | 33 ++++++++++++---- nmreval/gui_qt/main/management.py | 10 +++-- nmreval/models/bds.py | 52 ++++++++++++++++++++++++++ nmreval/models/relaxation.py | 6 +-- 10 files changed, 130 insertions(+), 44 deletions(-) diff --git a/nmreval/fit/_meta.py b/nmreval/fit/_meta.py index 8c8c0ab..68d92bd 100644 --- a/nmreval/fit/_meta.py +++ b/nmreval/fit/_meta.py @@ -100,7 +100,9 @@ class MultiModel: if v.default is not Parameter.empty} for k, v in temp_dic.items(): - key_ = f'{k}_{idx}' + key_ = k + if k != 'complex_mode': + key_ = f'{k}_{idx}' kw_dict[key_] = v self.fun_kwargs[key_] = v self._ext_int_kw[key_] = k diff --git a/nmreval/fit/result.py b/nmreval/fit/result.py index 8a5a589..cbcae65 100644 --- a/nmreval/fit/result.py +++ b/nmreval/fit/result.py @@ -1,6 +1,9 @@ -import pathlib +from __future__ import annotations + import re from collections import OrderedDict +from pathlib import Path +from typing import Any import numpy as np from scipy.stats import f as fdist @@ -14,7 +17,7 @@ from ..utils.text import convert class FitResultCreator: @staticmethod - def make_from_session(x_orig, y_orig, idx, kwargs) -> (dict, list): + def make_from_session(x_orig: np.ndarray, y_orig: np.ndarray, idx: int, kwargs: dict[Any]) -> (dict, list): params = OrderedDict() for key, pbest, err in zip(kwargs['pnames'], kwargs['parameter'], kwargs['error']): @@ -191,9 +194,6 @@ class FitResult(Points): def model_name(self): return self._model_name - def __len__(self): - return len(self.parameter) - def __repr__(self): try: return 'Fit: ' + self.name @@ -265,7 +265,7 @@ class FitResult(Points): return correlations - def savetxt(self, fname, err=True): + def savetxt(self, fname: str | Path, err: bool = True): header = self.name + '\n' for pval in self.parameter.values(): header += '{}\t{}\t{}\n'.format(convert(pval.name, old='tex', new='str'), pval.value, pval.error) @@ -279,8 +279,8 @@ class FitResult(Points): else: np.savetxt(fname, np.c_[self.x, self.y], header=header) - def save_parameter(self, fname: str, label: str = None, overwrite: bool = False): - path = pathlib.Path(fname) + def save_parameter(self, fname: str | Path, label: str = None, overwrite: bool = False): + path = Path(fname) if not path.is_file(): overwrite = True diff --git a/nmreval/gui_qt/data/valueeditwidget.py b/nmreval/gui_qt/data/valueeditwidget.py index ae13ba9..fc39a73 100644 --- a/nmreval/gui_qt/data/valueeditwidget.py +++ b/nmreval/gui_qt/data/valueeditwidget.py @@ -22,7 +22,7 @@ class ValueEditWidget(QtWidgets.QWidget, Ui_MaskDialog): self.setupUi(self) - self.graph_shown = None + self.connected_figure = None self.shown_set = None self.items = {} @@ -49,7 +49,7 @@ class ValueEditWidget(QtWidgets.QWidget, Ui_MaskDialog): self.graph_combobox.addItem(k[1], userData=k[0]) self.graph_combobox.blockSignals(False) - idx = self.graph_combobox.findData(self.graph_shown) + idx = self.graph_combobox.findData(self.connected_figure) if idx == -1: idx = 0 @@ -66,7 +66,7 @@ class ValueEditWidget(QtWidgets.QWidget, Ui_MaskDialog): self.set_combobox.blockSignals(True) self.set_combobox.clear() - self.graph_shown = self.graph_combobox.currentData() + self.connected_figure = self.graph_combobox.currentData() if self.items: for sid, name in self.items[(self.graph_combobox.currentData(), self.graph_combobox.currentText())]: @@ -91,6 +91,16 @@ class ValueEditWidget(QtWidgets.QWidget, Ui_MaskDialog): self.model.loadData(data, mask) self.spinBox.setMaximum(self.model.rowCount()) + def remove_graph(self): + # remove everything + self.connected_figure = None + self.items = {} + self.selection_model.clear() + while self.model.rowCount(): + self.model.removeRow(0) + self.graph_combobox.clear() + self.set_combobox.clear() + def ctx(self, pos: QtCore.QPoint): idx = self.tableView.indexAt(pos) if not idx.isValid(): @@ -185,7 +195,7 @@ class ValueEditWidget(QtWidgets.QWidget, Ui_MaskDialog): except ValueError: yvals.append(complex(self.model.data(idx.sibling(idx.row(), 1))).real) - self.values_selected.emit(self.graph_shown, xvals, yvals) + self.values_selected.emit(self.connected_figure, xvals, yvals) @QtCore.pyqtSlot(name='on_toolButton_clicked') def goto(self): @@ -323,6 +333,8 @@ class ValueModel(QtCore.QAbstractTableModel): self.mask.pop(pos) self.endRemoveRows() + self.total_rows -= rows + return True def addRows(self, num=1): diff --git a/nmreval/gui_qt/fit/fit_parameter.py b/nmreval/gui_qt/fit/fit_parameter.py index 65992b9..f2d4111 100644 --- a/nmreval/gui_qt/fit/fit_parameter.py +++ b/nmreval/gui_qt/fit/fit_parameter.py @@ -270,7 +270,8 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit): else: new_param = self.data_values[set_id] - for i in range(len(new_param)): + min_len = min(len(new_param), len(parameter)) + for i in range(min_len): new_param[i] = parameter[i] self.change_data(self.comboBox.currentIndex()) diff --git a/nmreval/gui_qt/fit/result.py b/nmreval/gui_qt/fit/result.py index 08e727d..fc13198 100644 --- a/nmreval/gui_qt/fit/result.py +++ b/nmreval/gui_qt/fit/result.py @@ -181,8 +181,8 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog): for pi, pj, corr, pcorr in c: cnt = self.corr_tableWidget.rowCount() self.corr_tableWidget.insertRow(cnt) - self.corr_tableWidget.setItem(cnt, 0, QtWidgets.QTableWidgetItem(convert(pi, old='tex', new='html'))) - self.corr_tableWidget.setItem(cnt, 1, QtWidgets.QTableWidgetItem(convert(pj, old='tex', new='html'))) + self.corr_tableWidget.setItem(cnt, 0, QtWidgets.QTableWidgetItem(convert(pi, old='tex', new='str'))) + self.corr_tableWidget.setItem(cnt, 1, QtWidgets.QTableWidgetItem(convert(pj, old='tex', new='str'))) for i, val in enumerate([corr, pcorr]): if isnan(val): diff --git a/nmreval/gui_qt/graphs/graphwindow.py b/nmreval/gui_qt/graphs/graphwindow.py index fd4b6f9..0004b93 100644 --- a/nmreval/gui_qt/graphs/graphwindow.py +++ b/nmreval/gui_qt/graphs/graphwindow.py @@ -146,7 +146,8 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow): toplevel = len(self.sets) self.sets.append(n) - real_plot.setZValue(2*toplevel+1) + if real_plot: + real_plot.setZValue(2*toplevel+1) if imag_plot: imag_plot.setZValue(2*toplevel+1) if err_plot: @@ -221,20 +222,15 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow): if a not in self.active: self.active.append(a) - if self.imag_button.isChecked(): - item = self.imag_plots[a] - if (item is not None) and (item not in self.graphic.items()): - self.graphic.addItem(item) - - if self.real_button.isChecked(): - item = self.real_plots[a] - if (item is not None) and item not in self.graphic.items(): - self.graphic.addItem(item) - - if self.error_button.isChecked(): - item = self.error_plots[a] - if (item is not None) and (item not in self.graphic.items()): - self.graphic.addItem(item) + for (bttn, plot_dic) in [ + (self.real_button, self.real_plots), + (self.imag_button, self.imag_plots), + (self.error_button, self.error_plots), + ]: + if bttn.isChecked(): + item = plot_dic[a] + if (item is not None) and (item not in self.graphic.items()): + self.graphic.addItem(item) self.show_legend() diff --git a/nmreval/gui_qt/main/mainwindow.py b/nmreval/gui_qt/main/mainwindow.py index 4aa4f97..dc0a950 100644 --- a/nmreval/gui_qt/main/mainwindow.py +++ b/nmreval/gui_qt/main/mainwindow.py @@ -319,11 +319,15 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): @QtCore.pyqtSlot(str) def remove_graph(self, gid: str): self.datawidget.remove_item(gid) + val_figure = self.valuewidget.connected_figure + self.valuewidget.remove_graph() + w = None for w in self.area.subWindowList(): wdgt = w.widget() if wdgt.id == gid: wdgt.disconnect() + wdgt.scene.disconnect() if wdgt == self.current_graph_widget: if self.ptsselectwidget.connected_figure == gid: self.ptsselectwidget.connected_figure = None @@ -338,20 +342,35 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): for item in self.fit_dialog.preview_lines: self.current_graph_widget.remove_external(item) - if self.valuewidget.graph_shown == gid: + if val_figure == gid: self.tabWidget.setCurrentIndex(0) + self.current_graph_widget.enable_picking(False) + self.current_graph_widget = None self.management.current_graph = '' self.current_plotitem = None + wdgt.setParent(None) + try: + wdgt.deleteLater() + except AttributeError: + pass + break + if w is not None: + self.area.removeSubWindow(w) w.close() + try: + w.deleteLater() + except AttributeError: + pass if self.current_graph_widget is None: - self.area.activateNextSubWindow() + if self.area.subWindowList(): + self.area.activateNextSubWindow() @QtCore.pyqtSlot(str) def set_graph(self, key: str): @@ -500,11 +519,11 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): if onoff: # Values self.valuewidget(self.management.graphs.tree()) self.valuewidget.connected_figure = self.management.current_graph - if self.valuewidget.graph_shown is not None: - self.management.graphs[self.valuewidget.graph_shown].add_external(self._values_plot) + if self.valuewidget.connected_figure is not None: + self.management.graphs[self.valuewidget.connected_figure].add_external(self._values_plot) else: - if self.valuewidget.graph_shown is not None: - self.management.graphs[self.valuewidget.graph_shown].remove_external(self._values_plot) + if self.valuewidget.connected_figure is not None: + self.management.graphs[self.valuewidget.connected_figure].remove_external(self._values_plot) def _select_integralwidget(self, onoff: bool, pick_required: bool): if self.current_graph_widget is None: @@ -736,7 +755,7 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): def plot_selected_values(self, gid: str, x: list, y: list): self._values_plot.setData(x=x, y=y) - if gid != self.valuewidget.connected_figure: + if gid != self.valuewidget.connected_figure and self.valuewidget.connected_figure is not None: self.management.graphs[self.valuewidget.connected_figure].remove_external(self._values_plot) self.management.graphs[gid].add_external(self._values_plot) self.valuewidget.connected_figure = gid diff --git a/nmreval/gui_qt/main/management.py b/nmreval/gui_qt/main/management.py index 2f96332..fc0f9a5 100644 --- a/nmreval/gui_qt/main/management.py +++ b/nmreval/gui_qt/main/management.py @@ -393,12 +393,12 @@ class UpperManagement(QtCore.QObject): for set_id, set_params in model_p['parameter'].items(): data_i = self.data[set_id] - if we == 'Deltay': + if we.lower() == 'deltay': we = data_i.y_err**2 if m_complex is None or m_complex == 1: _y = data_i.y.real - elif m_complex == 2 and np.iscomplexobj(self.data[set_id].y): + elif m_complex == 2 and np.iscomplexobj(data_i.y): _y = data_i.y.imag else: _y = data_i.y @@ -507,7 +507,9 @@ class UpperManagement(QtCore.QObject): accepted.append(fit) + data_name = f' ({data_k.name})' if show_fit: + fit.name += data_name f_id = self.add(fit, color=color, src=k) f_id_list.append(f_id) @@ -516,9 +518,11 @@ class UpperManagement(QtCore.QObject): gid = data_k.graph if k in parts and show_fit: - for subfunc, col in zip(parts[k], TUColorsC): + color_scheme = available_cycles['colorblind'] + for subfunc, col in zip(parts[k], cycle(color_scheme)): subfunc.value = data_k.value subfunc.group = data_k.group + subfunc.name += data_name sub_f_id = self.add(subfunc, color=col, linestyle=LineStyle.Dashed, symbol=SymbolStyle.No) f_id_list.append(sub_f_id) diff --git a/nmreval/models/bds.py b/nmreval/models/bds.py index 1bc25ea..3fefd0a 100644 --- a/nmreval/models/bds.py +++ b/nmreval/models/bds.py @@ -88,6 +88,58 @@ class EpsInfty: return ret_val +class CCwithHFW: + name = 'CC + HF wing' + type = 'Dielectric Spectroscopy' + equation = r'\Delta\epsilon CC(\omega, \tau, \alpha) / CD(\omega, \tau_{c}, \beta-\delta)' + params = [r'\Delta\epsilon', r'\tau', r'\alpha', r'\tau_{c}', '\delta'] + bounds = [(0, None), (0, None), (0, 1), (0, None), (0, 1)] + iscomplex = True + + @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') + + +class CDwithHFW: + name = 'CD + HF wing' + type = 'Dielectric Spectroscopy' + equation = r'\Delta\epsilon CD(\omega, \tau, \gamma) / CD(\omega, \tau_{c}, \gamma-\delta)' + params = [r'\Delta\epsilon', r'\tau', r'\gamma', r'\tau_{c}', '\delta'] + bounds = [(0, None), (0, None), (0, 1), (0, None), (0, 1)] + iscomplex = True + + @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') + + class PowerLawBDS: name = 'Power Law' type = 'Dielectric Spectroscopy' diff --git a/nmreval/models/relaxation.py b/nmreval/models/relaxation.py index ca4230b..f92e2fa 100644 --- a/nmreval/models/relaxation.py +++ b/nmreval/models/relaxation.py @@ -61,7 +61,7 @@ class SatRec: equation = r'\DeltaM(1-exp(-(x/T_{1})^{\beta})) + M_{0}' params = [r'\DeltaM', 'T_{1}', r'\beta', 'M_{0}'] choices = [('Type', 'is_inv', {'Saturation': False, 'Inversion': True})] - bounds = [(0, None), (None, None), (0, 1), (0, None)] + bounds = [(0, None), (0, None), (0, 1), (None, None)] @staticmethod def func(x, dm, t1, beta, m0, is_inv: bool = True): @@ -77,7 +77,7 @@ class TwoSatRecAbsolute: equation = r'M_{0} + \Sigma \DeltaM_{i}(1-exp(-(x/T_{1,i})^{\beta_{i}}))' params = [r'\DeltaM_{1}', 'T_{1,1}', r'\beta_{1}', r'\DeltaM_{2}', 'T_{1,2}', r'\beta_{2}', 'M_{0}'] choices = [('Type', 'is_inv', {'Saturation': False, 'Inversion': True})] - bounds = [(None, None), (0, None), (0, 1), (None, None), (0, None), (0, 1), (None, None)] + bounds = [(0, None), (0, None), (0, 1), (0, None), (0, None), (0, 1), (None, None)] @staticmethod def func(x, dm1, t11, beta1, dm2, t12, beta2, m0, is_inv: bool = False): @@ -94,7 +94,7 @@ class TwoSatRecRelative: r'(1-R)(1-exp(-(x/T_{1,2})^{\beta_{2}}))]' params = [r'\DeltaM', 'M_{0}', 'T_{1,1}', r'\beta_{1}', 'T_{1,2}', r'\beta_{2}', 'R'] choices = [('Type', 'kind', {'Saturation': 'sat', 'Inversion': 'inv'})] - bounds = [(None, None), (0, None), (0, 1), (None, None), (0, None), (0, 1), (None, None)] + bounds = [(0, None), (None, None), (0, None), (0, 1), (0, None), (0, 1), (0, 1)] @staticmethod def func(x, dm, m0, t11, beta1, t12, beta2, ratio, kind='sat'):