fixes in grace export and work on fit dialog

This commit is contained in:
dominik 2022-04-12 18:45:30 +02:00
parent 74dacd0180
commit 4a18965580
26 changed files with 355 additions and 499 deletions

View File

@ -1,27 +0,0 @@
{{ objname | escape | underline}}
Import as {{ fullname }}
.. currentmodule:: {{ module }}
.. autoclass:: {{ objname }}
{% block methods %}
{% if methods %}
.. rubric:: {{ _('Methods') }}
.. autosummary::
:toctree:
{% for item in methods %}
{%- if not item.startswith('_') %}
~{{ name }}.{{ item }}
{%- endif %}
{%- endfor %}
{% endif %}
{% endblock %}
.. minigallery:: {{module}}.{{objname}}
:add-heading:

View File

@ -1,14 +1,27 @@
:mod:`{{ module | escape }}`.{{ objname }}
{{ underline }}==================
{{ fullname | escape | underline}}
.. currentmodule:: {{ module }}
.. autoclass:: {{ objname }}
:members:
:inherited-members:
.. include:: {{ fullname }}.examples
{% block methods %}
.. raw:: html
{% if methods %}
.. rubric:: {{ _('Methods') }}
<div class="clearer"></div>
{% for item in methods %}
.. automethod:: {{ item }}
{%- endfor %}
{% endif %}
{% endblock %}
{% block attributes %}
{% if attributes %}
.. rubric:: {{ _('Attributes') }}
.. autosummary::
{% for item in attributes %}
~{{ name }}.{{ item }}
{%- endfor %}
{% endif %}
{% endblock %}

View File

@ -1,33 +0,0 @@
{{ fullname | escape | underline}}
.. currentmodule:: {{ module }}
.. autoclass:: {{ objname }}
{%- block methods %}
{% if methods %}
.. rubric:: {{ _('Methods') }}
{%- for item in methods %}
{% if not item.startswith('_') %}
.. automethod:: {{ item }}
{%- endif %}
{%- endfor %}
{%- endif %}
{% endblock %}
{%- block attributes %}
{%- if attributes %}
.. rubric:: {{ _('Attributes') }}
{% for item in attributes %}
.. autoattribute:: {{ item }}
{%- endfor %}
{% endif %}
{% endblock -%}
.. include:: {{ fullname }}.examples
.. raw:: html
<div class="clearer"></div>

View File

@ -1,4 +1,5 @@
.. automodule:: nmreval.distributions
:no-members:
:no-inherited-members:
:no-special-members:
:members:
:inherited-members:
:undoc-members:

View File

@ -1,4 +1,2 @@
.. automodule:: nmreval.models
:no-members:
:no-inherited-members:
:no-special-members:
:members:

View File

@ -39,7 +39,6 @@ version = release
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.autosummary',
'sphinx.ext.inheritance_diagram',
'sphinx.ext.napoleon',
'sphinx.ext.viewcode',
'sphinx.ext.intersphinx',
@ -55,14 +54,13 @@ intersphinx_mapping = {
'matplotlib': ('https://matplotlib.org/stable', None),
}
# autodoc options
# # autodoc options
autodoc_typehints = 'none'
autodoc_class_signature = 'separated'
autoclass_content = 'class'
autodoc_member_order = 'groupwise'
# autodoc_default_options = {'members': False, 'inherited-members': False}
# autosummay options
#
# # autosummay options
autosummary_generate = True
# spinx-gallery

View File

