diff --git a/doc/Makefile b/doc/Makefile index 6cfe3d2..407565f 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -5,8 +5,8 @@ # from the environment for the first two. SPHINXOPTS ?= SPHINXBUILD ?= /autohome/dominik/miniconda3/bin/sphinx-build -SOURCEDIR = /autohome/dominik/nmreval/docs/source -BUILDDIR = /autohome/dominik/nmreval/docs/build +SOURCEDIR = /autohome/dominik/nmreval/doc/source +BUILDDIR = /autohome/dominik/nmreval/doc/_build # Put it first so that "make" without argument is like "make help". help: diff --git a/doc/examples/nmr/plot_RelaxationEvaluation.py b/doc/examples/nmr/plot_RelaxationEvaluation.py index 964c753..d9252d8 100644 --- a/doc/examples/nmr/plot_RelaxationEvaluation.py +++ b/doc/examples/nmr/plot_RelaxationEvaluation.py @@ -1,12 +1,19 @@ """ -======================= -Spin-lattice relaxation -======================= +========== +T1 minimum +========== -Example for +``RelaxationEvaluation`` is used to get width parameter from a T1 minimum. +As a subclass of ``Relaxation`` it can also be used to calculate Relaxation times. +The basic steps are: + +* Determine a T1 minimum with `nmreval.nmr.RelaxationEvaluation.calculate_t1_min` +* Calculate width parameter of a spectral density/coupling constants/... with + ``RelaxationEvaluation.get_increase`` +* Calculate correlation times from these values with ``RelaxationEvaluation.correlation_from_t1`` """ import numpy as np -from matplotlib import pyplot as plt +import matplotlib.pyplot as plt from nmreval.distributions import ColeDavidson from nmreval.nmr import Relaxation, RelaxationEvaluation @@ -20,7 +27,7 @@ temperature = 1000/inv_temp # spectral density parameter ea = 0.45 tau = 1e-21 * np.exp(ea / kB / temperature) -gamma_cd = 0.1 +gamma_cd = 0.4 # interaction parameter omega = 2*np.pi*46e6 @@ -28,40 +35,57 @@ delta = 120e3 eta = 0 r = Relaxation() -r.set_distribution(ColeDavidson) # the only parameter that has to be set beforehand +r.set_distribution(ColeDavidson) # the only parameter that set beforehand t1_values = r.t1(omega, tau, gamma_cd, mode='bpp', prefactor=Quadrupolar.relax(delta, eta)) # add noise -rng = np.random.default_rng(123456789) +rng = np.random.default_rng() noisy = (rng.random(t1_values.size)-0.5) * 0.5 * t1_values + t1_values -# set parameter and data +ax_t1 = plt.figure().add_subplot() +ax_t1.semilogy(inv_temp, t1_values, label='Calculated T1') +ax_t1.semilogy(inv_temp, noisy, 'o', label='Noise') +ax_t1.legend() + +plt.show() + + +# Actual evaluation starts here +# setting necessary parameter r_eval = RelaxationEvaluation() r_eval.set_distribution(ColeDavidson) r_eval.set_coupling(Quadrupolar, (delta, eta)) -r_eval.data(temperature, noisy) +r_eval.set_data(temperature, noisy) r_eval.omega = omega +# Find a T1 minumum t1_min_data, _ = r_eval.calculate_t1_min() # second argument is None t1_min_inter, line = r_eval.calculate_t1_min(interpolate=1, trange=(160, 195), use_log=True) -fig, ax = plt.subplots() -ax.semilogy(1000/t1_min_data[0], t1_min_data[1], 'rx', label='Data minimum') -ax.semilogy(1000/t1_min_inter[0], t1_min_inter[1], 'r+', label='Parabola') -ax.semilogy(1000/line[0], line[1]) +ax_min = plt.figure().add_subplot() +ax_min.semilogy(inv_temp, noisy, 'o', label='Data') +ax_min.semilogy(1000/line[0], line[1], '--') +ax_min.semilogy(1000/t1_min_data[0], t1_min_data[1], 'C2X',label='Data minimum') +ax_min.semilogy(1000/t1_min_inter[0], t1_min_inter[1], 'C3P',label='Parabola') +ax_min.set_xlim(4.5, 7) +ax_min.set_ylim(1e-3, 1e-1) +ax_min.legend() +# Vary the first (and for Cole-Davidson, only) parameter of the spectral density found_gamma, found_height = r_eval.get_increase(t1_min_inter[1], idx=0, mode='distribution') -print(found_gamma) - -plt.axhline(found_height) +print(f'Minimum at {found_height} for {found_gamma}; input is {gamma_cd}') plt.show() -#%% -# Now we found temperature and height of the minimum we can calculate the correlation time +################################################################################## +# Calculation of correlation times uses previously parameter for spectral density +# and prefactor -plt.semilogy(1000/temperature, tau) -tau_from_t1, opts = r_eval.correlation_from_t1() -print(opts) -plt.semilogy(1000/tau_from_t1[:, 0], tau_from_t1[:, 1], 'o') +tau_from_t1, opts = r_eval.correlation_from_t1(mode='mean') +print(f'Used options: {opts}') + +ax_tau = plt.figure().add_subplot() +ax_tau.semilogy(inv_temp, tau*gamma_cd, label='Original input') +ax_tau.semilogy(1000/tau_from_t1[:, 0], tau_from_t1[:, 1], 'o', label='Calculated') +ax_tau.legend() plt.show() diff --git a/doc/source/gallery/index.rst b/doc/source/gallery/index.rst deleted file mode 100644 index 37d2514..0000000 --- a/doc/source/gallery/index.rst +++ /dev/null @@ -1,179 +0,0 @@ -:orphan: - - - -.. _sphx_glr_gallery: - -.. examples-index: - -.. _gallery: - -======== -Examples -======== - -This page contains example plots. Click on any image to see the full image and source code. - - -.. raw:: html - -
- - - -.. _sphx_glr_gallery_distribution: - - .. _distribution_examples: - -.. _distribution-examples-index: - -Distribution of correlation times -================================= - - - -.. raw:: html - -
- -.. only:: html - - .. figure:: /gallery/distribution/images/thumb/sphx_glr_plot_KWW_thumb.png - :alt: Kohlrausch-Williams-Watts - - :ref:`sphx_glr_gallery_distribution_plot_KWW.py` - -.. raw:: html - -
- - -.. toctree:: - :hidden: - - /gallery/distribution/plot_KWW - -.. raw:: html - -
- -.. only:: html - - .. figure:: /gallery/distribution/images/thumb/sphx_glr_plot_ColeCole_thumb.png - :alt: Cole-Cole - - :ref:`sphx_glr_gallery_distribution_plot_ColeCole.py` - -.. raw:: html - -
- - -.. toctree:: - :hidden: - - /gallery/distribution/plot_ColeCole - -.. raw:: html - -
- -.. only:: html - - .. figure:: /gallery/distribution/images/thumb/sphx_glr_plot_LogGaussian_thumb.png - :alt: Log-Gaussian - - :ref:`sphx_glr_gallery_distribution_plot_LogGaussian.py` - -.. raw:: html - -
- - -.. toctree:: - :hidden: - - /gallery/distribution/plot_LogGaussian - -.. raw:: html - -
- -.. only:: html - - .. figure:: /gallery/distribution/images/thumb/sphx_glr_plot_ColeDavidson_thumb.png - :alt: Cole-Davidson - - :ref:`sphx_glr_gallery_distribution_plot_ColeDavidson.py` - -.. raw:: html - -
- - -.. toctree:: - :hidden: - - /gallery/distribution/plot_ColeDavidson - -.. raw:: html - -
- -.. only:: html - - .. figure:: /gallery/distribution/images/thumb/sphx_glr_plot_HavriliakNegami_thumb.png - :alt: Havriliak-Negami - - :ref:`sphx_glr_gallery_distribution_plot_HavriliakNegami.py` - -.. raw:: html - -
- - -.. toctree:: - :hidden: - - /gallery/distribution/plot_HavriliakNegami -.. raw:: html - -
- - - -.. _sphx_glr_gallery_nmr: - -.. _nmr_examples: - -.. _nmr-examples-index: - -NMR specifics -============= - - - -.. raw:: html - -
- -.. only:: html - - .. figure:: /gallery/nmr/images/thumb/sphx_glr_plot_RelaxationEvaluation_thumb.png - :alt: Spin-lattice relaxation - - :ref:`sphx_glr_gallery_nmr_plot_RelaxationEvaluation.py` - -.. raw:: html - -
- - -.. toctree:: - :hidden: - - /gallery/nmr/plot_RelaxationEvaluation -.. raw:: html - -
- diff --git a/doc/source/gallery/searchindex.bak b/doc/source/gallery/searchindex.bak deleted file mode 100644 index 38a4678..0000000 --- a/doc/source/gallery/searchindex.bak +++ /dev/null @@ -1,3 +0,0 @@ -'/autohome/dominik/nmreval/doc/_build/html/index.html', (0, 6969) -'/autohome/dominik/nmreval/doc/_build/html/_static/documentation_options.js', (7168, 364) -'/autohome/dominik/nmreval/doc/_build/html/searchindex.js', (7680, 29280) diff --git a/doc/source/gallery/searchindex.dat b/doc/source/gallery/searchindex.dat deleted file mode 100644 index 2173478..0000000 Binary files a/doc/source/gallery/searchindex.dat and /dev/null differ diff --git a/doc/source/gallery/searchindex.dir b/doc/source/gallery/searchindex.dir deleted file mode 100644 index 38a4678..0000000 --- a/doc/source/gallery/searchindex.dir +++ /dev/null @@ -1,3 +0,0 @@ -'/autohome/dominik/nmreval/doc/_build/html/index.html', (0, 6969) -'/autohome/dominik/nmreval/doc/_build/html/_static/documentation_options.js', (7168, 364) -'/autohome/dominik/nmreval/doc/_build/html/searchindex.js', (7680, 29280) diff --git a/nmreval/gui_qt/data/container.py b/nmreval/gui_qt/data/container.py index fe18bff..40b2267 100644 --- a/nmreval/gui_qt/data/container.py +++ b/nmreval/gui_qt/data/container.py @@ -485,7 +485,7 @@ class PointContainer(ExperimentContainer): } if sym_kwargs['symbol'] is None and line_kwargs['style'] is None: - if len(self._data) > 1000: + if len(self._data) > 500: line_kwargs['style'] = LineStyle.Solid sym_kwargs['symbol'] = SymbolStyle.No else: diff --git a/nmreval/gui_qt/data/conversion.py b/nmreval/gui_qt/data/conversion.py index dbcb0b8..2f8392b 100644 --- a/nmreval/gui_qt/data/conversion.py +++ b/nmreval/gui_qt/data/conversion.py @@ -138,8 +138,6 @@ class ConversionDialog(QtWidgets.QDialog, Ui_Dialog): src_sets.append((set_id_real, set_id_imag, graph_id, type_idx)) - print(src_sets) - self.convertSets.emit(src_sets) return src_sets diff --git a/nmreval/gui_qt/fit/fit_parameter.py b/nmreval/gui_qt/fit/fit_parameter.py index 8bd125b..e8f03c0 100644 --- a/nmreval/gui_qt/fit/fit_parameter.py +++ b/nmreval/gui_qt/fit/fit_parameter.py @@ -124,7 +124,7 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit): self.data_parameter[idx].blockSignals(False) @QtCore.pyqtSlot(str, object) - def change_global_choice(self, argname, value): + def change_global_choice(self, _, value): idx = self.global_parameter.index(self.sender()) self.glob_values[idx] = value if self.data_values[self.comboBox.currentData()][idx] is None: @@ -242,6 +242,8 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit): else: if p_i is None: kw_p.update(g.value) + elif isinstance(p_i, dict): + kw_p.update(p_i) else: kw_p[g.argname] = p_i diff --git a/nmreval/gui_qt/graphs/graphwindow.py b/nmreval/gui_qt/graphs/graphwindow.py index 644f67e..c4da8ed 100644 --- a/nmreval/gui_qt/graphs/graphwindow.py +++ b/nmreval/gui_qt/graphs/graphwindow.py @@ -64,7 +64,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow): # reconnect "Export..." in context menu to our function self.scene.contextMenu[0].disconnect() - self.scene.contextMenu[0].triggered.connect(self.export) + self.scene.contextMenu[0].triggered.connect(self.export_dialog) def _init_gui(self): self.setWindowTitle('Graph ' + str(next(QGraphWindow.counter))) @@ -515,7 +515,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow): (item in self.graphic.items() or other_item in self.graphic.items()): self.legend.addItem(item, convert(item.opts.get('name', ''), old='tex', new='html')) - def export(self): + def export_dialog(self): filters = 'All files (*.*);;AGR (*.agr);;SVG (*.svg);;PDF (*.pdf)' for imgformat in QtGui.QImageWriter.supportedImageFormats(): str_format = imgformat.data().decode('utf-8') @@ -524,6 +524,9 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow): outfile, _ = QtWidgets.QFileDialog.getSaveFileName(self, caption='Export graphic', filter=filters, options=QtWidgets.QFileDialog.DontConfirmOverwrite) if outfile: + self.export(outfile) + + def export(self, outfile: str): _, suffix = os.path.splitext(outfile) if suffix == '': QtWidgets.QMessageBox.warning(self, 'No file extension', @@ -566,7 +569,6 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow): from ..io.exporters import PDFPrintExporter PDFPrintExporter(self.graphic).export(outfile) - elif suffix == '.svg': from pyqtgraph.exporters import SVGExporter SVGExporter(self.scene).export(outfile) @@ -591,8 +593,6 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow): if item_dic: dic['items'].append(item_dic) - print(dic) - return dic def get_state(self) -> dict: diff --git a/nmreval/gui_qt/io/filedialog.py b/nmreval/gui_qt/io/filedialog.py index 30306b6..9f1f532 100644 --- a/nmreval/gui_qt/io/filedialog.py +++ b/nmreval/gui_qt/io/filedialog.py @@ -1,3 +1,7 @@ +from __future__ import annotations + +import pathlib + from ..Qt import QtWidgets, QtCore @@ -74,15 +78,45 @@ class SaveDirectoryDialog(_FileDialog): self.setOption(QtWidgets.QFileDialog.DontConfirmOverwrite, False) self.setAcceptMode(QtWidgets.QFileDialog.AcceptSave) + lay = self.layout() + self.label = QtWidgets.QLabel(self) self.label.setTextFormat(QtCore.Qt.RichText) self.label.setText('Use <label> as placeholder in filename. (e.g. t1_<label>.dat)') - self.layout().addWidget(self.label, self.layout().rowCount(), 0, 1, self.layout().columnCount()) + lay.addWidget(self.label, lay.rowCount(), 0, 1, lay.columnCount()) + + line = QtWidgets.QFrame(self) + line.setFrameShape(line.HLine) + line.setFrameShadow(line.Sunken) + lay.addWidget(line, lay.rowCount(), 0, 1, lay.columnCount()) + + h_layout = QtWidgets.QHBoxLayout() + h_layout.setContentsMargins(0, 0, 0, 0) + h_layout.setSpacing(3) self.checkBox = QtWidgets.QCheckBox(self) self.checkBox.setChecked(True) - self.checkBox.setText('Replace spaces with underscore') - self.layout().addWidget(self.checkBox, self.layout().rowCount(), 0, 1, self.layout().columnCount()) + self.checkBox.setText('Replace spaces with _') + h_layout.addWidget(self.checkBox) + + self.agr_cb = QtWidgets.QCheckBox(self) + self.agr_cb.setChecked(True) + self.agr_cb.setText('Save graph as Grace file') + h_layout.addWidget(self.agr_cb) + + self.fit_cb = QtWidgets.QCheckBox(self) + self.fit_cb.setChecked(True) + self.fit_cb.setText('Save fit parameter') + h_layout.addWidget(self.fit_cb) + + lay.addLayout(h_layout, lay.rowCount(), 0, 1, lay.columnCount()) self.setWindowTitle('Save') - self.setNameFilters(['All files (*.*)', 'Session file (*.nmr)', 'Text file (*.dat)', 'HDF file (*.h5)', 'Grace files (*.agr)']) + self.setNameFilters(['All files (*.*)', 'Session file (*.nmr)', 'Text file (*.dat)', + 'HDF file (*.h5)', 'Grace files (*.agr)']) + + def save_file(self) -> pathlib.Path | None: + outfile = self.selectedFiles() + if outfile: + return pathlib.Path(outfile[0]) + return diff --git a/nmreval/gui_qt/lib/forms.py b/nmreval/gui_qt/lib/forms.py index 00e9233..9c1faaf 100644 --- a/nmreval/gui_qt/lib/forms.py +++ b/nmreval/gui_qt/lib/forms.py @@ -193,6 +193,8 @@ class SelectionWidget(QtWidgets.QWidget): @value.setter def value(self, val): + if isinstance(val, dict): + val = list(val.values())[0] key = [k for k, v in self.options.items() if v == val][0] self.comboBox.setCurrentIndex(self.comboBox.findText(key)) diff --git a/nmreval/gui_qt/lib/pg_objects.py b/nmreval/gui_qt/lib/pg_objects.py index 7fa9851..fb82b8d 100644 --- a/nmreval/gui_qt/lib/pg_objects.py +++ b/nmreval/gui_qt/lib/pg_objects.py @@ -341,7 +341,7 @@ class PlotItem(PlotDataItem): if opts['symbol'] is None: item_dic['symbol'] = SymbolStyle.No - item_dic['symbolcolor'] = Colors.Black + item_dic['symbolcolor'] = None else: item_dic['symbol'] = SymbolStyle.from_str(opts['symbol']) item_dic['symbolcolor'] = opts['symbolcolor'] @@ -354,9 +354,16 @@ class PlotItem(PlotDataItem): item_dic['linewidth'] = pen.widthF() else: item_dic['linestyle'] = LineStyle.No - item_dic['linecolor'] = item_dic['symbolcolor'] + item_dic['linecolor'] = None item_dic['linewidth'] = 0.0 + if item_dic['linecolor'] is None and item_dic['symbolcolor'] is None: + item_dic['symbolcolor'] = Colors.Black.rgb() + elif item_dic['linecolor'] is None: + item_dic['linecolor'] = item_dic['symbolcolor'] + elif item_dic['symbolcolor'] is None: + item_dic['symbolcolor'] = item_dic['linecolor'] + return item_dic diff --git a/nmreval/gui_qt/main/mainwindow.py b/nmreval/gui_qt/main/mainwindow.py index d5694be..2a3e899 100644 --- a/nmreval/gui_qt/main/mainwindow.py +++ b/nmreval/gui_qt/main/mainwindow.py @@ -1,4 +1,5 @@ import pathlib +import re from pathlib import Path from typing import List, Tuple @@ -249,11 +250,20 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): mode = save_dialog.exec() if mode == QtWidgets.QDialog.Accepted: - path = save_dialog.selectedFiles() + savefile = save_dialog.save_file() selected_filter = save_dialog.selectedNameFilter() - if path: - self.management.save(path[0], selected_filter) + if savefile is not None: + use_underscore = save_dialog.checkBox.isChecked() + self.management.save(savefile, selected_filter, strip_spaces=use_underscore) + + param_outfile = re.sub('[_\s-]?