@ -13,7 +13,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(314, 232)
Form.resize(382, 375)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
@ -23,14 +23,14 @@ class Ui_Form(object):
self.gridLayout.setContentsMargins(0, 0, 0, 0)
self.gridLayout.setSpacing(3)
self.gridLayout.setObjectName("gridLayout")
self.widget = ExpandableWidget(Form)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Maximum)
self.typecomboBox = QtWidgets.QComboBox(Form)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.widget.sizePolicy().hasHeightForWidth())
self.widget.setSizePolicy(sizePolicy)
self.widget.setObjectName("widget")
self.gridLayout.addWidget(self.widget, 4, 0, 1, 2)
sizePolicy.setHeightForWidth(self.typecomboBox.sizePolicy().hasHeightForWidth())
self.typecomboBox.setSizePolicy(sizePolicy)
self.typecomboBox.setObjectName("typecomboBox")
self.gridLayout.addWidget(self.typecomboBox, 0, 0, 1, 2)
self.complex_widget = QtWidgets.QWidget(Form)
self.complex_widget.setObjectName("complex_widget")
self.gridLayout_2 = QtWidgets.QGridLayout(self.complex_widget)
@ -53,7 +53,15 @@ class Ui_Form(object):
self.label.setSizePolicy(sizePolicy)
self.label.setObjectName("label")
self.gridLayout_2.addWidget(self.label, 0, 0, 1, 2)
self.gridLayout.addWidget(self.complex_widget, 5, 0, 1, 2)
self.gridLayout.addWidget(self.complex_widget, 6, 0, 1, 2)
self.fitcomboBox = QtWidgets.QComboBox(Form)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.fitcomboBox.sizePolicy().hasHeightForWidth())
self.fitcomboBox.setSizePolicy(sizePolicy)
self.fitcomboBox.setObjectName("fitcomboBox")
self.gridLayout.addWidget(self.fitcomboBox, 1, 0, 1, 2)
self.use_function_button = QtWidgets.QToolButton(Form)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
@ -65,31 +73,6 @@ class Ui_Form(object):
self.use_function_button.setArrowType(QtCore.Qt.RightArrow)
self.use_function_button.setObjectName("use_function_button")
self.gridLayout.addWidget(self.use_function_button, 3, 1, 1, 1)
self.fitcomboBox = QtWidgets.QComboBox(Form)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.fitcomboBox.sizePolicy().hasHeightForWidth())
self.fitcomboBox.setSizePolicy(sizePolicy)
self.fitcomboBox.setObjectName("fitcomboBox")
self.gridLayout.addWidget(self.fitcomboBox, 1, 0, 1, 2)
self.typecomboBox = QtWidgets.QComboBox(Form)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.typecomboBox.sizePolicy().hasHeightForWidth())
self.typecomboBox.setSizePolicy(sizePolicy)
self.typecomboBox.setObjectName("typecomboBox")
self.gridLayout.addWidget(self.typecomboBox, 0, 0, 1, 2)
self.fitequation = QtWidgets.QLabel(Form)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.fitequation.sizePolicy().hasHeightForWidth())
self.fitequation.setSizePolicy(sizePolicy)
self.fitequation.setWordWrap(True)
self.fitequation.setObjectName("fitequation")
self.gridLayout.addWidget(self.fitequation, 2, 0, 1, 2)
self.operator_combobox = QtWidgets.QComboBox(Form)
self.operator_combobox.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
self.operator_combobox.setFrame(True)
@ -99,9 +82,19 @@ class Ui_Form(object):
self.operator_combobox.addItem("")
self.operator_combobox.addItem("")
self.gridLayout.addWidget(self.operator_combobox, 3, 0, 1, 1)
self.use_combobox = QtWidgets.QComboBox(Form)
self.use_combobox.setObjectName("use_combobox")
self.gridLayout.addWidget(self.use_combobox, 6, 0, 1, 2)
self.functree = FitModelTree(Form)
self.functree.setObjectName("functree")
self.functree.headerItem().setText(0, "1")
self.gridLayout.addWidget(self.functree, 5, 0, 1, 2)
self.fitequation = QtWidgets.QLabel(Form)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.fitequation.sizePolicy().hasHeightForWidth())
self.fitequation.setSizePolicy(sizePolicy)
self.fitequation.setWordWrap(True)
self.fitequation.setObjectName("fitequation")
self.gridLayout.addWidget(self.fitequation, 2, 0, 1, 2)
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
@ -115,9 +108,9 @@ class Ui_Form(object):
self.complex_comboBox.setItemText(2, _translate("Form", "Imaginary"))
self.label.setText(_translate("Form", "Complex function found"))
self.use_function_button.setText(_translate("Form", "Use"))
self.fitequation.setText(_translate("Form", "Equation"))
self.operator_combobox.setItemText(0, _translate("Form", "Add"))
self.operator_combobox.setItemText(1, _translate("Form", "Multiply"))
self.operator_combobox.setItemText(2, _translate("Form", "Subtract"))
self.operator_combobox.setItemText(3, _translate("Form", "Divide by"))
from ..lib.expandablewidget import ExpandableWidget
self.fitequation.setText(_translate("Form", "Equation"))
from ..fit.fit_forms import FitModelTree

View File

@ -15,7 +15,7 @@ from ...lib.symbols import SymbolStyle, symbolcycle
from ...data.nmr import Spectrum, FID
from ..Qt import QtCore
from ..io.exporters import pgitem_to_dic
from ..io.exporters import GraceExporter
from ..lib.decorators import plot_update
from ..lib.pg_objects import ErrorBars, PlotItem
@ -33,6 +33,7 @@ class ExperimentContainer(QtCore.QObject):
self._fits = []
self._data = data
self._manager = kwargs.get('manager')
self.graph = ''
self.mode = 'point'
self.plot_real = None
@ -374,10 +375,12 @@ class ExperimentContainer(QtCore.QObject):
def save(self, fname):
ext = fname.suffix
if ext == '.agr':
real_dic = pgitem_to_dic(self.plot_real)
from ..io.exporters import GraceExporter
dic = self._manager.graphs[self.graph].get_state()
dic['items'] = [self.plot_real.get_data_opts()]
if self.plot_imag is not None:
dic['items'].append(self.plot_imag.get_data_opts())
GraceExporter(real_dic).export(fname)
GraceExporter(dic).export(fname)
elif ext in ['.dat', '.txt']:
self._data.savetxt(fname, err=True)

View File

@ -409,6 +409,9 @@ class FitTableWidget(QtWidgets.QTableWidget):
while self.rowCount():
self.removeRow(0)
self.setColumnCount(2)
self.hideColumn(1)
for (sid, name) in set_ids:
item = QtWidgets.QTableWidgetItem(name)
item.setCheckState(QtCore.Qt.Checked)
@ -419,7 +422,7 @@ class FitTableWidget(QtWidgets.QTableWidget):
item2 = QtWidgets.QTableWidgetItem('')
self.setItem(row, 1, item2)
cb = QtWidgets.QComboBox()
cb = QtWidgets.QComboBox(parent=self)
cb.addItem('Default')
self.setCellWidget(row, 1, cb)

View File

@ -1,7 +1,6 @@
from itertools import cycle, count
from typing import List, Tuple, Union
from .fit_forms import FitModelTree
from ..lib import get_icon
from .._py.fitfunctionwidget import Ui_Form
from ..Qt import QtWidgets, QtCore, QtGui
@ -26,9 +25,6 @@ class QFunctionWidget(QtWidgets.QWidget, Ui_Form):
super().__init__(parent=parent)
self.setupUi(self)
self.functree = FitModelTree()
self.widget.setText('Model structure')
self.widget.addWidget(self.functree)
self._types = []
self.functions = find_models(models)
@ -57,8 +53,14 @@ class QFunctionWidget(QtWidgets.QWidget, Ui_Form):
for i, op_icon in enumerate(self.op_names):
self.operator_combobox.setItemIcon(i, get_icon(op_icon))
def __len__(self):
return self.use_combobox.count()
def __len__(self) -> int:
num = 0
iterator = QtWidgets.QTreeWidgetItemIterator(self.functree)
while iterator.value():
num += 1
iterator += 1
return num
@QtCore.pyqtSlot(int, name='on_typecomboBox_currentIndexChanged')
def change_group(self, idx: int):
@ -114,11 +116,6 @@ class QFunctionWidget(QtWidgets.QWidget, Ui_Form):
qcolor = QtGui.QColor(color)
self.functree.add_function(idx, cnt, op, name, qcolor, **kwargs)
self.use_combobox.addItem(name, userData=cnt)
self.use_combobox.setItemData(self.use_combobox.count()-1, color, QtCore.Qt.DecorationRole)
self.use_combobox.setCurrentIndex(self.use_combobox.count() - 1)
f = self.functions[idx]
if hasattr(f, 'iscomplex') and f.iscomplex:
self.iscomplex = True
@ -138,17 +135,11 @@ class QFunctionWidget(QtWidgets.QWidget, Ui_Form):
iterator += 1
self.complex_widget.setVisible(self.iscomplex)
for i in range(self.use_combobox.count()):
if idx == self.use_combobox.itemData(i):
self.use_combobox.removeItem(i)
break
self.itemRemoved.emit(idx)
@QtCore.pyqtSlot(int, name='on_use_combobox_currentIndexChanged')
def show_parameter(self, idx: int):
if self.use_combobox.count():
self.showFunction.emit(self.use_combobox.itemData(idx, QtCore.Qt.UserRole))
@QtCore.pyqtSlot(QtWidgets.QTreeWidgetItem, int, name='on_functree_itemClicked')
def show_parameter(self, item: QtWidgets.QTreeWidgetItem, _: int):
self.showFunction.emit(item.data(0, self.functree.counterRole))
def get_selected(self):
function_nr, idx = self.functree.get_selected()
@ -217,9 +208,5 @@ class QFunctionWidget(QtWidgets.QWidget, Ui_Form):
self.functree.clear()
self.functree.blockSignals(False)
self.use_combobox.blockSignals(True)
self.use_combobox.clear()
self.use_combobox.blockSignals(False)
self.complex_comboBox.setCurrentIndex(0)
self.complex_widget.hide()

View File

@ -511,9 +511,11 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
item = self.real_plots[sid]
other_item = self.imag_plots[sid]
# should legend be visible? is either real part or imaginary part shown?
if self.listWidget.item(i).checkState() and \
(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'))
if self.listWidget.item(i).checkState():
if item in self.graphic.items():
self.legend.addItem(item, convert(item.opts.get('name', ''), old='tex', new='html'))
elif other_item in self.graphic.items():
self.legend.addItem(other_item, convert(other_item.opts.get('name', ''), old='tex', new='html'))
def export_dialog(self):
filters = 'All files (*.*);;AGR (*.agr);;SVG (*.svg);;PDF (*.pdf)'
@ -580,12 +582,21 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
self.set_color(foreground=fg_color, background=bg_color)
def export_graphics(self):
def export_graphics(self) -> dict:
dic = self.get_state()
dic['items'] = []
in_legend = []
for item in self.curves():
item_dic = item[0].get_data_opts()
plot_item = item[0]
legend_shown = False
for sample, _ in self.legend.items:
if sample.item is plot_item:
legend_shown = True
break
in_legend.append(legend_shown)
item_dic = plot_item.get_data_opts()
if len(item) == 2:
# plot can show errorbars
item_dic['yerr'] = item[1].opts['topData']
@ -593,6 +604,8 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
if item_dic:
dic['items'].append(item_dic)
dic['in_legend'] = in_legend
return dic
def get_state(self) -> dict:

View File

@ -1,9 +1,8 @@
from __future__ import annotations
from numpy import c_
from ...io.graceeditor import GraceEditor
from ...lib.colors import Colors
from ...lib.lines import LineStyle
from ...lib.symbols import SymbolStyle
from ...utils.text import convert
from ..Qt import QtGui, QtCore, QtPrintSupport
@ -14,7 +13,7 @@ class GraceExporter:
self.__agr = None
self.__opts = kwargs
def export(self, outfile: str, mode: (int, str) = 'w'):
def export(self, outfile: str, mode: int | str = 'w'):
if mode == 'w':
self.__agr = GraceEditor()
else:
@ -27,7 +26,7 @@ class GraceExporter:
new_g.set_log(x=self.__opts['log'][0], y=self.__opts['log'][1])
new_g.set_onoff('legend', self.__opts['legend'])
new_g.set_property(**{'title': '"' + convert(self.__opts['labels'][2], old="html", new="agr") + '"',
new_g.set_property(**{'title': f'"{convert(self.__opts["labels"][2], old="html", new="agr")}"',
'legend loctype': 'view',
'legend': ', '.join(str(i) for i in new_g.world_to_view(self.__opts['legend_pos']))})
@ -43,7 +42,7 @@ class GraceExporter:
colors = self.__agr.colors
new_colors = []
for item in self.__opts['items']:
for plot_label, item in zip(self.__opts['in_legend'], self.__opts['items']):
new_s = self.__agr.new_set(g_idx)
sc = item['symbolcolor']
@ -75,7 +74,12 @@ class GraceExporter:
new_s.set_line(**{'color': c_num, 'linewidth': item['linewidth'],
'linestyle': item['linestyle'].to_agr()})
new_s.set_property(comment='"' + item['name'] + '"', legend='"' + item['name'] + '"')
if plot_label:
new_s.set_property(comment=f'"{item["name"]}"',
legend=f'"{convert(item["name"], old="tex", new="agr")}"')
else:
new_s.set_property(comment=f'"{item["name"]}"')
data = self.__agr.dataset(g_idx, new_s.idx)
if 'yerr' in item:
@ -108,36 +112,3 @@ class PDFPrintExporter:
painter = QtGui.QPainter(printer)
self.graphic.render(painter)
painter.end()
def pgitem_to_dic(item):
x, y = item.xData, item.yData
if (x is None) or (len(x) == 0):
return
opts = item.opts
item_dic = {
'x': x, 'y': y,
'name': opts['name'],
'symbolsize': opts['symbolSize']
}
if opts['symbol'] is None:
item_dic['symbol'] = SymbolStyle.No
item_dic['symbolcolor'] = Colors.Black
else:
item_dic['symbol'] = SymbolStyle.from_str(opts['symbol'])
item_dic['symbolcolor'] = Colors.from_rgb(*opts['symbolBrush'].color().getRgbF()[:3], normed=True)
pen = opts['pen']
if pen is not None:
item_dic['linestyle'] = LineStyle(pen.style())
item_dic['linecolor'] = Colors.from_rgb(*pen.color().getRgbF()[:3], normed=True)
item_dic['linewidth'] = pen.widthF()
else:
item_dic['linestyle'] = LineStyle.No
item_dic['linecolor'] = item_dic['symbolcolor']
item_dic['linewidth'] = 0.0
return item_dic

View File

@ -35,8 +35,6 @@ def diamond(a):
y = a-x
pxl.extend([[x, y], [-x, y], [x, -y], [-x, -y]])
print(np.array(pxl).shape)
return np.array(pxl)
@ -100,7 +98,6 @@ class GameOfLife:
self.fill = np.zeros(self.populations)
self._populate(fill, pattern)
print(rules)
if isinstance(rules, str):
try:
b_rule, s_rule = predefined_rules[rules]

View File

@ -165,8 +165,8 @@ class PlotItem(PlotDataItem):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.opts['linecolor'] = Colors.Black
self.opts['symbolcolor'] = Colors.Black
self.opts['linecolor'] = (0, 0, 0)
self.opts['symbolcolor'] = (0, 0, 0)
if self.opts['pen'] is not None:
pen = self.opts['pen']
@ -251,7 +251,6 @@ class PlotItem(PlotDataItem):
x = []
if y is None:
y = []
# scatterArgs['mask'] = self.dataMask
if curveArgs['pen'] is not None or (curveArgs['brush'] is not None and curveArgs['fillLevel'] is not None):
is_finite = np.isfinite(x) & np.isfinite(y)
@ -347,7 +346,6 @@ class PlotItem(PlotDataItem):
item_dic['symbolcolor'] = opts['symbolcolor']
pen = opts['pen']
if pen is not None:
item_dic['linestyle'] = LineStyle(pen.style())
item_dic['linecolor'] = opts['linecolor']

View File

@ -180,7 +180,6 @@ class SnakeBoard(Board):
self.update()
def snake_move(self):
print(self.next_direction)
if self.next_direction:
turn = self.next_direction.pop()
if self.direction != self._direction_pairs[turn]:

View File

@ -463,10 +463,16 @@ class GraceGraph(GraceProperties):
def set_log(self, x=None, y=None):
kwargs = {}
if x is not None:
kwargs['xaxes scale'] = 'Logarithmic' if x else 'Normal'
if y is not None:
kwargs['yaxes scale'] = 'Logarithmic' if y else 'Normal'
for ax, label in [(x, 'x'), (y, 'y')]:
if ax is not None:
if ax:
kwargs[label + 'axes scale'] = 'Logarithmic'
kwargs[label + 'axis ticklabel format'] = 'power'
kwargs[label + 'axis ticklabel prec'] = '0'
else:
kwargs[label + 'axes scale'] = 'Normal'
kwargs[label + 'axis ticklabel format'] = 'general'
kwargs[label + 'axis ticklabel prec'] = '5'
for i, line in enumerate(self):
m = self._RE_ENTRY.match(line)

View File

@ -1,14 +1,14 @@
"""
=============
Fit functions
=============
=====================================
Fit functions (:mod:`nmreval.models`)
=====================================
.. currentmodule:: nmreval.models
.. autosummary::
:toctree: generated
:nosignatures:
:template: autosummary/class_with_attributes.rst
:recursive:
Constant
Linear

View File

@ -3,7 +3,7 @@
Basic functions
***************
Simple basic functions
Simple functions
"""
import numpy as np
@ -27,7 +27,7 @@ class Constant:
Constant
.. math::
y = c \cdot x
y = c
Args:
x (array-like): Input values
@ -135,22 +135,7 @@ class Parabola:
class PowerLawCross:
"""
Crossover between to power laws at position :math:`x_0`
.. math::
y \\propto
\\begin{cases}
x^{b_1}, & x \le x_0 \\\\
x^{b_2}, & x > x_0
\\end{cases}
Args:
x (array_like): Input values
c (float): Prefactor
b1 (float): Exponent of first power law
b2 (float): Exponent of second power law
x0 (float): x position of crossover
Crossover between power laws
"""
type = 'Basic'
name = 'Crossing Power Laws'
@ -158,6 +143,23 @@ class PowerLawCross:
@staticmethod
def func(x, c, b1, b2, x0):
"""
Crossover between to power laws at position :math:`x_0`
.. math::
y \\propto
\\begin{cases}
x^{b_1}, & x \le x_0 \\\\
x^{b_2}, & x > x_0
\\end{cases}
Args:
x (array_like): Input values
c (float): Prefactor
b1 (float): Exponent of first power law
b2 (float): Exponent of second power law
x0 (float): x position of crossover
"""
mas = np.nonzero(x > x0)
ret_val = c * x**b1
c2 = c * x0**(b1-b2)
@ -167,19 +169,8 @@ class PowerLawCross:
class Sine:
"""
Sine function
.. math::
y = C\sin(a x - \phi)
Args:
x (array_like): Input values
c (float): Prefactor
a (float): frequency
phi (float): shift
Wavy sine function
"""
type = 'Basic'
name = 'Sine'
equation = r'C*sin(a*x-\phi)'
@ -187,6 +178,19 @@ class Sine:
@staticmethod
def func(x, c: float, a: float, phi: float):
"""
Calculate sine function
.. math::
y = C\sin(a x - \phi)
Args:
x (array_like): Input values
c (float): Prefactor
a (float): frequency
phi (float): shift
"""
return c*np.sin(a*x-phi)

View File

@ -116,15 +116,16 @@ class Peschier:
name = 'Diffusion + Cross-Relaxation'
type = 'Diffusion'
equation = r'Diffusion with cross-relax f(ast) \rightarrow s(low)'
params = ['M_{0}', 'D', 'T_{1,f}', 'T_{1,s}', 'k_{f}', 'k_{s}', 't_{ev}', 'g']
params = ['M_{0}', 'D', 'T_{1,f}', 'T_{1,s}', 'k', 'p_{f}', 't_{ev}', 'g']
bounds = [(0, None), (0, None), (0, None), (0, None), (0, None), (0, None)]
choices = [(r'\gamma', 'nucleus', gamma)]
@staticmethod
def func(x, m0, d, t1f, t1s, kf, ks, tp, g, nucleus=2.67522128e8):
def func(x, m0, d, t1f, t1s, k, pf, tp, g, nucleus: float = 2.67522128e8):
q = nucleus*g*tp
r1s, r1f = 1 / t1s, 1 / t1f
kf, ks = pf*k, (1-pf)*k
a_plus = 0.5 * (d*q*q + kf + ks + r1f + r1s + np.sqrt((d*q*q + kf + r1f - ks - r1s)**2 + 4*kf*ks))
a_minu = 0.5 * (d*q*q + kf + ks + r1f + r1s - np.sqrt((d*q*q + kf + r1f - ks - r1s)**2 + 4*kf*ks))

View File

@ -119,20 +119,3 @@ class SatRecMLF:
if kind == 'inv':
a = 2
return dm * (1 - a * mlf(-(x/t1)**alpha, alpha)) + m0
class PeschierHomogeneous:
name = 'Cross-Relaxation (g=0)'
type = 'Relaxation'
equation = r'Cross-Relaxation f(ast) \rightarrow s(low) (m_{s}(0)=0)'
params = ['M_{0}', 'k_{s}', 'k_{f}', 'T_{1,s}', 'T_{1,f}']
bounds = [(0, None), (0, None), (0, None), (0, None), (0, None)]
@staticmethod
def func(x, m, ks, kf, t1s, t1f):
r1f, r1s = 1/t1f, 1/t1s
a_plus = 0.5*((kf + ks + r1f + r1s) + np.sqrt((kf + r1f - ks - r1s)**2 + 4*kf*ks))
a_minu = 0.5*((kf + ks + r1f + r1s) - np.sqrt((kf + r1f - ks - r1s)**2 + 4*kf*ks))
return m*((a_plus - ks - r1s) / (a_plus - a_minu) * np.exp(-a_plus * x) -
(a_minu - ks - r1s) / (a_plus - a_minu) * np.exp(-a_minu * x))

View File

@ -1,3 +0,0 @@
from ..configs import config_paths
print(config_paths() / 'usermodels.py')

53
pyproject.toml Normal file
View File

@ -0,0 +1,53 @@
[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 = {text = "BSD 3-Clause License"}
[tool.setuptools]
include_package_data = true
[tool.setuptools.packages]
find = {}
scripts = bin/evaluate.py
[tool.setuptools.packages.find]
include =[
'nmreval*',
'resources*',
]
[tool.setuptools.package_data]
* = *.txt, *.npz, *.png, *.json

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>314</width>
<height>232</height>
<width>382</width>
<height>375</height>
</rect>
</property>
<property name="sizePolicy">
@ -35,17 +35,17 @@
<property name="spacing">
<number>3</number>
</property>
<item row="4" column="0" colspan="2">
<widget class="ExpandableWidget" name="widget" native="true">
<item row="0" column="0" colspan="2">
<widget class="QComboBox" name="typecomboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Maximum">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<item row="6" column="0" colspan="2">
<widget class="QWidget" name="complex_widget" native="true">
<layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
@ -102,6 +102,16 @@
</layout>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QComboBox" name="fitcomboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QToolButton" name="use_function_button">
<property name="sizePolicy">
@ -124,42 +134,6 @@
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QComboBox" name="fitcomboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QComboBox" name="typecomboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QLabel" name="fitequation">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Equation</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QComboBox" name="operator_combobox">
<property name="sizeAdjustPolicy">
@ -190,17 +164,38 @@
</item>
</widget>
</item>
<item row="6" column="0" colspan="2">
<widget class="QComboBox" name="use_combobox"/>
<item row="5" column="0" colspan="2">
<widget class="FitModelTree" name="functree">
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QLabel" name="fitequation">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Equation</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ExpandableWidget</class>
<extends>QWidget</extends>
<header>..lib.expandablewidget</header>
<container>1</container>
<class>FitModelTree</class>
<extends>QTreeWidget</extends>
<header>..fit.fit_forms</header>
</customwidget>
</customwidgets>
<resources/>

47
setup.cfg Normal file
View File

@ -0,0 +1,47 @@
[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

242
setup.py
View File

@ -1,11 +1,3 @@
from setuptools import setup
setup(
name='nmreval',
version='0.1',
packages=['.nmreval']
)
"""A setuptools based setup module.
See:
@ -26,193 +18,57 @@ long_description = (here / 'README.md').read_text(encoding='utf-8')
# Fields marked as "Optional" may be commented out.
setup(
# This is the name of your project. The first time you publish this
# package, this name will be registered for you. It will determine how
# users can install this project, e.g.:
# 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',
# ],
#
# $ pip install sampleproject
#
# And where it will live on PyPI: https://pypi.org/project/sampleproject/
#
# There are some restrictions on what makes a valid project name
# specification here:
# https://packaging.python.org/specifications/core-metadata/#name
name='nmreval', # Required
# Versions should comply with PEP 440:
# https://www.python.org/dev/peps/pep-0440/
#
# For a discussion on single-sourcing the version across setup.py and the
# project code, see
# https://packaging.python.org/guides/single-sourcing-package-version/
version='0.1', # Required
# This is a one-line description or tagline of what your project does. This
# corresponds to the "Summary" metadata field:
# https://packaging.python.org/specifications/core-metadata/#summary
description='A sample Python project', # Optional
# This is an optional longer description of your project that represents
# the body of text which users will see when they visit PyPI.
#
# Often, this is the same as your README, so you can just read it in from
# that file directly (as we have already done above)
#
# This field corresponds to the "Description" metadata field:
# https://packaging.python.org/specifications/core-metadata/#description-optional
long_description=long_description, # Optional
# Denotes that our long_description is in Markdown; valid values are
# text/plain, text/x-rst, and text/markdown
#
# Optional if long_description is written in reStructuredText (rst) but
# required for plain-text or Markdown; if unspecified, "applications should
# attempt to render [the long_description] as text/x-rst; charset=UTF-8 and
# fall back to text/plain if it is not valid rst" (see link below)
#
# This field corresponds to the "Description-Content-Type" metadata field:
# https://packaging.python.org/specifications/core-metadata/#description-content-type-optional
long_description_content_type='text/x-rst', # Optional (see note above)
# This should be a valid link to your project's main homepage.
#
# This field corresponds to the "Home-Page" metadata field:
# https://packaging.python.org/specifications/core-metadata/#home-page-optional
url='https://github.com/pypa/sampleproject', # Optional
# This should be your name or the name of the organization which owns the
# project.
author='Dominik Demuth', # Optional
# This should be a valid email address corresponding to the author listed
# above.
author_email='dominik.demuth@physik.tu-darmstadt.de', # Optional
# Classifiers help users find your project by categorizing it.
#
# For a list of valid classifiers, see https://pypi.org/classifiers/
classifiers=[ # Optional
# How mature is this project? Common values are
# 3 - Alpha
# 4 - Beta
# 5 - Production/Stable
'Development Status :: 3 - Alpha',
# Indicate who your project is intended for
'Intended Audience :: Developers',
'Topic :: Software Development :: Build Tools',
# Pick your license as you wish
'License :: OSI Approved :: MIT License',
# Specify the Python versions you support here. In particular, ensure
# that you indicate you support Python 3. These classifiers are *not*
# checked by 'pip install'. See instead 'python_requires' below.
'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',
],
# This field adds keywords for your project which will appear on the
# project page. What does your project relate to?
#
# Note that this is a list of additional keywords, separated
# by commas, to be used to assist searching for the distribution in a
# larger catalog.
keywords='sample, setuptools, development', # Optional
# When your source code is in a subdirectory under the project root, e.g.
# `src/`, it is necessary to specify the `package_dir` argument.
package_dir={'': 'src'}, # Optional
# You can just specify package directories manually here if your project is
# simple. Or you can use find_packages().
#
# Alternatively, if you just want to distribute a single Python file, use
# the `py_modules` argument instead as follows, which will expect a file
# called `my_module.py` to exist:
#
# py_modules=["my_module"],
#
packages=find_packages(where='src'), # Required
# Specify which Python versions you support. In contrast to the
# 'Programming Language' classifiers above, 'pip install' will check this
# and refuse to install the project if the version does not match. See
# https://packaging.python.org/guides/distributing-packages-using-setuptools/#python-requires
python_requires='>=3.7, <4',
# This field lists other packages that your project depends on to run.
# Any package you put here will be installed by pip when your project is
# installed, so they must be valid existing projects.
#
# For an analysis of "install_requires" vs pip's requirements files see:
# https://packaging.python.org/discussions/install-requires-vs-requirements/
install_requires=[
'numpy',
'scipy',
'matplotlib',
'bsddb3',
'pyqtgraph',
'PyQt4',
'h5py'
], # Optional
# List additional groups of dependencies here (e.g. development
# dependencies). Users will be able to install these using the "extras"
# syntax, for example:
#
# $ pip install sampleproject[dev]
#
# Similar to `install_requires` above, these must be valid existing
# projects.
# extras_require={ # Optional
# 'dev': ['check-manifest'],
# 'test': ['coverage'],
# 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',
# ],
# },
# If there are data files included in your packages that need to be
# installed, specify them here.
package_data={ # Optional
'sample': ['package_data.dat'],
},
# Although 'package_data' is the preferred approach, in some case you may
# need to place data files outside of your packages. See:
# http://docs.python.org/distutils/setupscript.html#installing-additional-files
#
# In this case, 'data_file' will be installed into '<sys.prefix>/my_data'
data_files=[('my_data', ['data/data_file'])], # Optional
# To provide executable scripts, use entry points in preference to the
# "scripts" keyword. Entry points provide cross-platform support and allow
# `pip` to create the appropriate form of executable for the target
# platform.
#
# For example, the following would provide a command called `sample` which
# executes the function `main` from this package when invoked:
entry_points={ # Optional
'console_scripts': [
'sample=bin:main',
],
},
# List additional URLs that are relevant to your project as a dict.
#
# This field corresponds to the "Project-URL" metadata fields:
# https://packaging.python.org/specifications/core-metadata/#project-url-multiple-use
#
# Examples listed include a pattern for specifying where the package tracks
# issues, where the source is hosted, where to say thanks to the package
# maintainers, and where to support the project financially. The key is
# what's used to render the link text on PyPI.
# project_urls={ # Optional
# 'Bug Reports': 'https://github.com/pypa/sampleproject/issues',
# 'Funding': 'https://donate.pypi.org',
# 'Say Thanks!': 'http://saythanks.io/to/example',
# 'Source': 'https://github.com/pypa/sampleproject/',
# 'Source': 'https://chaos3.fkp.physik.tu-darmstadt.de/source/nmreval/',
# },
)