doc added; reading of isochronal bds
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
.. examples-index:
|
||||
|
||||
.. _gallery:
|
||||
|
||||
========
|
||||
Examples
|
||||
========
|
||||
|
||||
This page contains example plots. Click on any image to see the full image and source code.
|
||||
@@ -0,0 +1,6 @@
|
||||
.. _distribution_examples:
|
||||
|
||||
.. _distribution-examples-index:
|
||||
|
||||
Distribution of correlation times
|
||||
=================================
|
||||
@@ -0,0 +1,50 @@
|
||||
"""
|
||||
=========
|
||||
Cole-Cole
|
||||
=========
|
||||
|
||||
Example for Cole-Cole distributions
|
||||
"""
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
from nmreval.distributions import ColeCole
|
||||
|
||||
x = np.logspace(-5, 5, num=101)
|
||||
|
||||
cc = ColeCole
|
||||
|
||||
alpha_CC = [0.3, 0.5, 0.7]
|
||||
|
||||
fig, axes = plt.subplots(2, 3, constrained_layout=True)
|
||||
|
||||
lines = []
|
||||
for a in alpha_CC:
|
||||
axes[0, 0].plot(np.log10(x), cc.correlation(x, 1, a))
|
||||
axes[1, 0].plot(np.log10(x), np.log10(cc.specdens(x, 1, a)))
|
||||
axes[0, 1].plot(np.log10(x), np.log10(cc.susceptibility(x, 1, a).real))
|
||||
axes[1, 1].plot(np.log10(x), np.log10(cc.susceptibility(x, 1, a).imag))
|
||||
l, = axes[0, 2].plot(np.log10(x), cc.distribution(x, 1, a),
|
||||
label=rf'$\alpha={a}$')
|
||||
lines.append(l)
|
||||
|
||||
fig_titles = ('Correlation function', 'Susceptibility (real)', 'Distribution',
|
||||
'Spectral density', 'Susceptibility (imag)')
|
||||
fig_xlabel = (r'$\log(t/\tau_\mathrm{HN})$', r'$\log(\omega\tau_\mathrm{HN})$',
|
||||
r'$\log(\tau/\tau_\mathrm{HN})$', r'$\log(\omega\tau_\mathrm{HN})$',
|
||||
r'$\log(\omega\tau_\mathrm{HN})$')
|
||||
fig_ylabel = (r'$C(t)$', r"$\log(\chi'(\omega))$", r'$G(\ln\tau)$',
|
||||
r'$\log(J(\omega))$', r"$\log(\chi''(\omega))$")
|
||||
|
||||
for title, xlabel, ylabel, ax in zip(fig_titles, fig_xlabel, fig_ylabel, axes.ravel()):
|
||||
ax.set_title(title)
|
||||
ax.set_xlabel(xlabel)
|
||||
ax.set_ylabel(ylabel)
|
||||
|
||||
labels = [l.get_label() for l in lines]
|
||||
leg = fig.legend(lines, labels, loc='center left', bbox_to_anchor=(1.05, 0.50),
|
||||
bbox_transform=axes[1, 1].transAxes)
|
||||
|
||||
fig.delaxes(axes[1, 2])
|
||||
|
||||
plt.show()
|
||||
@@ -0,0 +1,50 @@
|
||||
"""
|
||||
=============
|
||||
Cole-Davidson
|
||||
=============
|
||||
|
||||
Example for Cole-Davidson distributions
|
||||
"""
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
from nmreval.distributions import ColeDavidson
|
||||
|
||||
x = np.logspace(-5, 5, num=101)
|
||||
|
||||
cd = ColeDavidson
|
||||
|
||||
gamma_CD = [0.3, 0.5, 0.7]
|
||||
|
||||
fig, axes = plt.subplots(2, 3, constrained_layout=True)
|
||||
|
||||
lines = []
|
||||
for g in gamma_CD:
|
||||
axes[0, 0].plot(np.log10(x), cd.correlation(x, 1, g))
|
||||
axes[1, 0].plot(np.log10(x), np.log10(cd.specdens(x, 1, g)))
|
||||
axes[0, 1].plot(np.log10(x), np.log10(cd.susceptibility(x, 1, g).real))
|
||||
axes[1, 1].plot(np.log10(x), np.log10(cd.susceptibility(x, 1, g).imag))
|
||||
l, = axes[0, 2].plot(np.log10(x), cd.distribution(x, 1, g),
|
||||
label=rf'$\gamma={g}$')
|
||||
lines.append(l)
|
||||
|
||||
fig_titles = ('Correlation function', 'Susceptibility (real)', 'Distribution',
|
||||
'Spectral density', 'Susceptibility (imag)')
|
||||
fig_xlabel = (r'$\log(t/\tau_\mathrm{CD})$', r'$\log(\omega\tau_\mathrm{CD})$',
|
||||
r'$\log(\tau/\tau_\mathrm{CD})$', r'$\log(\omega\tau_\mathrm{CD})$',
|
||||
r'$\log(\omega\tau_\mathrm{CD})$')
|
||||
fig_ylabel = (r'$C(t)$', r"$\log(\chi'(\omega))$", r'$G(\ln\tau)$',
|
||||
r'$\log(J(\omega))$', r"$\log(\chi''(\omega))$")
|
||||
|
||||
for title, xlabel, ylabel, ax in zip(fig_titles, fig_xlabel, fig_ylabel, axes.ravel()):
|
||||
ax.set_title(title)
|
||||
ax.set_xlabel(xlabel)
|
||||
ax.set_ylabel(ylabel)
|
||||
|
||||
labels = [l.get_label() for l in lines]
|
||||
leg = fig.legend(lines, labels, loc='center left', bbox_to_anchor=(1.05, 0.50),
|
||||
bbox_transform=axes[1, 1].transAxes)
|
||||
|
||||
fig.delaxes(axes[1, 2])
|
||||
|
||||
plt.show()
|
||||
@@ -0,0 +1,53 @@
|
||||
"""
|
||||
================
|
||||
Havriliak-Negami
|
||||
================
|
||||
|
||||
Example for Havriliak-Negami distributions
|
||||
"""
|
||||
from itertools import product
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
from nmreval.distributions import HavriliakNegami
|
||||
|
||||
x = np.logspace(-5, 5, num=101)
|
||||
|
||||
hn = HavriliakNegami
|
||||
|
||||
alpha_CC = [0.4, 0.8]
|
||||
gamma_CD = [0.3, 0.7]
|
||||
|
||||
fig, axes = plt.subplots(2, 3, constrained_layout=True)
|
||||
|
||||
lines = []
|
||||
for a, g in product(alpha_CC, gamma_CD):
|
||||
axes[0, 0].plot(np.log10(x), hn.correlation(x, 1, a, g))
|
||||
axes[1, 0].plot(np.log10(x), np.log10(hn.specdens(x, 1, a, g)))
|
||||
axes[0, 1].plot(np.log10(x), np.log10(hn.susceptibility(x, 1, a, g).real))
|
||||
axes[1, 1].plot(np.log10(x), np.log10(hn.susceptibility(x, 1, a, g).imag))
|
||||
l, = axes[0, 2].plot(np.log10(x), hn.distribution(x, 1, a, g),
|
||||
label=rf'$\alpha={a}, \gamma={g}$')
|
||||
lines.append(l)
|
||||
|
||||
fig_titles = ('Correlation function', 'Susceptibility (real)', 'Distribution',
|
||||
'Spectral density', 'Susceptibility (imag)')
|
||||
fig_xlabel = (r'$\log(t/\tau_\mathrm{HN})$', r'$\log(\omega\tau_\mathrm{HN})$',
|
||||
r'$\log(\tau/\tau_\mathrm{HN})$', r'$\log(\omega\tau_\mathrm{HN})$',
|
||||
r'$\log(\omega\tau_\mathrm{HN})$')
|
||||
fig_ylabel = (r'$C(t)$', r"$\log(\chi'(\omega))$", r'$G(\ln\tau)$',
|
||||
r'$\log(J(\omega))$', r"$\log(\chi''(\omega))$")
|
||||
|
||||
for title, xlabel, ylabel, ax in zip(fig_titles, fig_xlabel, fig_ylabel, axes.ravel()):
|
||||
ax.set_title(title)
|
||||
ax.set_xlabel(xlabel)
|
||||
ax.set_ylabel(ylabel)
|
||||
|
||||
labels = [l.get_label() for l in lines]
|
||||
leg = fig.legend(lines, labels, loc='center left', bbox_to_anchor=(1.05, 0.50),
|
||||
bbox_transform=axes[1, 1].transAxes)
|
||||
|
||||
fig.delaxes(axes[1, 2])
|
||||
|
||||
plt.show()
|
||||
@@ -0,0 +1,50 @@
|
||||
"""
|
||||
=========================
|
||||
Kohlrausch-Williams-Watts
|
||||
=========================
|
||||
|
||||
Example for KWW distributions
|
||||
"""
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
from nmreval.distributions import KWW
|
||||
|
||||
x = np.logspace(-5, 5, num=101)
|
||||
|
||||
kww = KWW
|
||||
|
||||
beta_KWW = [0.3, 0.5, 0.7]
|
||||
|
||||
fig, axes = plt.subplots(2, 3, constrained_layout=True)
|
||||
|
||||
lines = []
|
||||
for b in beta_KWW:
|
||||
axes[0, 0].plot(np.log10(x), kww.correlation(x, 1, b))
|
||||
axes[1, 0].plot(np.log10(x), np.log10(kww.specdens(x, 1, b)))
|
||||
axes[0, 1].plot(np.log10(x), np.log10(kww.susceptibility(x, 1, b).real))
|
||||
axes[1, 1].plot(np.log10(x), np.log10(kww.susceptibility(x, 1, b).imag))
|
||||
l, = axes[0, 2].plot(np.log10(x), kww.distribution(x, 1, b),
|
||||
label=rf'$\beta={b}$')
|
||||
lines.append(l)
|
||||
|
||||
fig_titles = ('Correlation function', 'Susceptibility (real)', 'Distribution',
|
||||
'Spectral density', 'Susceptibility (imag)')
|
||||
fig_xlabel = (r'$\log(t/\tau_\mathrm{KWW})$', r'$\log(\omega\tau_\mathrm{KWW})$',
|
||||
r'$\log(\tau/\tau_\mathrm{KWW})$', r'$\log(\omega\tau_\mathrm{KWW})$',
|
||||
r'$\log(\omega\tau_\mathrm{KWW})$')
|
||||
fig_ylabel = (r'$C(t)$', r"$\log(\chi'(\omega))$", r'$G(\ln\tau)$',
|
||||
r'$\log(J(\omega))$', r"$\log(\chi''(\omega))$")
|
||||
|
||||
for title, xlabel, ylabel, ax in zip(fig_titles, fig_xlabel, fig_ylabel, axes.ravel()):
|
||||
ax.set_title(title)
|
||||
ax.set_xlabel(xlabel)
|
||||
ax.set_ylabel(ylabel)
|
||||
|
||||
labels = [l.get_label() for l in lines]
|
||||
leg = fig.legend(lines, labels, loc='center left', bbox_to_anchor=(1.05, 0.50),
|
||||
bbox_transform=axes[1, 1].transAxes)
|
||||
|
||||
fig.delaxes(axes[1, 2])
|
||||
|
||||
plt.show()
|
||||
@@ -0,0 +1,50 @@
|
||||
"""
|
||||
============
|
||||
Log-Gaussian
|
||||
============
|
||||
|
||||
Example for Log-Gaussian distributions
|
||||
"""
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
from nmreval.distributions import LogGaussian
|
||||
|
||||
x = np.logspace(-5, 5, num=101)
|
||||
|
||||
lg = LogGaussian
|
||||
|
||||
sigma_lg = [1, 3, 5]
|
||||
|
||||
fig, axes = plt.subplots(2, 3, constrained_layout=True)
|
||||
|
||||
lines = []
|
||||
for s in sigma_lg:
|
||||
axes[0, 0].plot(np.log10(x), lg.correlation(x, 1, s))
|
||||
axes[1, 0].plot(np.log10(x), np.log10(lg.specdens(x, 1, s)))
|
||||
axes[0, 1].plot(np.log10(x), np.log10(lg.susceptibility(x, 1, s).real))
|
||||
axes[1, 1].plot(np.log10(x), np.log10(lg.susceptibility(x, 1, s).imag))
|
||||
l, = axes[0, 2].plot(np.log10(x), lg.distribution(x, 1, s),
|
||||
label=rf'$\sigma={s}$')
|
||||
lines.append(l)
|
||||
|
||||
fig_titles = ('Correlation function', 'Susceptibility (real)', 'Distribution',
|
||||
'Spectral density', 'Susceptibility (imag)')
|
||||
fig_xlabel = (r'$\log(t/\tau_\mathrm{LG})$', r'$\log(\omega\tau_\mathrm{LG})$',
|
||||
r'$\log(\tau/\tau_\mathrm{LG})$', r'$\log(\omega\tau_\mathrm{LG})$',
|
||||
r'$\log(\omega\tau_\mathrm{LG})$')
|
||||
fig_ylabel = (r'$C(t)$', r"$\log(\chi'(\omega))$", r'$G(\ln\tau)$',
|
||||
r'$\log(J(\omega))$', r"$\log(\chi''(\omega))$")
|
||||
|
||||
for title, xlabel, ylabel, ax in zip(fig_titles, fig_xlabel, fig_ylabel, axes.ravel()):
|
||||
ax.set_title(title)
|
||||
ax.set_xlabel(xlabel)
|
||||
ax.set_ylabel(ylabel)
|
||||
|
||||
labels = [l.get_label() for l in lines]
|
||||
leg = fig.legend(lines, labels, loc='center left', bbox_to_anchor=(1.05, 0.50),
|
||||
bbox_transform=axes[1, 1].transAxes)
|
||||
|
||||
fig.delaxes(axes[1, 2])
|
||||
|
||||
plt.show()
|
||||
@@ -0,0 +1,6 @@
|
||||
.. _nmr_examples:
|
||||
|
||||
.. _nmr-examples-index:
|
||||
|
||||
NMR specifics
|
||||
=============
|
||||
@@ -0,0 +1,67 @@
|
||||
"""
|
||||
=======================
|
||||
Spin-lattice relaxation
|
||||
=======================
|
||||
|
||||
Example for
|
||||
"""
|
||||
import numpy as np
|
||||
from matplotlib import pyplot as plt
|
||||
|
||||
from nmreval.distributions import ColeDavidson
|
||||
from nmreval.nmr import Relaxation, RelaxationEvaluation
|
||||
from nmreval.nmr.coupling import Quadrupolar
|
||||
from nmreval.utils.constants import kB
|
||||
|
||||
# Define temperature range
|
||||
inv_temp = np.linspace(3, 9, num=30)
|
||||
temperature = 1000/inv_temp
|
||||
|
||||
# spectral density parameter
|
||||
ea = 0.45
|
||||
tau = 1e-21 * np.exp(ea / kB / temperature)
|
||||
gamma_cd = 0.1
|
||||
|
||||
# interaction parameter
|
||||
omega = 2*np.pi*46e6
|
||||
delta = 120e3
|
||||
eta = 0
|
||||
|
||||
r = Relaxation()
|
||||
r.set_distribution(ColeDavidson) # the only parameter that has to be 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)
|
||||
noisy = (rng.random(t1_values.size)-0.5) * 0.5 * t1_values + t1_values
|
||||
|
||||
# set parameter and data
|
||||
r_eval = RelaxationEvaluation()
|
||||
r_eval.set_distribution(ColeDavidson)
|
||||
r_eval.set_coupling(Quadrupolar, (delta, eta))
|
||||
r_eval.data(temperature, noisy)
|
||||
r_eval.omega = omega
|
||||
|
||||
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])
|
||||
|
||||
found_gamma, found_height = r_eval.get_increase(t1_min_inter[1], idx=0, mode='distribution')
|
||||
print(found_gamma)
|
||||
|
||||
plt.axhline(found_height)
|
||||
plt.show()
|
||||
|
||||
#%%
|
||||
# Now we found temperature and height of the minimum we can calculate the correlation time
|
||||
|
||||
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')
|
||||
plt.show()
|
||||
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 600 KiB After Width: | Height: | Size: 600 KiB |
@@ -0,0 +1,14 @@
|
||||
:mod:`{{ module | escape }}`.{{ objname }}
|
||||
{{ underline }}==================
|
||||
|
||||
.. currentmodule:: {{ module }}
|
||||
|
||||
.. autoclass:: {{ objname }}
|
||||
:members:
|
||||
:inherited-members:
|
||||
|
||||
.. include:: {{ fullname }}.examples
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<div class="clearer"></div>
|
||||
@@ -0,0 +1,5 @@
|
||||
.. automodule:: nmreval.data
|
||||
:no-members:
|
||||
:no-inherited-members:
|
||||
:no-special-members:
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
.. automodule:: nmreval.distributions
|
||||
:no-members:
|
||||
:no-inherited-members:
|
||||
:no-special-members:
|
||||
@@ -0,0 +1,13 @@
|
||||
=========
|
||||
Reference
|
||||
=========
|
||||
|
||||
.. toctree::
|
||||
:caption: Table of contents
|
||||
:maxdepth: 1
|
||||
:glob:
|
||||
|
||||
data
|
||||
models
|
||||
distributions
|
||||
nmr
|
||||
@@ -0,0 +1,4 @@
|
||||
.. automodule:: nmreval.models
|
||||
:no-members:
|
||||
:no-inherited-members:
|
||||
:no-special-members:
|
||||
@@ -0,0 +1,4 @@
|
||||
.. automodule:: nmreval.nmr
|
||||
:no-members:
|
||||
:no-inherited-members:
|
||||
:no-special-members:
|
||||
@@ -10,6 +10,7 @@
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
import os
|
||||
import sys
|
||||
import sphinx_bootstrap_theme
|
||||
sys.path.append('/autohome/dominik/nmreval')
|
||||
@@ -43,8 +44,46 @@ extensions = [
|
||||
'sphinx.ext.napoleon',
|
||||
'sphinx.ext.viewcode',
|
||||
'sphinx.ext.intersphinx',
|
||||
'sphinx_gallery.gen_gallery',
|
||||
]
|
||||
|
||||
# configuration for intersphinx
|
||||
intersphinx_mapping = {
|
||||
'numpy': ('https://numpy.org/doc/stable/', None),
|
||||
'python': ('https://docs.python.org/3/', None),
|
||||
'scipy': ('https://docs.scipy.org/doc/scipy/', None),
|
||||
'PyQt': ('https://www.riverbankcomputing.com/static/Docs/PyQt5/', None),
|
||||
'matplotlib': ('https://matplotlib.org/stable', None),
|
||||
}
|
||||
|
||||
# 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
|
||||
autosummary_generate = True
|
||||
|
||||
# spinx-gallery
|
||||
sphinx_gallery_conf = {
|
||||
'examples_dirs': '../examples', # path to your example scripts
|
||||
'gallery_dirs': ['gallery'], # path to where to save gallery generated output
|
||||
'backreferences_dir': os.path.join('api', 'generated'), # directory where function/class granular galleries are stored
|
||||
'doc_module': 'nmreval', # Modules for which function/class level galleries are created.
|
||||
'reference_url': {
|
||||
'nmreval': None, # The module you locally document uses None
|
||||
},
|
||||
'download_all_examples': False,
|
||||
'plot_gallery': True,
|
||||
'inspect_global_variables': False,
|
||||
'remove_config_comments': True,
|
||||
'min_reported_time': 10000000000,
|
||||
'show_memory': False,
|
||||
'show_signature': False,
|
||||
}
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
#
|
||||
@@ -115,30 +154,15 @@ todo_include_todos = False
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
# html_theme = 'bootstrap'
|
||||
html_theme = 'pydata_sphinx_theme'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#
|
||||
# html_theme_options = {
|
||||
# # 'source_link_position': "footer",
|
||||
# 'bootswatch_theme': "cosmo",
|
||||
# 'navbar_title': "Welcome to hell",
|
||||
# 'navbar_sidebarrel': True,
|
||||
# 'nosidebar': False,
|
||||
# 'body_max_width': '100%',
|
||||
# 'navbar_links': [
|
||||
# ('User guide', 'user_guide/index'),
|
||||
# ('References', 'api/index'),
|
||||
# ],
|
||||
# }
|
||||
html_theme_options = {
|
||||
'collapse_navigation': False,
|
||||
'show_prev_next': False,
|
||||
'navbar_end': ['navbar-icon-links.html', 'search-field.html'],
|
||||
'show_toc_level': 3
|
||||
}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
@@ -396,29 +420,3 @@ epub_exclude_files = ['search.html']
|
||||
# If false, no index is generated.
|
||||
#
|
||||
# epub_use_index = True
|
||||
|
||||
|
||||
# configuration for intersphinx
|
||||
intersphinx_mapping = {
|
||||
'Pillow': ('https://pillow.readthedocs.io/en/stable/', None),
|
||||
'cycler': ('https://matplotlib.org/cycler/', None),
|
||||
'dateutil': ('https://dateutil.readthedocs.io/en/stable/', None),
|
||||
'ipykernel': ('https://ipykernel.readthedocs.io/en/latest/', None),
|
||||
'numpy': ('https://numpy.org/doc/stable/', None),
|
||||
'pandas': ('https://pandas.pydata.org/pandas-docs/stable/', None),
|
||||
'pytest': ('https://pytest.org/en/stable/', None),
|
||||
'python': ('https://docs.python.org/3/', None),
|
||||
'scipy': ('https://docs.scipy.org/doc/scipy/', None),
|
||||
'tornado': ('https://www.tornadoweb.org/en/stable/', None),
|
||||
'xarray': ('https://xarray.pydata.org/en/stable/', None),
|
||||
'PyQt': ('https://www.riverbankcomputing.com/static/Docs/PyQt5/', None),
|
||||
}
|
||||
|
||||
# autodoc options
|
||||
autodoc_typehints = 'none'
|
||||
autodoc_class_signature = 'separated'
|
||||
autoclass_content = 'class'
|
||||
|
||||
# autosummay options
|
||||
autosummary_generate = True
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
: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
|
||||
|
||||
<div class="sphx-glr-clear"></div>
|
||||
|
||||
|
||||
|
||||
.. _sphx_glr_gallery_distribution:
|
||||
|
||||
.. _distribution_examples:
|
||||
|
||||
.. _distribution-examples-index:
|
||||
|
||||
Distribution of correlation times
|
||||
=================================
|
||||
|
||||
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<div class="sphx-glr-thumbcontainer" tooltip="Example for KWW distributions">
|
||||
|
||||
.. 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
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
/gallery/distribution/plot_KWW
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<div class="sphx-glr-thumbcontainer" tooltip="Example for Cole-Cole distributions">
|
||||
|
||||
.. 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
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
/gallery/distribution/plot_ColeCole
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<div class="sphx-glr-thumbcontainer" tooltip="Example for Log-Gaussian distributions">
|
||||
|
||||
.. 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
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
/gallery/distribution/plot_LogGaussian
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<div class="sphx-glr-thumbcontainer" tooltip="Example for Cole-Davidson distributions">
|
||||
|
||||
.. 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
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
/gallery/distribution/plot_ColeDavidson
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<div class="sphx-glr-thumbcontainer" tooltip="Example for Havriliak-Negami distributions">
|
||||
|
||||
.. 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
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
/gallery/distribution/plot_HavriliakNegami
|
||||
.. raw:: html
|
||||
|
||||
<div class="sphx-glr-clear"></div>
|
||||
|
||||
|
||||
|
||||
.. _sphx_glr_gallery_nmr:
|
||||
|
||||
.. _nmr_examples:
|
||||
|
||||
.. _nmr-examples-index:
|
||||
|
||||
NMR specifics
|
||||
=============
|
||||
|
||||
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<div class="sphx-glr-thumbcontainer" tooltip="Example for">
|
||||
|
||||
.. 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
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
/gallery/nmr/plot_RelaxationEvaluation
|
||||
.. raw:: html
|
||||
|
||||
<div class="sphx-glr-clear"></div>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
'/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)
|
||||
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
'/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)
|
||||
@@ -3,17 +3,15 @@
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
###################################
|
||||
#####################
|
||||
NMREval documentation
|
||||
###################################
|
||||
#####################
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
user_guide/index
|
||||
|
||||
nmr/index
|
||||
|
||||
gallery/index
|
||||
api/index
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
@@ -1,25 +0,0 @@
|
||||
{{ fullname | escape | underline }}
|
||||
|
||||
|
||||
.. currentmodule:: {{ module }}
|
||||
|
||||
|
||||
{% if objtype in ['class'] %}
|
||||
|
||||
Inheritance diagram
|
||||
|
||||
.. inheritance-diagram:: {{ objname }}
|
||||
:parts: 1
|
||||
|
||||
{{ objtype }}
|
||||
|
||||
.. auto{{ objtype }}:: {{ objname }}
|
||||
:special-members: __call__
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
{% else %}
|
||||
.. auto{{ objtype }}:: {{ objname }}
|
||||
|
||||
{% endif %}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
**************
|
||||
Data container
|
||||
**************
|
||||
|
||||
.. automodule:: nmreval.data
|
||||
:no-members:
|
||||
:no-undoc-members:
|
||||
|
||||
|
||||
.. contents:: Table of Contents
|
||||
:depth: 3
|
||||
:local:
|
||||
:backlinks: entry
|
||||
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
:template: autosummary.rst
|
||||
:nosignatures:
|
||||
|
||||
nmreval.data.Points
|
||||
nmreval.data.Signal
|
||||
nmreval.data.BDS
|
||||
@@ -1,23 +0,0 @@
|
||||
nmreval.data.BDS
|
||||
================
|
||||
|
||||
|
||||
.. currentmodule:: nmreval.data
|
||||
|
||||
|
||||
|
||||
|
||||
Inheritance diagram
|
||||
|
||||
.. inheritance-diagram:: BDS
|
||||
:parts: 1
|
||||
|
||||
class
|
||||
|
||||
.. autoclass:: BDS
|
||||
:special-members: __call__
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
nmreval.data.Points
|
||||
===================
|
||||
|
||||
|
||||
.. currentmodule:: nmreval.data
|
||||
|
||||
|
||||
|
||||
|
||||
Inheritance diagram
|
||||
|
||||
.. inheritance-diagram:: Points
|
||||
:parts: 1
|
||||
|
||||
class
|
||||
|
||||
.. autoclass:: Points
|
||||
:special-members: __call__
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
nmreval.data.Signal
|
||||
===================
|
||||
|
||||
|
||||
.. currentmodule:: nmreval.data
|
||||
|
||||
|
||||
|
||||
|
||||
Inheritance diagram
|
||||
|
||||
.. inheritance-diagram:: Signal
|
||||
:parts: 1
|
||||
|
||||
class
|
||||
|
||||
.. autoclass:: Signal
|
||||
:special-members: __call__
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
==========
|
||||
References
|
||||
==========
|
||||
|
||||
.. toctree::
|
||||
:caption: Table of contents
|
||||
:maxdepth: 2
|
||||
:glob:
|
||||
|
||||
data.rst
|
||||
models/index.rst
|
||||
distributions/index.rst
|
||||
@@ -1,3 +1,20 @@
|
||||
"""
|
||||
====================================
|
||||
Data container (:mod:`nmreval.data`)
|
||||
====================================
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
:nosignatures:
|
||||
|
||||
Points
|
||||
Signal
|
||||
FID
|
||||
Spectrum
|
||||
BDS
|
||||
|
||||
"""
|
||||
|
||||
from .points import Points
|
||||
from .signals import Signal
|
||||
from .bds import BDS
|
||||
|
||||
+7
-6
@@ -15,10 +15,11 @@ class BDS(Signal):
|
||||
def __init__(self, x, y, **kwargs: Any):
|
||||
super().__init__(x, y, **kwargs)
|
||||
|
||||
if np.all(self._x > 0) and not np.allclose(np.diff(self._x), self._x[1]-self._x[0]):
|
||||
self.dx = self.x[1] / self.x[0]
|
||||
else:
|
||||
self.dx = self._x[1] - self._x[0]
|
||||
if len(self._x) > 1:
|
||||
if np.all(self._x > 0) and np.allclose(self._x[1:]/self._x[:-1], self._x[1]/self._x[0]):
|
||||
self.dx = self.x[1] / self.x[0]
|
||||
else:
|
||||
self.dx = self._x[1] - self._x[0]
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{self.meta['mode']}: {self.name}"
|
||||
@@ -32,10 +33,10 @@ class BDS(Signal):
|
||||
window_length (int)
|
||||
|
||||
Returns:
|
||||
Points
|
||||
Points:
|
||||
New Points instance with
|
||||
|
||||
References:
|
||||
Reference:
|
||||
Wübbenhorst, M.; van Turnhout, J.: Analysis of complex dielectric spectra.
|
||||
I. One-dimensional derivative techniques and three-dimensional modelling.
|
||||
J. Non-Cryst. Solid. 305 (2002) https://doi.org/10.1016/s0022-3093(02)01086-4
|
||||
|
||||
+33
-10
@@ -7,6 +7,9 @@ from .signals import Signal
|
||||
|
||||
|
||||
class FID(Signal):
|
||||
"""
|
||||
Extensions if :class:`Signal` for NMR timesignals
|
||||
"""
|
||||
def __init__(self, x, y, **kwargs):
|
||||
super().__init__(x, y, **kwargs)
|
||||
|
||||
@@ -16,7 +19,16 @@ class FID(Signal):
|
||||
def __repr__(self):
|
||||
return f"FID: {self.name}"
|
||||
|
||||
def baseline(self, percentage=0.12):
|
||||
def baseline(self, percentage: float = 0.12):
|
||||
"""
|
||||
Substract
|
||||
|
||||
Args:
|
||||
percentage:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
self._y -= self._y[int(-percentage * self.length):].mean()
|
||||
|
||||
return self
|
||||
@@ -51,7 +63,7 @@ class FID(Signal):
|
||||
|
||||
return self
|
||||
|
||||
def fourier(self):
|
||||
def fourier(self) -> 'Spectrum':
|
||||
ft = np.fft.fftshift(np.fft.fft(self._y)) / self.dx
|
||||
freq = np.fft.fftshift(np.fft.fftfreq(self.length, self.dx))
|
||||
|
||||
@@ -63,9 +75,15 @@ class FID(Signal):
|
||||
def fft_depake(self, scale: bool = False):
|
||||
"""
|
||||
Calculate deconvoluted Pake spectra
|
||||
M.A. McCabe, S.R. Wassail:
|
||||
Rapid deconvolution of NMR powder spectra by weighted fast Fourier transformation,
|
||||
SSNMR (1997), Vol.10, Nr.1-2, pp. 53-61,
|
||||
|
||||
Args:
|
||||
scale (bool):
|
||||
|
||||
Reference:
|
||||
M.A. McCabe, S.R. Wassail:
|
||||
Rapid deconvolution of NMR powder spectra by weighted fast Fourier transformation,
|
||||
SSNMR (1997), Vol.10, Nr.1-2, pp. 53-61
|
||||
|
||||
"""
|
||||
_y = self._y + 0
|
||||
# Perform FFT depaking
|
||||
@@ -102,8 +120,9 @@ class FID(Signal):
|
||||
self._y_err = np.std(self._y[-int(0.1*self.length):])/1.41
|
||||
return self._y_err * np.ones(self.length)
|
||||
|
||||
def shift(self, points: Union[str, float], mode: str = 'pts'):
|
||||
def shift(self, points: int, mode: str = 'pts') -> None:
|
||||
"""
|
||||
Shift y values to new x indexes
|
||||
|
||||
Args:
|
||||
points : shift value,
|
||||
@@ -113,13 +132,17 @@ class FID(Signal):
|
||||
|
||||
"""
|
||||
if mode == 'pts':
|
||||
super().shift(points)
|
||||
super().shift(int(points))
|
||||
else:
|
||||
pts = int(points//self.dx)
|
||||
super().shift(pts)
|
||||
|
||||
|
||||
class Spectrum(Signal):
|
||||
"""
|
||||
Extension of :class:`Signal` for NMR spectra
|
||||
"""
|
||||
|
||||
def __init__(self, x, y, dx=None, **kwargs):
|
||||
super().__init__(x, y, **kwargs)
|
||||
if dx is None:
|
||||
@@ -139,7 +162,7 @@ class Spectrum(Signal):
|
||||
|
||||
return self
|
||||
|
||||
def fourier(self):
|
||||
def fourier(self) -> FID:
|
||||
ft = np.fft.ifft(np.fft.ifftshift(self._y * self.dx))
|
||||
t = np.arange(len(ft))*self.dx
|
||||
shifted = np.fft.ifftshift(self._x)[0]
|
||||
@@ -172,8 +195,8 @@ class Spectrum(Signal):
|
||||
|
||||
return self._y_err * np.ones(self.length)
|
||||
|
||||
def shift(self, points, mode='pts'):
|
||||
def shift(self, points: int, mode: str = 'pts'):
|
||||
if mode == 'pts':
|
||||
super().shift(points)
|
||||
super().shift(int(points))
|
||||
else:
|
||||
return self
|
||||
|
||||
+23
-5
@@ -412,11 +412,15 @@ class Points:
|
||||
def diff(self, log: bool = False, limits: Optional[Tuple[float, float]] = None) -> PointLike:
|
||||
"""
|
||||
|
||||
Calculate first derivate :math:`dy/dx` or :math:`dy/d(\ln x)`.
|
||||
|
||||
|
||||
Args:
|
||||
log:
|
||||
limits:
|
||||
log (bool) : Flag if derivative is with respect to ln(x) instead of x. Default is `False`.
|
||||
limits (tuple, optional): Range `(xmin, xmax)` which is differentiated.
|
||||
|
||||
Returns:
|
||||
Copy of set with new y values.
|
||||
|
||||
"""
|
||||
|
||||
@@ -430,6 +434,8 @@ class Points:
|
||||
else:
|
||||
new_data.y = np.gradient(new_data.y, new_data.x)
|
||||
|
||||
new_data.remove(np.argwhere(np.isnan(new_data.y)))
|
||||
|
||||
return new_data
|
||||
|
||||
def magnitude(self) -> PointLike:
|
||||
@@ -530,6 +536,8 @@ class Points:
|
||||
|
||||
def shift(self, points: int) -> PointLike:
|
||||
"""
|
||||
Shift indexes of y values.
|
||||
|
||||
Move y values `points` indexes, remove invalid indexes and fill remaining with zeros
|
||||
Negative `points` shift to the left, positive `points` shift to the right
|
||||
|
||||
@@ -558,11 +566,21 @@ class Points:
|
||||
|
||||
return self
|
||||
|
||||
def sort(self, axis=0):
|
||||
if axis == 0:
|
||||
def sort(self, axis: str = 'x') -> PointLike:
|
||||
"""
|
||||
Sort data in increased order.
|
||||
|
||||
Args:
|
||||
axis (str, {`x`, `y`}) : Axis that determines new order. Default is `x`
|
||||
|
||||
"""
|
||||
|
||||
if axis == 'x':
|
||||
sort_order = np.argsort(self._x)
|
||||
else:
|
||||
elif axis == 'y':
|
||||
sort_order = np.argsort(self._y)
|
||||
else:
|
||||
raise ValueError('Unknown sort axis, use `x` or `y`.')
|
||||
self._x = self._x[sort_order]
|
||||
self._y = self._y[sort_order]
|
||||
self._y_err = self._y_err[sort_order]
|
||||
|
||||
@@ -6,7 +6,7 @@ from .points import Points
|
||||
|
||||
class Signal(Points):
|
||||
"""
|
||||
Extension of Points for complex y values.
|
||||
Extension of :class:`Points` for complex y values.
|
||||
"""
|
||||
|
||||
def __init__(self, x, y, **kwargs):
|
||||
@@ -18,7 +18,10 @@ class Signal(Points):
|
||||
|
||||
super().__init__(x, y, **kwargs)
|
||||
|
||||
self.dx = kwargs.get('dx', abs(self._x[1] - self._x[0]))
|
||||
if len(self._x) > 1:
|
||||
self.dx = kwargs.get('dx', abs(self._x[1] - self._x[0]))
|
||||
else:
|
||||
self.dx = 0
|
||||
self.meta.update({'phase': [], 'shift': 0})
|
||||
|
||||
def __repr__(self):
|
||||
|
||||
@@ -1,3 +1,25 @@
|
||||
"""
|
||||
=================================================================
|
||||
Distributions of correlation times (:mod:`nmreval.distributions`)
|
||||
=================================================================
|
||||
|
||||
Each class provides a distribution of correlation times, correlation
|
||||
function, spectral density, susceptibility, and calculation of different
|
||||
types of correlation times.
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
:template: autosummary/class_with_attributes.rst
|
||||
:nosignatures:
|
||||
|
||||
Debye
|
||||
ColeCole
|
||||
ColeDavidson
|
||||
HavriliakNegami
|
||||
KWW
|
||||
LogGaussian
|
||||
|
||||
"""
|
||||
|
||||
from .havriliaknegami import HavriliakNegami
|
||||
from .colecole import ColeCole
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import abc
|
||||
from warnings import warn
|
||||
from typing import Any, Union
|
||||
|
||||
|
||||
import numpy as np
|
||||
|
||||
from nmreval.lib.utils import ArrayLike
|
||||
|
||||
|
||||
class Distribution(abc.ABC):
|
||||
name = 'Abstract distribution'
|
||||
@@ -15,7 +18,12 @@ class Distribution(abc.ABC):
|
||||
|
||||
@staticmethod
|
||||
@abc.abstractmethod
|
||||
def distribution(taus, tau0, *args):
|
||||
def distribution(tau, tau0, *args):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
@abc.abstractmethod
|
||||
def correlation(t, tau, *args):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
@@ -24,13 +32,8 @@ class Distribution(abc.ABC):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def specdens(cls, omega, tau, *args):
|
||||
return -cls.susceptibility(omega, tau, *args).imag / omega
|
||||
|
||||
@staticmethod
|
||||
@abc.abstractmethod
|
||||
def correlation(t, tau0, *args):
|
||||
pass
|
||||
def specdens(cls, omega: ArrayLike, tau: ArrayLike, *args: Any) -> Union[np.ndarray, float]:
|
||||
return cls.susceptibility(omega, tau, *args).imag / omega
|
||||
|
||||
@classmethod
|
||||
def mean_value(cls, *args, mode='mean'):
|
||||
@@ -43,6 +46,27 @@ class Distribution(abc.ABC):
|
||||
|
||||
return args[0]
|
||||
|
||||
@staticmethod
|
||||
def mean(*args):
|
||||
return args[0]
|
||||
|
||||
@staticmethod
|
||||
def logmean(*args):
|
||||
"""
|
||||
Return mean logarithmic tau
|
||||
|
||||
Args:
|
||||
*args:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return np.log(args[0])
|
||||
|
||||
@staticmethod
|
||||
def max(*args):
|
||||
return args[0]
|
||||
|
||||
@classmethod
|
||||
def convert(cls, *args, from_='raw', to_='mean'):
|
||||
if from_ == to_:
|
||||
@@ -62,24 +86,3 @@ class Distribution(abc.ABC):
|
||||
return raw
|
||||
else:
|
||||
return cls.mean_value(raw, *args[1:], mode=to_)
|
||||
|
||||
@staticmethod
|
||||
def logmean(*args):
|
||||
"""
|
||||
Return mean logarithmic tau
|
||||
|
||||
Args:
|
||||
*args:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return np.log(args[0])
|
||||
|
||||
@staticmethod
|
||||
def mean(*args):
|
||||
return args[0]
|
||||
|
||||
@staticmethod
|
||||
def max(*args):
|
||||
return args[0]
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import numpy as np
|
||||
|
||||
from .base import Distribution
|
||||
from ..lib.utils import ArrayLike
|
||||
from ..lib.decorator import adjust_dims
|
||||
from ..math.mittagleffler import mlf
|
||||
|
||||
|
||||
@@ -13,7 +15,7 @@ class ColeCole(Distribution):
|
||||
bounds = [(0, 1)]
|
||||
|
||||
@staticmethod
|
||||
def distribution(taus, tau0, alpha):
|
||||
def distribution(tau, tau0, alpha):
|
||||
"""
|
||||
Distribution of correlation times
|
||||
|
||||
@@ -21,32 +23,33 @@ class ColeCole(Distribution):
|
||||
G(\ln\\tau) = \\frac{1}{2\pi} \\frac{\\sin(\\pi\\alpha)}{\cosh[\\alpha\ln(\\tau/\\tau_0)] + \cos(\\alpha \pi))}
|
||||
|
||||
Args:
|
||||
taus:
|
||||
tau:
|
||||
tau0:
|
||||
alpha:
|
||||
"""
|
||||
z = np.log(taus/tau0)
|
||||
z = np.log(tau/tau0)
|
||||
return np.sin(np.pi*alpha)/(np.cosh(z*alpha) + np.cos(alpha*np.pi))/(2*np.pi)
|
||||
|
||||
@staticmethod
|
||||
def susceptibility(omega, tau, alpha):
|
||||
"""
|
||||
Susceptibility
|
||||
@adjust_dims
|
||||
def susceptibility(omega: ArrayLike, tau: ArrayLike, alpha: float) -> ArrayLike:
|
||||
r"""
|
||||
Complex susceptibility
|
||||
|
||||
.. math::
|
||||
\chi(\omega, \\tau, \\alpha) = \\frac{1}{1-(i\omega\\tau_0)^\\alpha}
|
||||
\chi(\omega, \tau, \alpha) = \frac{1}{[1-(i\omega\tau)^\alpha]}
|
||||
|
||||
Args:
|
||||
omega:
|
||||
tau:
|
||||
alpha:
|
||||
omega (array-like): Frequency axis in 1/s (not Hz).
|
||||
tau (array-like): Correlation times :math:`\tau_\text{CC}` in s.
|
||||
alpha (float): Shape parameter.
|
||||
|
||||
"""
|
||||
_tau = np.atleast_1d(tau)
|
||||
_omega = np.atleast_1d(omega)
|
||||
val = 1/(1+(1j*_omega[:, None]*_tau[None, :])**alpha)
|
||||
return np.conjugate(val).squeeze()
|
||||
val = 1/(1+(1j*omega*tau)**alpha)
|
||||
return np.conjugate(val)
|
||||
|
||||
@staticmethod
|
||||
@adjust_dims
|
||||
def specdens(omega, tau, alpha):
|
||||
"""
|
||||
Spectral density
|
||||
@@ -59,12 +62,11 @@ class ColeCole(Distribution):
|
||||
tau:
|
||||
alpha:
|
||||
"""
|
||||
_tau = np.atleast_1d(tau)
|
||||
_omega = np.atleast_1d(omega)
|
||||
omtau = (_omega*_tau)**alpha
|
||||
omtau = (omega*tau)**alpha
|
||||
return np.sin(alpha*np.pi/2) * omtau / (1 + omtau**2 + 2*np.cos(alpha*np.pi/2)*omtau) / omega
|
||||
|
||||
@staticmethod
|
||||
@adjust_dims
|
||||
def correlation(t, tau0, alpha):
|
||||
"""
|
||||
Correlation function :math:`C(t)`
|
||||
@@ -80,26 +82,3 @@ class ColeCole(Distribution):
|
||||
alpha:
|
||||
"""
|
||||
return mlf(-(t/tau0)**alpha, alpha)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
x = np.logspace(-3, 3, num=61)
|
||||
|
||||
fig, ax = plt.subplots(2, 2)
|
||||
ax[0, 0].set_title('Distribution')
|
||||
ax[0, 1].set_title('Correlation func.')
|
||||
ax[1, 0].set_title('Spectral density')
|
||||
ax[1, 1].set_title('Susceptibility')
|
||||
|
||||
for a in [0.4, 0.6, 0.8]:
|
||||
ax[0, 0].loglog(x, ColeCole.distribution(x, 1, a))
|
||||
ax[0, 1].semilogx(x, ColeCole.correlation(x, 1, a))
|
||||
ax[1, 0].loglog(x, ColeCole.specdens(x, 1, a))
|
||||
g = ax[1, 1].loglog(x, ColeCole.susceptibility(x, 1, a).imag)
|
||||
ax[1, 1].loglog(x, ColeCole.susceptibility(x, 1, a).real, '--', color=g[0].get_color())
|
||||
|
||||
fig.tight_layout()
|
||||
|
||||
plt.show()
|
||||
|
||||
@@ -4,6 +4,8 @@ import numpy as np
|
||||
from scipy.special import psi, gammaincc
|
||||
|
||||
from .base import Distribution
|
||||
from ..lib.decorator import adjust_dims
|
||||
from ..lib.utils import ArrayLike
|
||||
from ..utils.constants import Eu
|
||||
|
||||
|
||||
@@ -16,22 +18,50 @@ class ColeDavidson(Distribution):
|
||||
bounds = [(0, 1)]
|
||||
|
||||
@staticmethod
|
||||
def susceptibility(omega, tau, gamma):
|
||||
def distribution(tau, tau0, gamma):
|
||||
"""
|
||||
Susceptibility
|
||||
Distribution of correlation times
|
||||
|
||||
.. math::
|
||||
\chi(\omega, \\tau, \\gamma) = \\frac{1}{(1-i\omega\\tau_0)^\\gamma}
|
||||
G(\ln\\tau) =
|
||||
\\begin{cases}
|
||||
\\frac{\sin(\pi\gamma)}{\pi} \\Big(\\frac{\\tau}{\\tau_0 - \\tau}\\Big)^\gamma & \\tau < \\tau_0 \\\\
|
||||
0 & \\text{else}
|
||||
\end{cases}
|
||||
|
||||
Args:
|
||||
omega:
|
||||
tau:
|
||||
tau0:
|
||||
gamma:
|
||||
"""
|
||||
|
||||
ratio = tau0/tau
|
||||
|
||||
ret_val = np.zeros_like(ratio)
|
||||
ret_val[ratio > 1] = np.sin(np.pi*gamma) / np.pi * (1 / (ratio[ratio>1] - 1))**gamma
|
||||
|
||||
return ret_val
|
||||
|
||||
@staticmethod
|
||||
@adjust_dims
|
||||
def susceptibility(omega: ArrayLike, tau: ArrayLike, gamma: float) -> ArrayLike:
|
||||
r"""
|
||||
Complex susceptibility
|
||||
|
||||
.. math::
|
||||
\chi(\omega, \tau, \alpha, \gamma) = \frac{1}{[1-i\omega\tau]^\gamma}
|
||||
|
||||
Args:
|
||||
omega (array-like): Frequency axis in 1/s (not Hz).
|
||||
tau (array-like): Correlation times in s.
|
||||
gamma (float): Shape parameter.
|
||||
|
||||
"""
|
||||
return (1/(1+1j*omega*tau)**gamma).conjugate()
|
||||
|
||||
@staticmethod
|
||||
def specdens(omega, tau,gamma):
|
||||
@adjust_dims
|
||||
def specdens(omega, tau, gamma):
|
||||
"""
|
||||
Spectral density
|
||||
|
||||
@@ -43,15 +73,12 @@ class ColeDavidson(Distribution):
|
||||
tau:
|
||||
gamma:
|
||||
"""
|
||||
gam = gamma
|
||||
_w = np.atleast_1d(omega)
|
||||
_t = np.atleast_1d(tau)
|
||||
omtau = _w[:, None] * _t[None, :]
|
||||
omtau = omega*tau
|
||||
|
||||
ret_val = np.sin(gam*np.arctan2(omtau, 1)) / (1 + omtau**2)**(gam/2.) / _w[:, None]
|
||||
ret_val[_w == 0, :] = gam*_t
|
||||
ret_val = np.sin(gamma*np.arctan2(omtau, 1)) / (1 + omtau**2)**(gamma/2.) / omega
|
||||
ret_val[np.argwhere(omega == 0)] = gamma*tau
|
||||
|
||||
return np.squeeze(ret_val)
|
||||
return ret_val
|
||||
|
||||
@staticmethod
|
||||
def mean(tau, gamma):
|
||||
@@ -113,33 +140,8 @@ class ColeDavidson(Distribution):
|
||||
return tau/np.tan(np.pi/(2*gamma+2))
|
||||
|
||||
@staticmethod
|
||||
def distribution(tau, tau0, gamma):
|
||||
"""
|
||||
Distribution of correlation times
|
||||
|
||||
.. math::
|
||||
G(\ln\\tau) =
|
||||
\\begin{cases}
|
||||
\\frac{\sin(\pi\gamma)}{\pi} \\Big(\\frac{\\tau}{\\tau_0 - \\tau}\\Big)^\gamma & \\tau < \\tau_0 \\\\
|
||||
0 & \\text{else}
|
||||
\end{cases}
|
||||
|
||||
Args:
|
||||
tau:
|
||||
tau0:
|
||||
gamma:
|
||||
"""
|
||||
|
||||
if isinstance(tau, numbers.Number):
|
||||
return np.sin(np.pi*gamma) / np.pi * (1 / (tau0/tau - 1))**gamma if tau < tau0 else 0
|
||||
|
||||
ret_val = np.zeros_like(tau)
|
||||
ret_val[tau < tau0] = np.sin(np.pi*gamma) / np.pi * (1 / (tau0/tau[tau < tau0] - 1))**gamma
|
||||
|
||||
return ret_val
|
||||
|
||||
@staticmethod
|
||||
def correlation(t, tau0, gamma):
|
||||
@adjust_dims
|
||||
def correlation(t, tau, gamma):
|
||||
r"""
|
||||
Correlation function
|
||||
|
||||
@@ -153,14 +155,14 @@ class ColeDavidson(Distribution):
|
||||
|
||||
Args:
|
||||
t:
|
||||
tau0:
|
||||
tau:
|
||||
gamma:
|
||||
|
||||
References:
|
||||
R. Hilfer: H-function representations for stretched exponential relaxation and non-Debye susceptibilities in glassy systems. Phys. Rev. E (2002) https://doi.org/10.1103/PhysRevE.65.061510
|
||||
|
||||
"""
|
||||
return gammaincc(gamma, t / tau0)
|
||||
return gammaincc(gamma, t / tau)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -3,28 +3,44 @@ import numbers
|
||||
import numpy as np
|
||||
|
||||
from .base import Distribution
|
||||
from ..lib.decorator import adjust_dims
|
||||
from ..lib.utils import ArrayLike
|
||||
|
||||
|
||||
class Debye(Distribution):
|
||||
name = 'Debye'
|
||||
|
||||
@staticmethod
|
||||
def correlation(t, tau0, *args):
|
||||
return np.exp(-t/tau0)
|
||||
def distribution(tau: ArrayLike, tau0: float) -> ArrayLike:
|
||||
if isinstance(tau, numbers.Number):
|
||||
return 1 if tau == tau0 else 0
|
||||
|
||||
@staticmethod
|
||||
def susceptibility(omega, tau0, *args):
|
||||
return 1/(1 + 1j*omega*tau0)
|
||||
|
||||
@staticmethod
|
||||
def specdens(omega, tau0, *args):
|
||||
return tau0 / (1 + (omega*tau0)**2)
|
||||
|
||||
@staticmethod
|
||||
def distribution(taus, tau0, *args):
|
||||
if isinstance(taus, numbers.Number):
|
||||
return 1 if taus == tau0 else 0
|
||||
|
||||
ret_val = np.zeros_like(taus)
|
||||
ret_val[np.argmin(abs(taus - tau0))] = 1
|
||||
ret_val = np.zeros_like(tau)
|
||||
ret_val[tau == tau0] = 1.
|
||||
return ret_val
|
||||
|
||||
@staticmethod
|
||||
@adjust_dims
|
||||
def correlation(t, tau, *args):
|
||||
return np.exp(-t/tau)
|
||||
|
||||
@staticmethod
|
||||
@adjust_dims
|
||||
def susceptibility(omega: ArrayLike, tau: ArrayLike) -> ArrayLike:
|
||||
r"""
|
||||
Complex susceptibility
|
||||
|
||||
.. math::
|
||||
\chi(\omega, \tau) = \frac{1}{1-i\omega\tau}
|
||||
|
||||
Args:
|
||||
omega (array-like): Frequency axis in 1/s (not Hz).
|
||||
tau (array-like): Correlation times in s.
|
||||
|
||||
"""
|
||||
return 1/(1 - 1j*omega*tau)
|
||||
|
||||
@staticmethod
|
||||
@adjust_dims
|
||||
def specdens(omega: ArrayLike, tau: ArrayLike) -> ArrayLike:
|
||||
return tau / (1 + (omega*tau)**2)
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
from typing import Union
|
||||
|
||||
import numpy as np
|
||||
from scipy.special import psi
|
||||
|
||||
from .base import Distribution
|
||||
from ..lib.utils import ArrayLike
|
||||
from ..lib.decorator import adjust_dims
|
||||
from ..math.mittagleffler import mlf
|
||||
from ..utils import Eu
|
||||
|
||||
|
||||
class HavriliakNegami(Distribution):
|
||||
"""
|
||||
Functions based on Cole-Davidson distribution
|
||||
Functions based on Havriliak-Negami distribution
|
||||
"""
|
||||
|
||||
name = 'Havriliak-Negami'
|
||||
@@ -16,51 +20,31 @@ class HavriliakNegami(Distribution):
|
||||
bounds = [(0, 1), (0, 1)]
|
||||
|
||||
@staticmethod
|
||||
def correlation(t, tau0, alpha, gamma):
|
||||
def distribution(tau: ArrayLike, tau0: float, alpha: float, gamma: float) -> ArrayLike:
|
||||
r"""
|
||||
Correlation function
|
||||
Distribution of correlation times :math:`G(\ln\tau)`.
|
||||
|
||||
.. math::
|
||||
C(t, \tau_0, \alpha, \gamma) = 1 - \left(\frac{t}{\tau_0}\right)^{\alpha\gamma} \text{E}_{\alpha,\alpha\gamma+1}^\gamma\left[-\left(\frac{t}{\tau_0}\right)^\alpha\right]
|
||||
|
||||
with incomplete Gamma function
|
||||
|
||||
.. math::
|
||||
\Gamma(\gamma, t/\tau_0) = \frac{1}{\Gamma(\gamma)}\int_{t/\tau_0}^\infty x^{\gamma-1}\text{e}^{-x}\,\mathrm{d}x
|
||||
G(\ln\tau) = \frac{(\tau/\tau_0)^{\alpha\gamma}
|
||||
\sin\left\lbrace\gamma\arctan\left[\frac{\sin\alpha\pi}{(\tau/\tau_0)^\alpha + \cos\alpha\pi}\right] \right\rbrace}{\pi\left[1+2(\tau/\tau_0)^\alpha\cos\alpha\pi + (\tau/\tau_0)^{2\gamma}\right]}
|
||||
|
||||
Args:
|
||||
t:
|
||||
tau0:
|
||||
tau (array_like) :
|
||||
tau0 (array-like) :
|
||||
alpha:
|
||||
gamma:
|
||||
|
||||
References:
|
||||
R. Hilfer: H-function representations for stretched exponential relaxation and non-Debye susceptibilities in glassy systems. Phys. Rev. E (2002) https://doi.org/10.1103/PhysRevE.65.061510
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return 1 - (t/tau0)**(alpha*gamma) * mlf(-(t/tau0)**alpha, alpha, alpha*gamma+1, gamma)
|
||||
|
||||
@staticmethod
|
||||
def susceptibility(omega, tau, alpha, gamma):
|
||||
return np.conjugate(1/(1 + (1j*omega[:, None]*tau[None, :])**alpha)**gamma).squeeze()
|
||||
|
||||
@staticmethod
|
||||
def specdens(omega, tau, alpha, gamma):
|
||||
omtau = (omega[:, None]*tau[None, :])**alpha
|
||||
|
||||
zaehler = np.sin(gamma * np.arctan2(omtau*np.sin(0.5*alpha*np.pi), 1 + omtau*np.cos(0.5*alpha*np.pi)))
|
||||
nenner = (1 + 2*omtau * np.cos(0.5*alpha*np.pi) + omtau**2)**(0.5*gamma)
|
||||
|
||||
return ((1 / omega) * (zaehler/nenner)).squeeze()
|
||||
|
||||
@staticmethod
|
||||
def distribution(tau, tau0, alpha, gamma):
|
||||
if alpha == 1:
|
||||
from .coledavidson import ColeDavidson
|
||||
return ColeDavidson.distribution(tau, tau0, gamma)
|
||||
|
||||
elif gamma == 1:
|
||||
from .colecole import ColeCole
|
||||
return ColeCole.distribution(tau, tau0, alpha)
|
||||
|
||||
else:
|
||||
_y = tau/tau0
|
||||
om_y = (1 + 2*np.cos(np.pi*alpha)*(_y**alpha) + _y**(2*alpha))**(-gamma/2)
|
||||
@@ -69,11 +53,104 @@ class HavriliakNegami(Distribution):
|
||||
return np.sin(gamma*theta_y) * om_y * (_y**(alpha*gamma)) / np.pi
|
||||
|
||||
@staticmethod
|
||||
def max(tau, alpha, gamma):
|
||||
return tau*(np.sin(0.5*np.pi*alpha*gamma/(gamma+1)) / np.sin(0.5*np.pi*alpha/(gamma+1)))**(1/alpha)
|
||||
@adjust_dims
|
||||
def correlation(t: ArrayLike, tau: ArrayLike, alpha: float, gamma: float) -> ArrayLike:
|
||||
r"""
|
||||
Correlation function
|
||||
|
||||
.. math::
|
||||
C(t, \tau_\text{HN}, \alpha, \gamma) = 1 - \left(\frac{t}{\tau_\text{HN}}\right)^{\alpha\gamma}
|
||||
\text{E}_{\alpha,\alpha\gamma+1}^\gamma\left[-\left(\frac{t}{\tau_\text{HN}}\right)^\alpha\right]
|
||||
|
||||
with three-parameter Mittag-Leffler function
|
||||
|
||||
.. math::
|
||||
\text{E}_{a,b}^g (z) = \frac{1}{\Gamma(g)} \sum_{k=0}^\infty \frac{\Gamma(g+k) z^k}{k!\Gamma(ak+b)}
|
||||
|
||||
Args:
|
||||
t (array-like): Time axis in s.
|
||||
tau (array-like): Correlation times :math:`\tau_\text{HN}` in s.
|
||||
alpha (float): Cole-Cole shape parameter.
|
||||
gamma (float): Cole-Davidson shape parameter.
|
||||
|
||||
References:
|
||||
R. Garrappa: Models of dielectric relaxation based on completely monotone functions.
|
||||
Frac. Calc Appl. Anal. 19 (2016) https://arxiv.org/abs/1611.04028
|
||||
|
||||
"""
|
||||
return 1 - (t/tau)**(alpha*gamma) * mlf(-(t/tau)**alpha, alpha, alpha*gamma+1, gamma)
|
||||
|
||||
@staticmethod
|
||||
def logmean(tau, alpha, gamma):
|
||||
@adjust_dims
|
||||
def susceptibility(omega: ArrayLike, tau: ArrayLike, alpha: float, gamma: float) -> Union[np.ndarray, complex]:
|
||||
r"""
|
||||
Complex susceptibility
|
||||
|
||||
.. math::
|
||||
\chi(\omega, \tau, \alpha, \gamma) = \frac{1}{[1-(i\omega\tau)^\alpha]^\gamma}
|
||||
|
||||
Args:
|
||||
omega (array-like): Frequency axis in 1/s (not Hz).
|
||||
tau (array-like): Correlation times in s.
|
||||
alpha (float): Shape parameter.
|
||||
gamma (float): Even more shape parameter.
|
||||
|
||||
"""
|
||||
return np.conjugate(1/(1 + (1j*omega*tau)**alpha)**gamma)
|
||||
|
||||
@staticmethod
|
||||
@adjust_dims
|
||||
def specdens(omega: ArrayLike, tau: ArrayLike, alpha: float, gamma: float) -> Union[np.ndarray, float]:
|
||||
r"""
|
||||
Spectral density :math:`J(\omega)`
|
||||
|
||||
.. math::
|
||||
J(\omega) = \frac{\sin\left\lbrace \gamma\arctan\left[ \frac{(\omega\tau)^\alpha\sin(\alpha\pi/2)}{1+(\omega\tau)^\alpha\cos(\alpha\pi/2)} \right] \right\rbrace}
|
||||
{\omega \left[ 1+(\omega\tau)^\alpha \cos(\alpha\pi/2) + (\omega\tau)^{2\alpha} \right]^{\gamma/2}}
|
||||
|
||||
Args:
|
||||
omega (array-like): Frequency axis in 1/s (not Hz).
|
||||
tau (array-like): Correlation times in s.
|
||||
alpha (float): Cole-Cole shape parameter.
|
||||
gamma (float): Cole-Davidson shape parameter.
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
omtau = (omega*tau)**alpha
|
||||
|
||||
zaehler = np.sin(gamma * np.arctan2(omtau*np.sin(0.5*alpha*np.pi), 1 + omtau*np.cos(0.5*alpha*np.pi)))
|
||||
nenner = (1 + 2*omtau * np.cos(0.5*alpha*np.pi) + omtau**2)**(0.5*gamma)
|
||||
|
||||
result = (1 / omega) * (zaehler/nenner)
|
||||
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def mean(tau: ArrayLike, alpha: float, gamma: float) -> Union[np.ndarray, float]:
|
||||
r"""
|
||||
|
||||
.. math::
|
||||
\langle \tau \rangle \approx \tau_\text{HN} \alpha \gamma
|
||||
|
||||
This is an approximation given in `[1]`_
|
||||
|
||||
Args:
|
||||
tau (array-like): Function parameter in s.
|
||||
alpha (float): Shape parameter.
|
||||
gamma (float): Even more shape parameter.
|
||||
|
||||
Reference:
|
||||
_`[1]` Bauer, Th., Köhler, M., Lunkenheimer, P., Angell, C.A.:
|
||||
Relaxation dynamics and ionic conductivity in a fragile plastic crystal.
|
||||
J. Chem. Phys. 133, 144509 (2010). https://doi.org/10.1063/1.3487521
|
||||
|
||||
|
||||
"""
|
||||
return tau * alpha*gamma
|
||||
|
||||
@staticmethod
|
||||
def logmean(tau: ArrayLike, alpha: float, gamma: float) -> ArrayLike:
|
||||
r"""
|
||||
Calculate mean logarithm of tau
|
||||
|
||||
@@ -82,25 +159,12 @@ class HavriliakNegami(Distribution):
|
||||
|
||||
Args:
|
||||
tau:
|
||||
alpha:
|
||||
gamma:
|
||||
|
||||
Returns:
|
||||
alpha (float):
|
||||
gamma (float):
|
||||
|
||||
"""
|
||||
return np.log(tau) + (psi(gamma)+Eu)/alpha
|
||||
|
||||
@staticmethod
|
||||
def mean(tau, alpha, gamma):
|
||||
# approximation according to Th. Bauer et al., J. Chem. Phys. 133, 144509 (2010).
|
||||
return tau * alpha*gamma
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
x = np.logspace(-7, 3)
|
||||
y = HavriliakNegami.correlation(x, 1, 0.8, 0.26)
|
||||
|
||||
plt.semilogx(x, y)
|
||||
plt.show()
|
||||
def max(tau: ArrayLike, alpha: float, gamma: float) -> ArrayLike:
|
||||
return tau*(np.sin(0.5*np.pi*alpha*gamma/(gamma+1)) / np.sin(0.5*np.pi*alpha/(gamma+1)))**(1/alpha)
|
||||
|
||||
@@ -2,6 +2,7 @@ import numpy as np
|
||||
from scipy.interpolate import interp1d
|
||||
from scipy.special import gamma
|
||||
|
||||
from nmreval.lib.decorator import adjust_dims
|
||||
from .base import Distribution
|
||||
from ..math.kww import kww_cos, kww_sin
|
||||
from ..utils.constants import Eu
|
||||
@@ -13,52 +14,52 @@ class KWW(Distribution):
|
||||
boounds = [(0, 1)]
|
||||
|
||||
@staticmethod
|
||||
def distribution(taus, tau, *args):
|
||||
b = args[0]
|
||||
assert 0.1 <= b <= 0.9
|
||||
def distribution(taus, tau, beta):
|
||||
if not (0.1 <= beta <= 0.9):
|
||||
raise ValueError('KWW distribution is only defined between 0.1 and 0.9')
|
||||
b_supp = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
|
||||
B_supp = [0.145, 0.197, 0.243, 0.285, 0.382, 0.306, 0.360, 0.435, 0.700]
|
||||
C_supp = [0.89, 0.50, 0.35, 0.25, 0, 0.13, 0.22, 0.4015, 0.32]
|
||||
|
||||
B = interp1d(b_supp, B_supp)(b)
|
||||
C = interp1d(b_supp, C_supp)(b)
|
||||
B = interp1d(b_supp, B_supp)(beta)
|
||||
C = interp1d(b_supp, C_supp)(beta)
|
||||
|
||||
tt = tau/taus
|
||||
|
||||
delta = b * abs(b-0.5) / (1-b)
|
||||
if b > 0.5:
|
||||
delta = beta * abs(beta-0.5) / (1-beta)
|
||||
if beta > 0.5:
|
||||
f = 1 + C * tt**delta
|
||||
else:
|
||||
f = 1 / (1 + C * tt**delta)
|
||||
ret_val = tau * B * np.exp(-(1-b) * b**(b/(1-b)) / tt**(b/(1-b))) / tt**((1-0.5*b)/(1-b))
|
||||
ret_val = tau * B * np.exp(-(1-beta) * beta**(beta/(1-beta)) / tt**(beta/(1-beta))) / tt**((1-0.5*beta)/(1-beta))
|
||||
|
||||
return ret_val * f / taus
|
||||
|
||||
@staticmethod
|
||||
def correlation(t, tau, *args):
|
||||
return np.exp(-(t/tau)**args[0])
|
||||
@adjust_dims
|
||||
def correlation(t, tau, beta):
|
||||
return np.exp(-(t/tau)**beta)
|
||||
|
||||
@staticmethod
|
||||
def susceptibility(omega, tau, *args):
|
||||
return 1-omega*kww_sin(omega, tau, args[0]) + 1j*omega*kww_cos(omega, tau, args[0])
|
||||
@adjust_dims
|
||||
def susceptibility(omega, tau, beta):
|
||||
return 1-omega*kww_sin(omega, tau, beta) + 1j*omega*kww_cos(omega, tau, beta)
|
||||
|
||||
@staticmethod
|
||||
def specdens(omega, tau, *args):
|
||||
return kww_cos(omega, tau, args[0])
|
||||
@adjust_dims
|
||||
def specdens(omega, tau, beta):
|
||||
return kww_cos(omega, tau, beta)
|
||||
|
||||
@staticmethod
|
||||
def mean(*args):
|
||||
tau, beta = args
|
||||
def mean(tau, beta):
|
||||
return tau/beta * gamma(1 / beta)
|
||||
|
||||
@staticmethod
|
||||
def logmean(*args):
|
||||
tau, beta = args
|
||||
def logmean(tau, beta):
|
||||
return (1-1/beta) * Eu + np.log(tau)
|
||||
|
||||
@staticmethod
|
||||
def max(*args):
|
||||
tau, beta = args
|
||||
def max(tau, beta):
|
||||
return (1.7851 - 0.87052*beta - 0.028836*beta**2 + 0.11391*beta**3) * tau
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@ from multiprocessing import Pool, cpu_count
|
||||
from itertools import product
|
||||
|
||||
import numpy as np
|
||||
|
||||
from nmreval.lib.decorator import adjust_dims
|
||||
|
||||
try:
|
||||
from scipy.integrate import simpson
|
||||
except ImportError:
|
||||
@@ -25,8 +28,7 @@ class LogGaussian(Distribution):
|
||||
return np.exp(-0.5*(np.log(tau/tau0)/sigma)**2)/np.sqrt(2*np.pi)/sigma
|
||||
|
||||
@staticmethod
|
||||
def correlation(t, tau0, *args):
|
||||
sigma = args[0]
|
||||
def correlation(t, tau0, sigma: float):
|
||||
_t = np.atleast_1d(t)
|
||||
_tau = np.atleast_1d(tau0)
|
||||
|
||||
@@ -40,8 +42,7 @@ class LogGaussian(Distribution):
|
||||
return ret_val.squeeze()
|
||||
|
||||
@staticmethod
|
||||
def susceptibility(omega, tau0, *args):
|
||||
sigma = args[0]
|
||||
def susceptibility(omega, tau0, sigma: float):
|
||||
_omega = np.atleast_1d(omega)
|
||||
_tau = np.atleast_1d(tau0)
|
||||
|
||||
@@ -56,8 +57,7 @@ class LogGaussian(Distribution):
|
||||
return ret_val.squeeze()
|
||||
|
||||
@staticmethod
|
||||
def specdens(omega, tau0, *args):
|
||||
sigma = args[0]
|
||||
def specdens(omega, tau0, sigma):
|
||||
_omega = np.atleast_1d(omega)
|
||||
_tau = np.atleast_1d(tau0)
|
||||
|
||||
@@ -78,7 +78,7 @@ class LogGaussian(Distribution):
|
||||
|
||||
def _integrate_process_1(args):
|
||||
omega_i, tau_j, sigma = args
|
||||
area = quad(_integrand_freq_imag_high, 0, 50, args=(omega_i, tau_j, sigma))[0]
|
||||
area, _ = quad(_integrand_freq_imag_high, 0, 50, args=(omega_i, tau_j, sigma))
|
||||
area += quad(_integrand_freq_imag_low, -50, 0, args=(omega_i, tau_j, sigma))[0]
|
||||
|
||||
return area
|
||||
@@ -102,7 +102,9 @@ def _integrand_time(u, t, tau, sigma):
|
||||
return LogGaussian.distribution(uu, tau, sigma) * np.exp(-t/uu)
|
||||
|
||||
|
||||
# integrands
|
||||
def _integrand_freq_imag_low(u, omega, tau, sigma):
|
||||
# integrand
|
||||
uu = np.exp(u)
|
||||
return LogGaussian.distribution(uu, tau, sigma) * omega * uu / (1 + (omega*uu)**2)
|
||||
|
||||
|
||||
@@ -2,14 +2,12 @@
|
||||
|
||||
# Form implementation generated from reading ui file 'resources/_ui/basewindow.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.12.3
|
||||
# Created by: PyQt5 UI code generator 5.9.2
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_BaseWindow(object):
|
||||
def setupUi(self, BaseWindow):
|
||||
BaseWindow.setObjectName("BaseWindow")
|
||||
@@ -461,6 +459,9 @@ class Ui_BaseWindow(object):
|
||||
self.toolBar.addAction(self.actionSave)
|
||||
self.toolBar.addSeparator()
|
||||
self.toolBar.addAction(self.actionMouse_behaviour)
|
||||
self.toolBar.addSeparator()
|
||||
self.toolBar.addAction(self.actionPrevious)
|
||||
self.toolBar.addAction(self.actionNext_window)
|
||||
self.toolbar_edit.addAction(self.action_calc)
|
||||
self.toolbar_edit.addAction(self.action_mean_t1)
|
||||
self.toolbar_edit.addAction(self.actionShift)
|
||||
@@ -598,6 +599,7 @@ class Ui_BaseWindow(object):
|
||||
self.actionLife.setText(_translate("BaseWindow", "Life..."))
|
||||
self.actionTetris.setText(_translate("BaseWindow", "Not Tetris"))
|
||||
self.actionUpdate.setText(_translate("BaseWindow", "Look for updates"))
|
||||
|
||||
from ..data.datawidget.datawidget import DataWidget
|
||||
from ..data.point_select import PointSelectWidget
|
||||
from ..data.signaledit.editsignalwidget import EditSignalWidget
|
||||
|
||||
@@ -2,19 +2,19 @@
|
||||
|
||||
# Form implementation generated from reading ui file 'resources/_ui/bdsdialog.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.12.3
|
||||
# Created by: PyQt5 UI code generator 5.9.2
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_Dialog(object):
|
||||
def setupUi(self, Dialog):
|
||||
Dialog.setObjectName("Dialog")
|
||||
Dialog.resize(400, 319)
|
||||
Dialog.resize(544, 443)
|
||||
self.gridLayout = QtWidgets.QGridLayout(Dialog)
|
||||
self.gridLayout.setContentsMargins(3, 3, 3, 3)
|
||||
self.gridLayout.setSpacing(3)
|
||||
self.gridLayout.setObjectName("gridLayout")
|
||||
self.listWidget = QtWidgets.QListWidget(Dialog)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding)
|
||||
@@ -23,7 +23,66 @@ class Ui_Dialog(object):
|
||||
sizePolicy.setHeightForWidth(self.listWidget.sizePolicy().hasHeightForWidth())
|
||||
self.listWidget.setSizePolicy(sizePolicy)
|
||||
self.listWidget.setObjectName("listWidget")
|
||||
self.gridLayout.addWidget(self.listWidget, 1, 0, 1, 1)
|
||||
self.gridLayout.addWidget(self.listWidget, 1, 0, 2, 1)
|
||||
self.groupBox_2 = QtWidgets.QGroupBox(Dialog)
|
||||
self.groupBox_2.setObjectName("groupBox_2")
|
||||
self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.groupBox_2)
|
||||
self.verticalLayout_3.setContentsMargins(3, 3, 3, 3)
|
||||
self.verticalLayout_3.setSpacing(3)
|
||||
self.verticalLayout_3.setObjectName("verticalLayout_3")
|
||||
self.freq_button = QtWidgets.QRadioButton(self.groupBox_2)
|
||||
self.freq_button.setChecked(True)
|
||||
self.freq_button.setObjectName("freq_button")
|
||||
self.buttonGroup = QtWidgets.QButtonGroup(Dialog)
|
||||
self.buttonGroup.setObjectName("buttonGroup")
|
||||
self.buttonGroup.addButton(self.freq_button)
|
||||
self.verticalLayout_3.addWidget(self.freq_button)
|
||||
self.temp_button = QtWidgets.QRadioButton(self.groupBox_2)
|
||||
self.temp_button.setObjectName("temp_button")
|
||||
self.buttonGroup.addButton(self.temp_button)
|
||||
self.verticalLayout_3.addWidget(self.temp_button)
|
||||
self.gridLayout.addWidget(self.groupBox_2, 1, 1, 1, 1)
|
||||
self.groupBox = QtWidgets.QGroupBox(Dialog)
|
||||
self.groupBox.setObjectName("groupBox")
|
||||
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.groupBox)
|
||||
self.verticalLayout_2.setContentsMargins(3, 3, 3, 3)
|
||||
self.verticalLayout_2.setSpacing(3)
|
||||
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
||||
self.eps_checkBox = QtWidgets.QCheckBox(self.groupBox)
|
||||
self.eps_checkBox.setChecked(True)
|
||||
self.eps_checkBox.setObjectName("eps_checkBox")
|
||||
self.verticalLayout_2.addWidget(self.eps_checkBox)
|
||||
self.modul_checkBox = QtWidgets.QCheckBox(self.groupBox)
|
||||
self.modul_checkBox.setObjectName("modul_checkBox")
|
||||
self.verticalLayout_2.addWidget(self.modul_checkBox)
|
||||
self.cond_checkBox = QtWidgets.QCheckBox(self.groupBox)
|
||||
self.cond_checkBox.setObjectName("cond_checkBox")
|
||||
self.verticalLayout_2.addWidget(self.cond_checkBox)
|
||||
self.loss_checkBox = QtWidgets.QCheckBox(self.groupBox)
|
||||
self.loss_checkBox.setObjectName("loss_checkBox")
|
||||
self.verticalLayout_2.addWidget(self.loss_checkBox)
|
||||
self.line = QtWidgets.QFrame(self.groupBox)
|
||||
self.line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
self.line.setObjectName("line")
|
||||
self.verticalLayout_2.addWidget(self.line)
|
||||
self.cap_checkBox = QtWidgets.QCheckBox(self.groupBox)
|
||||
self.cap_checkBox.setObjectName("cap_checkBox")
|
||||
self.verticalLayout_2.addWidget(self.cap_checkBox)
|
||||
self.temp_checkBox = QtWidgets.QCheckBox(self.groupBox)
|
||||
self.temp_checkBox.setObjectName("temp_checkBox")
|
||||
self.verticalLayout_2.addWidget(self.temp_checkBox)
|
||||
self.time_checkBox = QtWidgets.QCheckBox(self.groupBox)
|
||||
self.time_checkBox.setObjectName("time_checkBox")
|
||||
self.verticalLayout_2.addWidget(self.time_checkBox)
|
||||
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.verticalLayout_2.addItem(spacerItem)
|
||||
self.gridLayout.addWidget(self.groupBox, 2, 1, 1, 1)
|
||||
self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
|
||||
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
|
||||
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
|
||||
self.buttonBox.setObjectName("buttonBox")
|
||||
self.gridLayout.addWidget(self.buttonBox, 3, 0, 1, 2)
|
||||
self.label = QtWidgets.QLabel(Dialog)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
@@ -31,46 +90,31 @@ class Ui_Dialog(object):
|
||||
sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
|
||||
self.label.setSizePolicy(sizePolicy)
|
||||
self.label.setObjectName("label")
|
||||
self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
|
||||
self.label_2 = QtWidgets.QLabel(Dialog)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.label_2.sizePolicy().hasHeightForWidth())
|
||||
self.label_2.setSizePolicy(sizePolicy)
|
||||
self.label_2.setObjectName("label_2")
|
||||
self.gridLayout.addWidget(self.label_2, 0, 1, 1, 1)
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout()
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.eps_checkBox = QtWidgets.QCheckBox(Dialog)
|
||||
self.eps_checkBox.setChecked(True)
|
||||
self.eps_checkBox.setObjectName("eps_checkBox")
|
||||
self.verticalLayout.addWidget(self.eps_checkBox)
|
||||
self.modul_checkBox = QtWidgets.QCheckBox(Dialog)
|
||||
self.modul_checkBox.setObjectName("modul_checkBox")
|
||||
self.verticalLayout.addWidget(self.modul_checkBox)
|
||||
self.cond_checkBox = QtWidgets.QCheckBox(Dialog)
|
||||
self.cond_checkBox.setObjectName("cond_checkBox")
|
||||
self.verticalLayout.addWidget(self.cond_checkBox)
|
||||
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.verticalLayout.addItem(spacerItem)
|
||||
self.gridLayout.addLayout(self.verticalLayout, 1, 1, 1, 1)
|
||||
self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
|
||||
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
|
||||
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
|
||||
self.buttonBox.setObjectName("buttonBox")
|
||||
self.gridLayout.addWidget(self.buttonBox, 2, 0, 1, 2)
|
||||
self.gridLayout.addWidget(self.label, 0, 0, 1, 2)
|
||||
|
||||
self.retranslateUi(Dialog)
|
||||
self.buttonBox.accepted.connect(Dialog.accept)
|
||||
self.buttonBox.rejected.connect(Dialog.reject)
|
||||
QtCore.QMetaObject.connectSlotsByName(Dialog)
|
||||
Dialog.setTabOrder(self.freq_button, self.temp_button)
|
||||
Dialog.setTabOrder(self.temp_button, self.eps_checkBox)
|
||||
Dialog.setTabOrder(self.eps_checkBox, self.modul_checkBox)
|
||||
Dialog.setTabOrder(self.modul_checkBox, self.cond_checkBox)
|
||||
Dialog.setTabOrder(self.cond_checkBox, self.listWidget)
|
||||
|
||||
def retranslateUi(self, Dialog):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
Dialog.setWindowTitle(_translate("Dialog", "Read BDS data"))
|
||||
self.label.setText(_translate("Dialog", "Found temperatures"))
|
||||
self.label_2.setText(_translate("Dialog", "Read as:"))
|
||||
self.groupBox_2.setTitle(_translate("Dialog", "X Axis"))
|
||||
self.freq_button.setText(_translate("Dialog", "Frequency"))
|
||||
self.temp_button.setText(_translate("Dialog", "Temperature"))
|
||||
self.groupBox.setTitle(_translate("Dialog", "Y Axis"))
|
||||
self.eps_checkBox.setText(_translate("Dialog", "Permittivity ε"))
|
||||
self.modul_checkBox.setText(_translate("Dialog", "Modulus 1/ε"))
|
||||
self.cond_checkBox.setText(_translate("Dialog", "Conductivity iεω"))
|
||||
self.loss_checkBox.setText(_translate("Dialog", "Loss factor tan(δ)"))
|
||||
self.cap_checkBox.setText(_translate("Dialog", "Capacity"))
|
||||
self.temp_checkBox.setText(_translate("Dialog", "Meas. temperature"))
|
||||
self.time_checkBox.setText(_translate("Dialog", "Meas. time"))
|
||||
self.label.setText(_translate("Dialog", "Found entries"))
|
||||
|
||||
|
||||
@@ -599,10 +599,13 @@ class SignalContainer(ExperimentContainer):
|
||||
|
||||
if isinstance(self._data, BDS):
|
||||
self.mode = 'bds'
|
||||
|
||||
if sym_kwargs['symbol'] is None and line_kwargs['style'] is None:
|
||||
sym_kwargs['symbol'] = next(PointContainer.symbols)
|
||||
line_kwargs['style'] = LineStyle.No
|
||||
if len(self._data) <= 91:
|
||||
sym_kwargs['symbol'] = next(PointContainer.symbols)
|
||||
line_kwargs['style'] = LineStyle.No
|
||||
else:
|
||||
line_kwargs['style'] = LineStyle.Solid
|
||||
sym_kwargs['symbol'] = SymbolStyle.No
|
||||
|
||||
elif isinstance(self._data, Signal):
|
||||
if line_kwargs['style'] is None and sym_kwargs['symbol'] is None:
|
||||
@@ -617,7 +620,7 @@ class SignalContainer(ExperimentContainer):
|
||||
raise TypeError('Unknown class %s, should be FID, Spectrum, or BDS.' % type(self._data))
|
||||
|
||||
for mode in ['real', 'imag']:
|
||||
if mode == 'imag':
|
||||
if mode == 'imag' and line_kwargs['style'] != LineStyle.No:
|
||||
line_kwargs['style'] = LineStyle.Dashed
|
||||
self.setSymbol(mode=mode, **sym_kwargs)
|
||||
self.setLine(mode=mode, **line_kwargs)
|
||||
|
||||
@@ -591,6 +591,8 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
|
||||
if item_dic:
|
||||
dic['items'].append(item_dic)
|
||||
|
||||
print(dic)
|
||||
|
||||
return dic
|
||||
|
||||
def get_state(self) -> dict:
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from ...io.bds_reader import BDSReader
|
||||
|
||||
from ..Qt import QtCore, QtWidgets
|
||||
from .._py.bdsdialog import Ui_Dialog
|
||||
from nmreval.io.bds_reader import BDSReader
|
||||
from nmreval.gui_qt.Qt import QtCore, QtWidgets
|
||||
from nmreval.gui_qt._py.bdsdialog import Ui_Dialog
|
||||
|
||||
|
||||
class QBDSReader(QtWidgets.QDialog, Ui_Dialog):
|
||||
@@ -11,22 +10,39 @@ class QBDSReader(QtWidgets.QDialog, Ui_Dialog):
|
||||
def __init__(self, fname: str = None, parent=None):
|
||||
super().__init__(parent=parent)
|
||||
self.setupUi(self)
|
||||
self.reader = None
|
||||
self.fname = fname
|
||||
|
||||
if fname is not None:
|
||||
if self.fname is not None:
|
||||
self.reader = BDSReader(fname)
|
||||
self.setup_gui()
|
||||
|
||||
def __call__(self, fname: str):
|
||||
self.reader = BDSReader(fname)
|
||||
self.fname = fname
|
||||
self.setup_gui()
|
||||
|
||||
return self
|
||||
|
||||
def setup_gui(self):
|
||||
self.listWidget.clear()
|
||||
self.label.setText(f'Found entries for {self.reader.fname.name}')
|
||||
|
||||
for temp in self.reader.set_temp:
|
||||
item = QtWidgets.QListWidgetItem(str(temp))
|
||||
self.make_list(True)
|
||||
|
||||
@QtCore.pyqtSlot(QtWidgets.QAbstractButton, name='on_buttonGroup_buttonClicked')
|
||||
def change_list(self, bttn: QtWidgets.QAbstractButton):
|
||||
self.make_list(bttn == self.freq_button)
|
||||
|
||||
def make_list(self, use_freq: bool) -> None:
|
||||
self.listWidget.clear()
|
||||
if use_freq:
|
||||
secondary = self.reader.set_temp
|
||||
else:
|
||||
secondary = self.reader.frequencies
|
||||
|
||||
for x2 in secondary:
|
||||
item = QtWidgets.QListWidgetItem(f'{x2:.8g}')
|
||||
item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsSelectable)
|
||||
item.setCheckState(QtCore.Qt.Checked)
|
||||
self.listWidget.addItem(item)
|
||||
@@ -34,19 +50,26 @@ class QBDSReader(QtWidgets.QDialog, Ui_Dialog):
|
||||
def accept(self):
|
||||
data = []
|
||||
|
||||
temps = []
|
||||
x2 = []
|
||||
for i in range(self.listWidget.count()):
|
||||
item = self.listWidget.item(i)
|
||||
if item.checkState() == QtCore.Qt.Checked:
|
||||
temps.append(i)
|
||||
x2.append(i)
|
||||
|
||||
for (mode, cb) in [('epsilon', self.eps_checkBox),
|
||||
('sigma', self.cond_checkBox),
|
||||
('modulus', self.modul_checkBox)]:
|
||||
xmode = 'freq' if self.freq_button.isChecked() else 'temp'
|
||||
|
||||
for (mode, cb) in [
|
||||
('epsilon', self.eps_checkBox),
|
||||
('sigma', self.cond_checkBox),
|
||||
('modulus', self.modul_checkBox),
|
||||
('capacity', self.cap_checkBox),
|
||||
('time', self.time_checkBox),
|
||||
('sample_temp', self.temp_checkBox),
|
||||
('loss_factor', self.loss_checkBox)
|
||||
]:
|
||||
if cb.checkState() == QtCore.Qt.Checked:
|
||||
values = self.reader.export(mode=mode)
|
||||
for t in temps:
|
||||
values = self.reader.export(ymode=mode, xmode=xmode)
|
||||
for t in x2:
|
||||
data.append(values[t])
|
||||
|
||||
self.data_read.emit(data)
|
||||
@@ -60,7 +83,8 @@ if __name__ == '__main__':
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
|
||||
reader = QBDSReader()
|
||||
reader('/autohome/dominik/nmreval/testdata/ZWEITECHANCE_EGD4H2O.EPS')
|
||||
# reader('/autohome/dominik/nmreval/testdata/ZWEITECHANCE_EGD4H2O.EPS')
|
||||
# reader('/autohome/dominik/nmreval/testdata/MZ-ME/CHLOROFORM_2017-02-06.EPS')
|
||||
reader('/autohome/dominik/nmreval/testdata/SBA_5P4NM_ISOCHRON_150-300K_18-03-22.EPS')
|
||||
reader.show()
|
||||
sys.exit(app.exec())
|
||||
|
||||
|
||||
@@ -127,13 +127,13 @@ def pgitem_to_dic(item):
|
||||
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])
|
||||
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])
|
||||
item_dic['linecolor'] = Colors.from_rgb(*pen.color().getRgbF()[:3], normed=True)
|
||||
item_dic['linewidth'] = pen.widthF()
|
||||
else:
|
||||
item_dic['linestyle'] = LineStyle.No
|
||||
|
||||
@@ -186,10 +186,7 @@ class SelectionWidget(QtWidgets.QWidget):
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
try:
|
||||
return float(self.options[str(self.comboBox.currentText())])
|
||||
except ValueError:
|
||||
return str(self.options[str(self.comboBox.currentText())])
|
||||
return {self.argname: self.options[self.comboBox.currentText()]}
|
||||
|
||||
def get_parameter(self):
|
||||
return str(self.comboBox.currentText())
|
||||
|
||||
@@ -562,7 +562,7 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
||||
|
||||
self.management.skip_points(**dial.get_arguments())
|
||||
|
||||
@QtCore.pyqtSlot(str, name='on_action_coup_calc_triggered')
|
||||
@QtCore.pyqtSlot(name='on_action_coup_calc_triggered')
|
||||
def coupling_dialog(self):
|
||||
dialog = QCoupCalcDialog(self)
|
||||
dialog.show()
|
||||
|
||||
@@ -977,10 +977,10 @@ class UpperManagement(QtCore.QObject):
|
||||
sd.convert(_x, *sd_param, from_=opts['tau_type'], to_='raw')
|
||||
|
||||
relax = Relaxation()
|
||||
relax.distribution(sd, parameter=sd_param, keywords=opts['sd_param'][1])
|
||||
relax.set_distribution(sd, parameter=sd_param, keywords=opts['sd_param'][1])
|
||||
|
||||
cp_param = [self.data[p].y.real if isinstance(p, str) else p for p in opts['cp_param'][0]]
|
||||
relax.coupling(opts['coup'], parameter=cp_param, keywords=opts['cp_param'][1])
|
||||
relax.set_coupling(opts['coup'], parameter=cp_param, keywords=opts['cp_param'][1])
|
||||
|
||||
if opts['out'] == 't1':
|
||||
y = relax.t1(x2, _x)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from ...nmr.couplings import *
|
||||
from ...nmr.coupling import *
|
||||
from ...utils.text import convert
|
||||
|
||||
from ..Qt import QtWidgets, QtCore
|
||||
@@ -11,7 +11,7 @@ class QCoupCalcDialog(QtWidgets.QDialog, Ui_coupling_calc_dialog):
|
||||
super().__init__(parent)
|
||||
self.setupUi(self)
|
||||
|
||||
self.cp = [Quadrupolar, QuadrupolarQCC, Czjzek, HeteroDipolar, HomoDipolar]
|
||||
self.cp = [Quadrupolar, Czjzek, HeteroDipolar, HomoDipolar]
|
||||
for cp in self.cp:
|
||||
self.comboBox.addItem(cp.name)
|
||||
|
||||
@@ -62,7 +62,7 @@ class QCoupCalcDialog(QtWidgets.QDialog, Ui_coupling_calc_dialog):
|
||||
for pp in self._coupling_parameter:
|
||||
p.append(pp.value)
|
||||
|
||||
self.label.setText('Coupling constant: %.8g' % self.cp[self.comboBox.currentIndex()].func(*p))
|
||||
self.label.setText('Coupling constant: %.8g' % self.cp[self.comboBox.currentIndex()].relax(*p))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from typing import List, Tuple
|
||||
|
||||
from ...nmr.couplings import *
|
||||
from ...nmr.coupling import *
|
||||
from ...distributions import ColeCole, ColeDavidson, HavriliakNegami
|
||||
from ...utils import pi
|
||||
from ...utils.text import convert
|
||||
@@ -20,7 +20,7 @@ class QRelaxCalc(QtWidgets.QDialog, Ui_Dialog):
|
||||
self.graphs = {}
|
||||
|
||||
self.specdens = [ColeCole, ColeDavidson, HavriliakNegami]
|
||||
self.coupling = [Quadrupolar, QuadrupolarQCC, Czjzek, HomoDipolar]
|
||||
self.coupling = [Quadrupolar, Czjzek, HomoDipolar]
|
||||
self.tau_parameter = []
|
||||
|
||||
for line_edit in [self.ea_lineEdit, self.tau0_lineEdit, self.start_lineEdit, self.stop_lineEdit,
|
||||
|
||||
@@ -4,7 +4,7 @@ from pyqtgraph import mkBrush, mkPen
|
||||
from ..lib.pg_objects import PlotItem
|
||||
from ...data.points import Points
|
||||
from ...nmr.relaxation import RelaxationEvaluation
|
||||
from ...nmr.couplings import *
|
||||
from ...nmr.coupling import *
|
||||
from ...distributions import *
|
||||
|
||||
from ..Qt import QtCore, QtWidgets, QtGui
|
||||
@@ -37,7 +37,7 @@ class QT1Widget(QtWidgets.QDialog, Ui_t1dialog):
|
||||
self.specdens_combobox.currentIndexChanged.connect(self.update_specdens)
|
||||
|
||||
self.cp_parameter = []
|
||||
self.coupling = [Quadrupolar, QuadrupolarQCC, Czjzek, HomoDipolar, Constant]
|
||||
self.coupling = [Quadrupolar, Czjzek, HomoDipolar, Constant]
|
||||
for i in self.coupling:
|
||||
self.coupling_combobox.addItem(i.name)
|
||||
self.coupling_combobox.currentIndexChanged.connect(self.update_coupling)
|
||||
@@ -116,7 +116,7 @@ class QT1Widget(QtWidgets.QDialog, Ui_t1dialog):
|
||||
self.verticalLayout_3.addWidget(_temp)
|
||||
self.sd_parameter.append(_temp)
|
||||
|
||||
self.t1calculator.distribution(self.sdmodels[idx])
|
||||
self.t1calculator.set_distribution(self.sdmodels[idx])
|
||||
self.update_model()
|
||||
|
||||
@QtCore.pyqtSlot()
|
||||
@@ -159,6 +159,7 @@ class QT1Widget(QtWidgets.QDialog, Ui_t1dialog):
|
||||
_temp.valueChanged.connect(self.update_coupling_parameter)
|
||||
_temp.stateChanged.connect(self.update_coupling_parameter)
|
||||
self.cp_parameter.append(_temp)
|
||||
print(self.cp_parameter)
|
||||
|
||||
if self.coupling[idx].choice is not None:
|
||||
for (name, kw_name, opts) in self.coupling[idx].choice:
|
||||
@@ -172,10 +173,14 @@ class QT1Widget(QtWidgets.QDialog, Ui_t1dialog):
|
||||
@QtCore.pyqtSlot()
|
||||
def update_coupling_parameter(self):
|
||||
new_p = []
|
||||
new_kw = {}
|
||||
for pp in self.cp_parameter:
|
||||
new_p.append(pp.value)
|
||||
if isinstance(pp, FormWidget):
|
||||
new_p.append(pp.value)
|
||||
else:
|
||||
new_kw.update(pp.value)
|
||||
new_coupling = self.coupling[self.coupling_combobox.currentIndex()]
|
||||
self.t1calculator.coupling(new_coupling)
|
||||
self.t1calculator.set_coupling(new_coupling, parameter=new_p, keywords=new_kw)
|
||||
|
||||
self.update_model()
|
||||
|
||||
@@ -253,30 +258,33 @@ class QT1Widget(QtWidgets.QDialog, Ui_t1dialog):
|
||||
'Only one can be determined from a minimum.')
|
||||
return
|
||||
|
||||
notfix = ()
|
||||
notfix = None
|
||||
var_idx = 0
|
||||
if not all(sd_fix):
|
||||
notfix = ('distribution', sd_fix.index(False))
|
||||
notfix = 'distribution'
|
||||
var_idx = sd_fix.index(False)
|
||||
|
||||
if not all(cp_fix):
|
||||
notfix = ('coupling', cp_fix.index(False))
|
||||
notfix = 'coupling'
|
||||
var_idx = cp_fix.index(False)
|
||||
|
||||
if not np.isfinite(self.t1calculator.t1min[1]):
|
||||
return
|
||||
|
||||
mini = np.nan
|
||||
with busy_cursor():
|
||||
calc_stretching, mini = self.t1calculator.increase(notfix, height=self.minimum[1],
|
||||
omega=2*np.pi*self.frequency,
|
||||
dist_parameter=sd_args, prefactor=cp_args,
|
||||
coupling_kwargs=cp_kwargs)
|
||||
calc_stretching, mini = self.t1calculator.get_increase(height=self.minimum[1],
|
||||
idx=var_idx, mode=notfix,
|
||||
omega=2*np.pi*self.frequency,
|
||||
dist_parameter=sd_args, prefactor=cp_args,
|
||||
coupling_kwargs=cp_kwargs)
|
||||
|
||||
self.label_t1min.setText(f'{mini:.4g} s')
|
||||
|
||||
if notfix:
|
||||
forms = self.sd_parameter if notfix[0] == 'distribution' else self.cp_parameter
|
||||
forms[notfix[1]].blockSignals(True)
|
||||
forms[notfix[1]].value = f'{calc_stretching:.4g}'
|
||||
forms[notfix[1]].blockSignals(False)
|
||||
forms = self.sd_parameter if notfix == 'distribution' else self.cp_parameter
|
||||
forms[var_idx].blockSignals(True)
|
||||
forms[var_idx].value = f'{calc_stretching:.4g}'
|
||||
forms[var_idx].blockSignals(False)
|
||||
|
||||
@QtCore.pyqtSlot(name='on_calc_pushButton_clicked')
|
||||
def calculate_correlations(self):
|
||||
@@ -317,7 +325,7 @@ class QT1Widget(QtWidgets.QDialog, Ui_t1dialog):
|
||||
cp_args.append(p.value)
|
||||
except AttributeError:
|
||||
# SelectionWidget has no isChecked()
|
||||
cp_kwargs[p.argname] = p.value
|
||||
cp_kwargs.update(p.value)
|
||||
|
||||
return cp_args, cp_kwargs, cp_fix
|
||||
|
||||
|
||||
+85
-66
@@ -2,10 +2,11 @@ import re
|
||||
import struct
|
||||
import warnings
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
import numpy as np
|
||||
|
||||
from ..data.signals import Signal
|
||||
from ..data import BDS, Points
|
||||
|
||||
MAGIC_RE = re.compile(b'NOVOCONTROL DK-RESULTS')
|
||||
|
||||
@@ -27,6 +28,9 @@ class BDSReader:
|
||||
|
||||
return self
|
||||
|
||||
def __getitem__(self, val):
|
||||
return self.opts[val]
|
||||
|
||||
def _parse_file(self):
|
||||
self.opts = {}
|
||||
with self.fname.open(mode='rb') as f:
|
||||
@@ -43,7 +47,7 @@ class BDSReader:
|
||||
f.seek(1, 1)
|
||||
self.opts['electrode thickness'] = struct.unpack('f', f.read(4))[0]
|
||||
|
||||
self.opts['parameter'] += struct.unpack('3f', f.read(12))
|
||||
self.opts['parameter'] += struct.unpack('3f', f.read(12)) # 1, 0.05, 0.06, 0.02
|
||||
|
||||
f.seek(2, 1)
|
||||
self.opts['capacity'] = struct.unpack('f', f.read(4))[0]
|
||||
@@ -56,43 +60,51 @@ class BDSReader:
|
||||
self.opts['C_0'] = (self.opts['area']-self.opts['spacer area']) / self.opts['thickness'] * 8.8541878128e-12
|
||||
|
||||
f.seek(158)
|
||||
name_length = struct.unpack('b', f.read(1))[0]
|
||||
name_length = struct.unpack('i', f.read(4))[0]
|
||||
|
||||
f.seek(164)
|
||||
f.seek(2, 1)
|
||||
b = f.read(name_length)
|
||||
self.opts['name'] = str(struct.unpack(f'<{name_length}s', b)[0][:-1], encoding='utf-8')
|
||||
|
||||
_ = struct.unpack('h', f.read(2))
|
||||
freq_values_length = struct.unpack('b', f.read(1))[0]
|
||||
freq_values_length = struct.unpack('i', f.read(4))[0]
|
||||
|
||||
f.seek(7, 1)
|
||||
f.seek(4, 1)
|
||||
self.x = np.empty(freq_values_length)
|
||||
for freqs in range(freq_values_length):
|
||||
self.x[freqs] = struct.unpack('f', f.read(4))[0]
|
||||
(self.x[freqs],) = struct.unpack('f', f.read(4))
|
||||
|
||||
_ = struct.unpack('h', f.read(2))
|
||||
length_temps = struct.unpack('b', f.read(1))[0]
|
||||
_ = struct.unpack('h', f.read(2)) # TODO if no temperature set this is not 400 and rest breaks
|
||||
length_temps = struct.unpack('i', f.read(4))[0]
|
||||
|
||||
f.seek(7, 1)
|
||||
f.seek(4, 1)
|
||||
self.opts['temperatures'] = np.array(struct.unpack('f' * length_temps, f.read(4*length_temps)))
|
||||
|
||||
f.seek(110, 1)
|
||||
date_length = (struct.unpack('b', f.read(1)))[0]
|
||||
date_length = (struct.unpack('i', f.read(4)))[0]
|
||||
|
||||
f.seek(5, 1)
|
||||
f.seek(2, 1)
|
||||
self.opts['date'] = str(struct.unpack(f'<{date_length}s', f.read(date_length))[0][:-1],
|
||||
encoding='utf-8')
|
||||
|
||||
f.seek(47, 1)
|
||||
# there are 9 different values per frequency and temperature
|
||||
|
||||
_y = []
|
||||
actual_temps_length = 0
|
||||
read_length = 9 * freq_values_length
|
||||
read_length = 9 * freq_values_length # per entry 9 * 4 byte
|
||||
while True:
|
||||
# catch error if measurement was aborted
|
||||
try:
|
||||
_y += struct.unpack('f' * read_length, f.read(4*read_length))
|
||||
y_i = struct.unpack('f' * read_length, f.read(4*read_length))
|
||||
if any([y_ii != 0.0 for y_ii in y_i[-3:]]):
|
||||
# the last three should be zero
|
||||
break
|
||||
|
||||
_y += y_i
|
||||
actual_temps_length += 1
|
||||
if actual_temps_length == length_temps:
|
||||
break
|
||||
|
||||
except struct.error:
|
||||
break
|
||||
|
||||
@@ -100,79 +112,86 @@ class BDSReader:
|
||||
warnings.warn('Number of set temperatures does not match number of data points')
|
||||
|
||||
_y = np.array(_y).reshape((actual_temps_length, freq_values_length, 9))
|
||||
print(_y.shape)
|
||||
print(f.tell())
|
||||
# last 3 entries are zero, save only 6
|
||||
# Z.imag*omega), Z.real, meas.time, meas. temp., ac voltage, dc voltage
|
||||
self.y = np.transpose(_y[:, :, :6], (2, 0, 1))
|
||||
|
||||
def export(self, mode='epsilon') -> list:
|
||||
if mode == 'epsilon':
|
||||
eps = self.epsilon()
|
||||
elif mode == 'sigma':
|
||||
eps = self.sigma()
|
||||
elif mode == 'modulus':
|
||||
eps = self.modulus()
|
||||
else:
|
||||
raise ValueError(f'Unknown mode {mode}, not "epsilon", "sigma", or "modulus".')
|
||||
|
||||
ret_val = []
|
||||
def export(self, ymode: str = 'epsilon', xmode: str = 'freq') -> List[BDS]:
|
||||
y = getattr(self, ymode)()
|
||||
|
||||
useful_info = {
|
||||
'voltage': self.opts['voltage'],
|
||||
'diameter': self.opts['diameter'],
|
||||
'area': self.opts['area'],
|
||||
'thickness': self.opts['thickness'],
|
||||
'mode': mode
|
||||
'mode': ymode
|
||||
}
|
||||
|
||||
for i, temps in enumerate(self.temperature):
|
||||
ret_val.append(Signal(x=self.frequencies, y=eps[0][i, :] + 1j*eps[1][i, :],
|
||||
name=f'{temps:.2f}', value=temps, **useful_info))
|
||||
if xmode == 'freq':
|
||||
fmt = '.2f'
|
||||
x2 = self.temperature
|
||||
x = self.frequencies
|
||||
|
||||
elif xmode == 'temp':
|
||||
fmt = '.2f'
|
||||
x2 = self.frequencies
|
||||
x = self.temperature
|
||||
y = y.T
|
||||
|
||||
else:
|
||||
raise ValueError(f'Unknown mode {xmode!r}, use `freq` or `temp`.')
|
||||
|
||||
ret_val = []
|
||||
for i, label in enumerate(x2):
|
||||
y_i = y[i]
|
||||
if np.allclose(y_i.imag, 0):
|
||||
ret_val.append(Points(x=x, y=y_i,
|
||||
name=f'{label:{fmt}}', value=label, **useful_info))
|
||||
else:
|
||||
ret_val.append(BDS(x=x, y=y_i,
|
||||
name=f'{label:{fmt}}', value=label, **useful_info))
|
||||
|
||||
return ret_val
|
||||
|
||||
def __getitem__(self, val):
|
||||
return self.opts[val]
|
||||
def capacity(self):
|
||||
# NOTE: nobody ever used edge correction, so we ignore this option entirely
|
||||
return self.y[0] - self.opts['capacity'] - 1j / self.y[1] / (self.frequencies*2*np.pi)
|
||||
|
||||
def sample_temp(self):
|
||||
return self.y[2]
|
||||
|
||||
def time(self):
|
||||
return self.y[3]
|
||||
|
||||
def sigma(self):
|
||||
# sigma^* = i * omega * eps_0 * (eps^* - 1)
|
||||
conv = 0.01 * 8.8541878128e-12 * 2*np.pi*self.x # factor 0.01: sigma in S/cm
|
||||
return conv * (self.epsilon() - 1)
|
||||
|
||||
def epsilon(self):
|
||||
# eps^* = Cp^* d/A/eps_0
|
||||
c = self.capacity()
|
||||
return (c.real - 1j * c.imag) / self.opts['C_0']
|
||||
|
||||
def modulus(self):
|
||||
# M^* = 1/eps^*
|
||||
return np.conjugate(1/self.epsilon())
|
||||
|
||||
def loss_factor(self):
|
||||
eps = self.epsilon()
|
||||
|
||||
return eps.imag / eps.real
|
||||
|
||||
@property
|
||||
def set_temp(self):
|
||||
return self.opts['temperatures'][:len(self.temperature)]
|
||||
|
||||
@property
|
||||
def probe_temp(self):
|
||||
return self.y[2]
|
||||
|
||||
@property
|
||||
def temperature(self):
|
||||
return self.probe_temp.mean(axis=1)
|
||||
return self.sample_temp().mean(axis=1)
|
||||
|
||||
@property
|
||||
def frequencies(self):
|
||||
return self.x
|
||||
|
||||
def capacity(self):
|
||||
# NOTE: nobody ever used edge correction, so we ignore this option entirely
|
||||
return self.y[0] - self.opts['capacity'], -1./self.y[1] / (self.frequencies*2*np.pi)
|
||||
|
||||
def voltage(self):
|
||||
return self.y[5]
|
||||
|
||||
def sigma(self):
|
||||
# sigma^* = i * omega * eps_0 * (eps^* - 1)
|
||||
eps_r, eps_i = self.epsilon()
|
||||
conv = 0.01 * 8.8541878128e-12 * 2*np.pi*self.x # factor 0.01: sigma in S/cm
|
||||
return conv * eps_i, conv * (eps_r-1)
|
||||
|
||||
def epsilon(self):
|
||||
# eps^* = Cp^* d/A/eps_0
|
||||
cr, ci = self.capacity()
|
||||
return cr / self.opts['C_0'], -ci / self.opts['C_0']
|
||||
|
||||
def modulus(self):
|
||||
# M^* = 1/eps^*
|
||||
eps_r, eps_i = self.epsilon()
|
||||
magn = eps_r ** 2 + eps_i ** 2
|
||||
return eps_r / magn, eps_i / magn
|
||||
|
||||
def loss_factor(self):
|
||||
eps_r, eps_i = self.epsilon()
|
||||
|
||||
return eps_i / eps_r, None
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
from functools import wraps
|
||||
from typing import Callable
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
def adjust_dims(func) -> Callable:
|
||||
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
"""
|
||||
ALlow outer product for omega*tau and remove redundant dimension afterwards
|
||||
"""
|
||||
x1, x2, *rest = args
|
||||
|
||||
x1 = np.asanyarray(x1)
|
||||
x2 = np.asanyarray(x2)
|
||||
|
||||
if len(x1.shape) > 1 or len(x2.shape) > 1:
|
||||
raise ValueError('Only numbers or 1d-arrays are supported')
|
||||
|
||||
result = func(x1[..., None], x2[None, ...], *rest, **kwargs)
|
||||
if result.ndim == 1:
|
||||
return result[0]
|
||||
else:
|
||||
return result.squeeze()
|
||||
|
||||
return wrapper
|
||||
#
|
||||
# return func
|
||||
@@ -3,7 +3,6 @@ from typing import TypeVar
|
||||
from ..math.mittagleffler import mlf
|
||||
|
||||
|
||||
|
||||
ArrayLike = TypeVar('ArrayLike')
|
||||
|
||||
|
||||
|
||||
+17
-19
@@ -19,7 +19,8 @@ def _kww_low(w, beta, kappa):
|
||||
|
||||
ln_w = np.log(w[:, np.newaxis])
|
||||
ln_a_k = scipy.special.gammaln((k+1) / beta) - scipy.special.gammaln(k+1) + k*ln_w
|
||||
a_k = np.exp(ln_a_k)
|
||||
with np.errstate(over='ignore'):
|
||||
a_k = np.exp(ln_a_k)
|
||||
|
||||
y_n = (sign * a_k).cumsum(axis=1)
|
||||
y_n = np.nan_to_num(y_n)
|
||||
@@ -45,7 +46,7 @@ def kwws_low(w, beta):
|
||||
|
||||
def _kww_high(w, beta, kappa):
|
||||
if beta < 0.1 or beta > 2.0:
|
||||
raise ValueError("invalid call to kww_hig: beta out of range")
|
||||
raise ValueError("invalid call to kww_high: beta out of range")
|
||||
|
||||
ln_omega = np.log(w[:, np.newaxis])
|
||||
k = np.atleast_2d(np.arange(kappa, __max_terms+kappa))
|
||||
@@ -89,7 +90,7 @@ def kwwc_high(w, beta):
|
||||
def _kww_mid(w, beta, kappa):
|
||||
if beta < 0.1 or beta > 2.0:
|
||||
raise ValueError("invalid call to kww_mid: beta out of range")
|
||||
elif any(w < 0):
|
||||
elif np.any(w < 0):
|
||||
raise ValueError("invalid call to kww_mid: w out of range")
|
||||
|
||||
if kappa and beta == 2:
|
||||
@@ -203,19 +204,19 @@ def _kwws_lim_hig(b):
|
||||
|
||||
|
||||
def kww_cos(w, tau, beta):
|
||||
r"""
|
||||
Calculate \int_0^\infty dt cos(w*t) exp(-(t/tau)^beta)
|
||||
:param w: array-like
|
||||
:param tau: float
|
||||
:param beta: float; 0.1 < beta <= 2
|
||||
:return: array (w.shape)
|
||||
"""
|
||||
|
||||
Args:
|
||||
w:
|
||||
tau:
|
||||
beta:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
# check input data
|
||||
if beta < 0.1:
|
||||
raise ValueError("kww: beta smaller than 0.1")
|
||||
|
||||
if beta > 2.0:
|
||||
raise ValueError("kww: beta larger than 2.0")
|
||||
if not (0.1 <= beta <= 2.):
|
||||
raise ValueError('KWW beta is not inside range 0.1-2')
|
||||
|
||||
if beta == 1:
|
||||
return tau/(1 + (w*tau)**2)
|
||||
@@ -246,11 +247,8 @@ def kww_cos(w, tau, beta):
|
||||
# \int_0^\infty dt sin(w*t) exp(-(t/tau)^beta)
|
||||
def kww_sin(w, tau, beta):
|
||||
# check input data
|
||||
if beta < 0.1:
|
||||
raise ValueError("kww: beta smaller than 0.1")
|
||||
|
||||
if beta > 2.0:
|
||||
raise ValueError("kww: beta larger than 2.0")
|
||||
if not (0.1 <= beta <= 2.):
|
||||
raise ValueError('KWW beta is not inside range 0.1-2')
|
||||
|
||||
if beta == 1:
|
||||
return w*tau**2/(1+(w*tau)**2)
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
from numbers import Number
|
||||
from typing import TypeVar, Union
|
||||
|
||||
import numpy as np
|
||||
from math import sqrt, log, exp, ceil
|
||||
|
||||
from scipy.special import gamma
|
||||
|
||||
T = TypeVar('T')
|
||||
|
||||
__all__ = ['mlf']
|
||||
|
||||
# calculate Mittag-Leffler based on MATLAB code
|
||||
@@ -13,7 +16,7 @@ __all__ = ['mlf']
|
||||
# SIAM Journal of Numerical Analysis, 2015, 53(3), 1350-1369
|
||||
|
||||
|
||||
def mlf(z, a: float, b: float = 1, g: float = 1):
|
||||
def mlf(z: T, a: float, b: float = 1, g: float = 1) -> T:
|
||||
if b == 1 and gamma == 1:
|
||||
if a == 0:
|
||||
return 1 / (1 - z)
|
||||
@@ -27,15 +30,16 @@ def mlf(z, a: float, b: float = 1, g: float = 1):
|
||||
|
||||
if isinstance(z, Number):
|
||||
return _mlf_single(z, a, b, g)
|
||||
|
||||
else:
|
||||
ret_val = np.zeros_like(z)
|
||||
for i, zz in enumerate(z):
|
||||
ret_val = np.zeros(z.size)
|
||||
for i, zz in enumerate(z.ravel()):
|
||||
ret_val[i] = _mlf_single(zz, a, b, g)
|
||||
|
||||
return ret_val
|
||||
return ret_val.reshape(z.shape)
|
||||
|
||||
|
||||
def _mlf_single(z, a, b, g):
|
||||
def _mlf_single(z: Union[int, float, complex], a: float, b: float, g: float):
|
||||
if abs(z) < 1.0e-15:
|
||||
ret_val = 1 / gamma(b)
|
||||
else:
|
||||
|
||||
@@ -1,3 +1,26 @@
|
||||
"""
|
||||
=============
|
||||
Fit functions
|
||||
=============
|
||||
|
||||
.. currentmodule:: nmreval.models
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated
|
||||
:nosignatures:
|
||||
:template: autosummary/class_with_attributes.rst
|
||||
|
||||
Constant
|
||||
Linear
|
||||
Parabola
|
||||
PowerLaw
|
||||
PowerLawCross
|
||||
ExpFunc
|
||||
MittagLeffler
|
||||
Log
|
||||
Sine
|
||||
|
||||
"""
|
||||
|
||||
from .basic import *
|
||||
from .relaxation import *
|
||||
|
||||
+35
-21
@@ -1,42 +1,44 @@
|
||||
"""
|
||||
***************
|
||||
Basic functions
|
||||
***************
|
||||
|
||||
Simple basic functions
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
|
||||
from ..lib.utils import ArrayLike
|
||||
from ..math.mittagleffler import mlf
|
||||
|
||||
|
||||
class Constant:
|
||||
"""
|
||||
Constant
|
||||
|
||||
.. math::
|
||||
y = c\cdot x
|
||||
|
||||
Args:
|
||||
x (array_like): Input values
|
||||
c (float): constant
|
||||
A boring constant line.
|
||||
"""
|
||||
|
||||
type = 'Basic'
|
||||
name = 'Constant'
|
||||
equation = 'C'
|
||||
params = ['C']
|
||||
|
||||
@staticmethod
|
||||
def func(x, c: float):
|
||||
def func(x: ArrayLike, c: float) -> ArrayLike:
|
||||
"""
|
||||
Constant
|
||||
|
||||
.. math::
|
||||
y = c \cdot x
|
||||
|
||||
Args:
|
||||
x (array-like): Input values
|
||||
c (float): constant
|
||||
"""
|
||||
return c*np.ones(len(x))
|
||||
|
||||
|
||||
class Linear:
|
||||
"""
|
||||
Straight line.
|
||||
|
||||
.. math::
|
||||
y = m\cdot x + t
|
||||
|
||||
Args:
|
||||
x (array_like): Input values
|
||||
m (float): Slope
|
||||
t (float): Intercept
|
||||
|
||||
Slightly more exciting line
|
||||
"""
|
||||
type = 'Basic'
|
||||
name = 'Straight line'
|
||||
@@ -44,7 +46,19 @@ class Linear:
|
||||
params = ['m', 't']
|
||||
|
||||
@staticmethod
|
||||
def func(x, m: float, t: float):
|
||||
def func(x: ArrayLike, m: float, t: float) -> ArrayLike:
|
||||
"""
|
||||
Straight line.
|
||||
|
||||
.. math::
|
||||
y = m\cdot x + t
|
||||
|
||||
Args:
|
||||
x (array_like): Input values
|
||||
m (float): Slope
|
||||
t (float): Intercept
|
||||
|
||||
"""
|
||||
return m*x + t
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import numpy as np
|
||||
from numpy import trapz
|
||||
try:
|
||||
from scipy.integrate import simpson
|
||||
except ImportError:
|
||||
from scipy.integrate import simps as simpson
|
||||
from numpy import pi
|
||||
|
||||
from ..math.orientations import zcw_spherical as crystallites
|
||||
|
||||
@@ -29,21 +33,22 @@ class Pake:
|
||||
_x -= 0.5*_x[-1]
|
||||
|
||||
if broad == 'l':
|
||||
apd = 2 * sigma / (4 * _x**2 + sigma**2) / np.pi
|
||||
apd = 2 * sigma / (4 * _x**2 + sigma**2) / pi
|
||||
else:
|
||||
apd = np.exp(-4 * np.log(2) * (_x/sigma)**2) * 2 * np.sqrt(np.log(2) / np.pi) / sigma
|
||||
apd = np.exp(-4 * np.log(2) * (_x/sigma)**2) * 2 * np.sqrt(np.log(2) / pi) / sigma
|
||||
|
||||
ret_val = np.convolve(s, apd, mode='same')
|
||||
|
||||
else:
|
||||
ret_val = s
|
||||
|
||||
omega_1 = np.pi/2/t_pulse
|
||||
attn = omega_1 * np.sin(t_pulse*np.sqrt(omega_1**2+0.5*(2*np.pi*x)**2)) / np.sqrt(omega_1**2+(np.pi*x)**2)
|
||||
omega_1 = pi/2/t_pulse
|
||||
attn = omega_1 * np.sin(t_pulse*np.sqrt(omega_1**2+0.5*(2*pi*x)**2)) / \
|
||||
np.sqrt(omega_1**2+(np.pi*x)**2)
|
||||
|
||||
ret_val *= attn
|
||||
|
||||
return c * ret_val / trapz(ret_val, x)
|
||||
return c * ret_val / simpson(ret_val, x)
|
||||
|
||||
|
||||
class CSA:
|
||||
@@ -69,14 +74,14 @@ class CSA:
|
||||
_x = np.arange(len(x)) * (x[1] - x[0])
|
||||
_x -= 0.5 * _x[-1]
|
||||
if broad == 'l':
|
||||
apd = 2 * sigma / (4*_x**2 + sigma**2) / np.pi
|
||||
apd = 2 * sigma / (4*_x**2 + sigma**2) / pi
|
||||
else:
|
||||
apd = np.exp(-4 * np.log(2) * (_x / sigma) ** 2) * 2 * np.sqrt(np.log(2) / np.pi) / sigma
|
||||
apd = np.exp(-4 * np.log(2) * (_x / sigma) ** 2) * 2 * np.sqrt(np.log(2) / pi) / sigma
|
||||
ret_val = np.convolve(s, apd, mode='same')
|
||||
else:
|
||||
ret_val = s
|
||||
|
||||
return c * ret_val / trapz(ret_val, x)
|
||||
return c * ret_val / simpson(ret_val, x)
|
||||
|
||||
|
||||
class SecCentralLine:
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
"""
|
||||
=========================================
|
||||
NMR specific methods (:mod:`nmreval.nmr`)
|
||||
=========================================
|
||||
|
||||
Each class provides a distribution of correlation times, correlation
|
||||
function, spectral density, susceptibility, and calculation of different
|
||||
types of correlation times.
|
||||
|
||||
.. currentmodule:: nmreval.nmr
|
||||
|
||||
Relaxation
|
||||
**********
|
||||
|
||||
Calculate spin-lattice and spin-spin relaxation, and evaluate T1 minima for correlation times.
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
:nosignatures:
|
||||
|
||||
Relaxation
|
||||
RelaxationEvaluation
|
||||
|
||||
Coupling constants (:mod:`nmreval.nmr.coupling`)
|
||||
************************************************
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated/
|
||||
:template: autosummary/class_with_attributes.rst
|
||||
:nosignatures:
|
||||
|
||||
~coupling.Quadrupolar
|
||||
~coupling.HeteroDipolar
|
||||
~coupling.HomoDipolar
|
||||
~coupling.CSA
|
||||
~coupling.Constant
|
||||
~coupling.Czjzek
|
||||
|
||||
"""
|
||||
|
||||
from .relaxation import Relaxation, RelaxationEvaluation
|
||||
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
"""
|
||||
NMR coupling values
|
||||
===================
|
||||
|
||||
This is a compilation of NMR prefactors for calculation of relaxation times.
|
||||
A note of caution is
|
||||
|
||||
"""
|
||||
|
||||
import abc
|
||||
from math import log
|
||||
from collections import OrderedDict
|
||||
|
||||
from ..utils.constants import gamma_full, hbar_joule, pi, gamma, mu0
|
||||
|
||||
|
||||
__all__ = ['Quadrupolar', 'Czjzek', 'HeteroDipolar',
|
||||
'HomoDipolar', 'Constant', 'CSA']
|
||||
|
||||
|
||||
class Coupling(metaclass=abc.ABCMeta):
|
||||
name = 'coupling'
|
||||
parameter = None
|
||||
choice = None
|
||||
unit = None
|
||||
equation = ''
|
||||
|
||||
@staticmethod
|
||||
@abc.abstractmethod
|
||||
def relax(*args, **kwargs):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class Quadrupolar(Coupling):
|
||||
""" Quadrupolar interaction
|
||||
"""
|
||||
|
||||
name = 'Quadrupolar'
|
||||
parameter = [r'\delta/C_{q}', r'\eta']
|
||||
unit = ['Hz', '']
|
||||
equation = r'3/50 \pi^{2} C_{q}^{2} (1+\eta^{2}/3) (2I+3)/(I^{2}(2I-1))'
|
||||
choice = [
|
||||
('Spin', 'spin', OrderedDict([('1', 1), ('3/2', 1.5), ('2', 2), ('5/2', 2.5)])),
|
||||
('Type', 'is_delta', {'Anisotropy': True, 'QCC': False})
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def relax(value: float, eta: float, spin: float = 1, is_delta: bool = True) -> float:
|
||||
r"""Calculates prefactor for quadrupolar relaxation
|
||||
|
||||
.. math::
|
||||
\frac{3}{200} (2\pi C_q)^2 \left(1+\frac{\eta^{2}}{3}\right) \frac{2I+3}{I^{2}(2I-1)}
|
||||
|
||||
If input is given as anisotropy :math:`\delta` then :math:`C_q` is calculated via
|
||||
|
||||
.. math::
|
||||
C_q = \delta \frac{4I(2I-1)}{3(2m-1)}
|
||||
|
||||
where `m` is 1 for integer spin and 3/2 else.
|
||||
|
||||
Args:
|
||||
value (float): Quadrupolar coupling constant in Hz.
|
||||
eta (float): Asymmetry parameter
|
||||
spin (float): Spin `I`. Default is 1.
|
||||
is_delta (bool): If True, uses value as anisotropy :math:`\delta` else as QCC. Default is True.
|
||||
|
||||
"""
|
||||
m = 1 if (2*spin) % 2 == 0 else 1.5
|
||||
if is_delta:
|
||||
value *= 4*spin*(2*spin-1) / (3*(2*m-1))
|
||||
|
||||
return 0.06 * (2*spin+3) * (pi*value)**2 * (1 + eta**2 / 3) / (spin**2 * (2*spin-1))
|
||||
|
||||
@staticmethod
|
||||
def convert(in_value: float, to: str = 'aniso', spin: float = 1) -> float:
|
||||
r"""Convert between QCC and anisotropy.
|
||||
|
||||
Relation between quadrupole coupling constant :math:`C_q` and anisotropy :math:`\delta` is given by
|
||||
|
||||
.. math::
|
||||
\delta = \frac{3(2m-1)}{4I(2I-1)} C_q
|
||||
|
||||
where `m` is 1 for integer spin and 3/2 else.
|
||||
|
||||
Args:
|
||||
in_value (float): Input value
|
||||
to (str, {`aniso`, `qcc`}): Direction of conversion. Default is `aniso`
|
||||
spin (float): Spin number `I`. Default is 1.
|
||||
|
||||
Returns:
|
||||
Converted value
|
||||
"""
|
||||
if to not in ['aniso', 'qcc']:
|
||||
raise ValueError(f'Cannot convert to {to} use `aniso` or `qcc`')
|
||||
|
||||
m = 1 if (2 * spin) % 2 == 0 else 1.5
|
||||
fac = 4 * spin * (2 * spin - 1) / (3 * (2 * m - 1))
|
||||
if to == 'delta':
|
||||
return in_value * fac
|
||||
else:
|
||||
return in_value / fac
|
||||
|
||||
|
||||
class HomoDipolar(Coupling):
|
||||
"""Homonuclear dipolar coupling"""
|
||||
|
||||
name = 'Homonuclear Dipolar'
|
||||
parameter = ['r']
|
||||
unit = ['m']
|
||||
choice = [
|
||||
(r'\gamma', 'nucleus', {k: k for k in gamma}),
|
||||
('Spin', 'spin', OrderedDict([('1/2', 0.5), ('1', 1), ('3/2', 1.5), ('2', 2), ('5/2', 2.5), ('3', 3)])),
|
||||
]
|
||||
equation = r'2/5 [\mu_{0}/(4\pi) \hbar \gamma^{2}/r^{3}]^{2} I(I+1)'
|
||||
|
||||
@staticmethod
|
||||
def relax(r: float, nucleus: str = '1H', spin: float = 0.5) -> float:
|
||||
"""Calculate prefactor for homonuclear dipolar relaxation.
|
||||
|
||||
Args:
|
||||
r (float): Distance in m.
|
||||
nucleus (str): Name of isotope. Default is `1H`
|
||||
spin (float): Spin `I` of isotope. Default is 1/2
|
||||
|
||||
"""
|
||||
|
||||
if nucleus not in gamma_full:
|
||||
raise KeyError(f'Unknown nucleus {nucleus}')
|
||||
|
||||
try:
|
||||
coupling = mu0 / (4*pi) * hbar_joule * gamma_full[nucleus]**2 / (r+1e-34)**3
|
||||
except ZeroDivisionError:
|
||||
return 1e318
|
||||
|
||||
coupling **= 2
|
||||
coupling *= 2 * spin * (spin+1) / 5
|
||||
|
||||
return coupling
|
||||
|
||||
|
||||
class HeteroDipolar(Coupling):
|
||||
"""Heteronuclear dipolar coupling"""
|
||||
|
||||
name = 'Heteronuclear Dipolar'
|
||||
parameter = ['r']
|
||||
unit = ['m']
|
||||
choice = [
|
||||
(r'\gamma_{1}', 'nucleus1', {k: k for k in gamma}),
|
||||
(r'\gamma_{2}', 'nucleus2', {k: k for k in gamma}),
|
||||
('Spin', 'spin', dict([('1/2', 0.5), ('1', 1), ('3/2', 1.5), ('2', 2), ('5/2', 2.5), ('3', 3)]))
|
||||
]
|
||||
equation = r'2/15 [\mu_{0}/(4\pi) \hbar \gamma^{2}/r^{3}]^{2} I(I+1)'
|
||||
|
||||
@staticmethod
|
||||
def relax(r: float, nucleus1: str = '1H', nucleus2: str = '19F', spin: float = 0.5) -> float:
|
||||
r"""Calculate prefactor for feteronuclear dipolar relaxation.
|
||||
|
||||
.. math::
|
||||
\frac{2}{15} \left(\frac{\mu_0}{4\pi} \hbar \frac{\gamma_1 \gamma_2}{r^3} \right)^2 S(S+1)
|
||||
|
||||
Args:
|
||||
r (float): Distance in m.
|
||||
nucleus1 (str): Name of observed isotope. Default is `1H`
|
||||
nucleus2 (str): Name of coupled isotope. Default is `19F`
|
||||
spin (float): Spin `S` of the coupled isotope. Default is 1/2
|
||||
|
||||
"""
|
||||
if nucleus1 not in gamma_full:
|
||||
raise KeyError(f'Unknown nucleus {nucleus1}')
|
||||
|
||||
if nucleus2 not in gamma_full:
|
||||
raise KeyError(f'Unknown nucleus {nucleus2}')
|
||||
|
||||
try:
|
||||
coupling = mu0 / (4*pi) * hbar_joule * gamma_full[nucleus1]*gamma_full[nucleus2] / r**3
|
||||
except ZeroDivisionError:
|
||||
return 1e318
|
||||
|
||||
coupling **= 2
|
||||
coupling *= 2 * spin * (spin+1) / 15
|
||||
|
||||
return coupling
|
||||
|
||||
|
||||
class Czjzek(Coupling):
|
||||
name = 'Czjzek'
|
||||
parameter = [r'\Sigma']
|
||||
unit = ['Hz']
|
||||
choice = [('Spin', 'spin', {'1': 1, '3/2': 1.5, '5/2': 2.5, '7/2': 3.5})]
|
||||
equation = r'3/[20log(2)] \pi^{2} \Sigma^{2} (2I+3)/(I^{2}(2I-1))'
|
||||
|
||||
@staticmethod
|
||||
def relax(fwhm, spin=1):
|
||||
return 3*(2+spin+3) * pi**2 * fwhm**2 / 20 / log(2) / spin**2 / (2*spin-1)
|
||||
|
||||
|
||||
class Constant(Coupling):
|
||||
name = 'Constant'
|
||||
parameter = ['C']
|
||||
equation = 'C'
|
||||
|
||||
@staticmethod
|
||||
def relax(c):
|
||||
return c
|
||||
|
||||
|
||||
class CSA(Coupling):
|
||||
"""Chemical shift anisotropy"""
|
||||
|
||||
name = 'CSA'
|
||||
equation = r'2/15 \Delta\sigma^{2} (1+\eta^{2}/2)'
|
||||
parameter = [r'\Delta\sigma', '\eta']
|
||||
unit = ['ppm', '']
|
||||
|
||||
@staticmethod
|
||||
def relax(sigma: float, eta: float) -> float:
|
||||
r"""Relaxation prefactor for chemical shift anisotropy.
|
||||
|
||||
.. math::
|
||||
C = \frac{2}{15} \Delta\sigma^2 \left(1+\frac{\eta^2}{3}\right)
|
||||
|
||||
.. note::
|
||||
The influence of the external magnetic field is missing here but
|
||||
is multiplied in :func:`~nmreval.nmr.Relaxation.t1_csa`
|
||||
|
||||
Args:
|
||||
sigma: Anisotropy (in ppm)
|
||||
eta: Asymmetry parameter
|
||||
|
||||
Reference:
|
||||
Spiess, H.W.: Rotation of Molecules and Nuclear Spin Relaxation.
|
||||
In: NMR - Basic principles and progress, Vol. 15, (Springer, 1978)
|
||||
https://doi.org/10.1007/978-3-642-66961-3_2
|
||||
|
||||
"""
|
||||
return 2 * sigma**2 * (1+eta**2 / 3) / 15
|
||||
@@ -1,102 +0,0 @@
|
||||
from math import log
|
||||
from collections import OrderedDict
|
||||
|
||||
from ..utils.constants import pi, gamma, mu0, hbar
|
||||
|
||||
|
||||
__all__ = ['Quadrupolar', 'QuadrupolarQCC', 'Czjzek', 'HeteroDipolar', 'HomoDipolar', 'Constant']
|
||||
|
||||
|
||||
class Coupling(object):
|
||||
name = 'coupling'
|
||||
parameter = None
|
||||
choice = None
|
||||
unit = None
|
||||
equation = ''
|
||||
|
||||
|
||||
class Quadrupolar(Coupling):
|
||||
name = 'Quadrupolar'
|
||||
parameter = [r'\delta', r'\eta']
|
||||
unit = ['Hz', '']
|
||||
equation = r'24 (2I-1)(2I+3) / (25(6m+3)^{2}) (1+\eta^2/3) \pi^{2} \delta^{2}'
|
||||
choice = [('Spin', 'spin', OrderedDict([('1', 1), ('3/2', 1.5), ('2', 2),
|
||||
('5/2', 2.5), ('3', 3), ('7/2', 3.5)]))]
|
||||
|
||||
@staticmethod
|
||||
def func(delta, eta, spin=1):
|
||||
m = 0 if (2*spin) % 2 == 0 else 0.5
|
||||
return 24*(2*spin-1)*(2*spin+3) * pi**2 * delta**2 * (1+eta**2 / 3) / 25 / (6*m+3)**2
|
||||
|
||||
|
||||
class QuadrupolarQCC(Coupling):
|
||||
name = 'Quadrupolar (QCC)'
|
||||
parameter = ['C_{Q}', r'\eta']
|
||||
unit = ['Hz', '']
|
||||
choice = [('Spin', 'spin', {'1': 1, '3/2': 1.5, '5/2': 2.5, '7/2': 3.5})]
|
||||
equation = r'3/50 \pi^{2} C_{Q}^{2} (1+\eta^{2}/3) (2I+3)/(I^{2}(2I-1))'
|
||||
|
||||
@staticmethod
|
||||
def func(cq, eta, spin=1):
|
||||
return 0.06 * (2*spin+3)*pi**2 * cq**2 * (1+eta**2 / 3) / spin**2 / (2*spin-1)
|
||||
|
||||
|
||||
class Czjzek(Coupling):
|
||||
name = 'Czjzek'
|
||||
parameter = [r'\Sigma']
|
||||
unit = ['Hz']
|
||||
choice = [('Spin', 'spin', {'1': 1, '3/2': 1.5, '5/2': 2.5, '7/2': 3.5})]
|
||||
equation = r'3/[20log(2)] \pi^{2} \Sigma^{2} (2I+3)/(I^{2}(2I-1))'
|
||||
|
||||
@staticmethod
|
||||
def func(fwhm, spin=1):
|
||||
return 3*(2+spin+3) * pi**2 * fwhm**2 / 20 / log(2) / spin**2 / (2*spin-1)
|
||||
|
||||
|
||||
class HeteroDipolar(Coupling):
|
||||
name = 'Heteronuclear Dipolar'
|
||||
parameter = ['r']
|
||||
unit = ['m']
|
||||
choice = [(r'\gamma_{1}', 'nucleus1', {k: k for k in gamma.keys()}),
|
||||
(r'\gamma_{2}', 'nucleus2', {k: k for k in gamma.keys()})]
|
||||
equation = r'1/10 * [\mu_{0}/(4\pi) * \hbar * \gamma_{1}\gamma_{2}/r^{3}]^{2}'
|
||||
|
||||
@staticmethod
|
||||
def func(r, nucleus1='1H', nucleus2='19F'):
|
||||
if nucleus1 not in gamma:
|
||||
raise KeyError('Unknown nucleus {}'.format(nucleus1))
|
||||
if nucleus2 not in gamma:
|
||||
raise KeyError('Unknown nucleus {}'.format(nucleus2))
|
||||
|
||||
coupling = mu0 / (4*pi) * hbar*gamma[nucleus1]*gamma[nucleus2] / (r+1e-34)**3
|
||||
coupling **= 2
|
||||
|
||||
return 0.1 * coupling
|
||||
|
||||
|
||||
class HomoDipolar(Coupling):
|
||||
name = 'Homonuclear Dipolar'
|
||||
parameter = ['r']
|
||||
unit = ['m']
|
||||
choice = [(r'\gamma', 'nucleus', {k: k for k in gamma.keys()})]
|
||||
equation = r'3/10 * [\mu_{0}/(4\pi) * \hbar * \gamma^{2}/r^{3}]^{2}'
|
||||
|
||||
@staticmethod
|
||||
def func(r, nucleus='1H'):
|
||||
if nucleus not in gamma:
|
||||
raise KeyError('Unknown nucleus {}'.format(nucleus))
|
||||
|
||||
coupling = mu0 / (4*pi) * hbar * gamma[nucleus]**2 / (r+1e-34)**3
|
||||
coupling **= 2
|
||||
|
||||
return 0.3 * coupling
|
||||
|
||||
|
||||
class Constant(Coupling):
|
||||
name = 'Constant'
|
||||
parameter = ['C']
|
||||
equation = 'C'
|
||||
|
||||
@staticmethod
|
||||
def func(c):
|
||||
return c
|
||||
+514
-177
@@ -1,6 +1,12 @@
|
||||
from numbers import Number
|
||||
"""
|
||||
Relaxation
|
||||
==========
|
||||
|
||||
Classes to calculate spin-lattice and spin-spin relaxation, as well as to evaluate T1 data and calculate correlation times
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Tuple
|
||||
from typing import Any, Optional, Tuple, Type, Union
|
||||
from warnings import warn
|
||||
|
||||
import numpy as np
|
||||
@@ -8,138 +14,417 @@ from scipy.interpolate import interp1d, Akima1DInterpolator
|
||||
from scipy.optimize import minimize
|
||||
|
||||
from nmreval.lib.utils import ArrayLike
|
||||
from .coupling import Coupling
|
||||
from ..distributions.base import Distribution
|
||||
from ..distributions.debye import Debye as Debye
|
||||
from ..utils import gamma_full
|
||||
|
||||
|
||||
class Relaxation:
|
||||
def __init__(self, distribution=None):
|
||||
self._distribution = distribution
|
||||
self._dist_parameter = ()
|
||||
"""
|
||||
Class to calculate relaxation times.
|
||||
"""
|
||||
|
||||
def __init__(self, distribution: Type[Distribution] = None):
|
||||
self.distribution = distribution
|
||||
self.dist_parameter = ()
|
||||
self._dist_kw = {}
|
||||
|
||||
self._coupling = None
|
||||
self._coup_parameter = ()
|
||||
self._coup_kw = {}
|
||||
self.coupling = None
|
||||
self.coup_parameter = []
|
||||
self.coup_kw = {}
|
||||
|
||||
self._prefactor = 1.
|
||||
self.prefactor = 1.
|
||||
|
||||
def __repr__(self):
|
||||
if self._distribution is not None:
|
||||
return str(self._distribution.name)
|
||||
if self.distribution is not None:
|
||||
return str(self.distribution.name)
|
||||
else:
|
||||
return super().__repr__()
|
||||
|
||||
def coupling(self, coupling, parameter: list = None, keywords: dict = None):
|
||||
self._coupling = coupling
|
||||
def set_coupling(self, coupling: Union[float, Type[Coupling]],
|
||||
parameter: Union[tuple, list] = None, keywords: dict = None):
|
||||
|
||||
if parameter is not None:
|
||||
self._coup_parameter = parameter
|
||||
self.coup_parameter = parameter
|
||||
|
||||
if keywords is not None:
|
||||
self._coup_kw = keywords
|
||||
self.coup_kw = keywords
|
||||
|
||||
if isinstance(self._coupling, Number):
|
||||
self._prefactor = self._coupling
|
||||
if isinstance(coupling, float):
|
||||
self.prefactor = coupling
|
||||
elif issubclass(coupling, Coupling):
|
||||
self.coupling = coupling
|
||||
if self.coup_kw is None:
|
||||
raise ValueError('Coupling is missing parameter')
|
||||
self.prefactor = self.coupling.relax(*self.coup_parameter, **self.coup_kw)
|
||||
else:
|
||||
try:
|
||||
self._prefactor = self._coupling.func(*self._coup_parameter, **self._coup_kw)
|
||||
except TypeError:
|
||||
pass
|
||||
raise ValueError(f'`coupling` is not number or of type `Coupling`, found {coupling!r}')
|
||||
|
||||
def distribution(self, dist, parameter=None, keywords=None):
|
||||
self._distribution = dist
|
||||
def set_distribution(self, dist: Type[Distribution], parameter: Union[tuple, list] = None, keywords: dict = None):
|
||||
self.distribution = dist
|
||||
|
||||
if parameter is not None:
|
||||
self._dist_parameter = parameter
|
||||
self.dist_parameter = parameter
|
||||
|
||||
if keywords is not None:
|
||||
self._dist_kw = keywords
|
||||
|
||||
def t1(self, omega, tau, *args, mode='bpp', **kwargs):
|
||||
# defaults to BPP
|
||||
if mode == 'bpp':
|
||||
return self.t1_bpp(omega, tau, *args, **kwargs)
|
||||
elif mode == 'dipolar':
|
||||
return self.t1_dipolar(omega, tau, *args, **kwargs)
|
||||
else:
|
||||
raise AttributeError(f'Unknown mode {mode}. Use either "bpp" or "dipolar".')
|
||||
|
||||
def t1_dipolar(self, omega: ArrayLike, tau: ArrayLike, *args,
|
||||
inverse: bool = True, prefactor: float = None, **kwargs) -> ArrayLike:
|
||||
"""
|
||||
def t1(self, omega: ArrayLike, tau: ArrayLike, *specdens_args: Any,
|
||||
mode: str = 'bpp', **kwargs) -> Union[np.ndarray, float]:
|
||||
r"""
|
||||
Convenience function
|
||||
|
||||
Args:
|
||||
omega:
|
||||
tau:
|
||||
*args:
|
||||
inverse:
|
||||
prefactor:
|
||||
omega (array-like): Frequency in 1/s (not Hz)
|
||||
tau (array-like): Correlation times in s
|
||||
*specdens_args: Additional parameter for spectral density.
|
||||
If given this will replace previously set arguments during calculation
|
||||
mode (str, {`bpp`, `dipolar`, `csa`}): Type of relaxation
|
||||
|
||||
- `bpp` : Homonuclear dipolar/quadrupolar interaction :func:`(see here) <nmreval.nmr.Relaxation.t1_bpp>`
|
||||
- `dipolar` : Heteronuclear dipolar interaction :func:`(see here) <nmreval.nmr.Relaxation.t1_dipolar>`
|
||||
- `csa` : Chemical shift interaction :func:`(see here) <nmreval.nmr.Relaxation.t1_csa>`
|
||||
|
||||
Default is `bpp`
|
||||
|
||||
**kwargs:
|
||||
|
||||
Returns:
|
||||
float or ndarray:
|
||||
A k by n array where k is length of ``omega`` and n is length of ``tau``.
|
||||
If both are of length 1 a number is returned instead of array.
|
||||
|
||||
"""
|
||||
try:
|
||||
omega_2 = kwargs['omega_coup']
|
||||
except KeyError:
|
||||
if kwargs.get('gamma_obs', False):
|
||||
omega_2 = kwargs['gamma_coup'] / kwargs['gamma_obs'] * omega
|
||||
else:
|
||||
raise AttributeError('Unknown second frequency.')
|
||||
if mode not in ['bpp', 'dipolar', 'csa']:
|
||||
raise ValueError(f'Unknown mode {mode} not `bpp`, `dipolar`, `csa`.')
|
||||
|
||||
# defaults to BPP
|
||||
if mode == 'bpp':
|
||||
return self.t1_bpp(omega, tau, *specdens_args, **kwargs)
|
||||
elif mode == 'dipolar':
|
||||
return self.t1_dipolar(omega, tau, *specdens_args, **kwargs)
|
||||
|
||||
def t1_dipolar(self, omega: ArrayLike, tau: ArrayLike, *specdens_args: Any, inverse: bool = True,
|
||||
prefactor: float = None, omega_coup: ArrayLike = None,
|
||||
gamma_coup: str = None, gamma_obs: str = None) -> Union[np.ndarray, float]:
|
||||
r"""Calculate T1 under heteronuclear dipolar coupling.
|
||||
|
||||
.. math::
|
||||
\frac{1}{T_1} = C [3J(\omega_I) + 6J(\omega_I + \omega_I) + J(\omega_I - \omega_S)]
|
||||
|
||||
Args:
|
||||
omega (array-like): Frequency in 1/s (not Hz)
|
||||
tau (array-like): Correlation times in s
|
||||
*specdens_args: Additional parameter for spectral density.
|
||||
If given this will replace previously set arguments during calculation
|
||||
inverse (bool): Function returs relaxation time if True else relaxation rate. Default is `True`
|
||||
prefactor (float, optional): If given it is used as prefactor `C`
|
||||
instead of previously set coupling prefactor.
|
||||
omega_coup (array-like, optional): Frequency of coupled isotope must be of same shape as omega.
|
||||
gamma_obs (str, optional): Name of observed isotope ('1H', '23Na', ...).
|
||||
Values is used if `omega_coup` is None.
|
||||
gamma_coup (str, optional): Name of coupled isotope ('1H', '23Na', ...).
|
||||
Values is used if `omega_coup` is None.
|
||||
|
||||
Returns:
|
||||
float or ndarray:
|
||||
A k by n array where k is length of ``omega`` and n is length of ``tau``.
|
||||
If both are of length 1 a number is returned instead of array.
|
||||
|
||||
"""
|
||||
omega = np.asanyarray(omega)
|
||||
|
||||
if omega_coup is not None:
|
||||
omega_coup = np.asanyarray(omega_coup)
|
||||
if omega.shape != omega_coup.shape:
|
||||
raise ValueError('omega and omega_coup have not same shape')
|
||||
omega_2 = omega_coup
|
||||
elif (gamma_coup is not None) and (gamma_obs is not None):
|
||||
omega_2 = gamma_full[gamma_coup] / gamma_full[gamma_obs] * omega
|
||||
else:
|
||||
raise AttributeError('Unknown second frequency. Set `omega_coup`, or `gamma_coup` and `gamma_obs`')
|
||||
|
||||
if prefactor is None:
|
||||
prefactor = self._prefactor
|
||||
prefactor = self.prefactor
|
||||
|
||||
if len(args) == 0:
|
||||
args = self._dist_parameter
|
||||
if len(specdens_args) == 0:
|
||||
specdens_args = self.dist_parameter
|
||||
|
||||
rate = prefactor * (3 * self._distribution.specdens(omega, tau, *args) +
|
||||
self._distribution.specdens(np.abs(omega - omega_2), tau, *args) +
|
||||
6 * self._distribution.specdens(omega + omega_2, tau, *args))
|
||||
rate = prefactor * (self.distribution.specdens(omega - omega_2, tau, *specdens_args) +
|
||||
3 * self.distribution.specdens(omega, tau, *specdens_args) +
|
||||
6 * self.distribution.specdens(omega + omega_2, tau, *specdens_args)) / 2
|
||||
if inverse:
|
||||
return 1 / rate
|
||||
else:
|
||||
return rate
|
||||
|
||||
def t1_bpp(self, omega, tau, *args, inverse=True, prefactor=None, **kwargs):
|
||||
def t1_bpp(self, omega: ArrayLike, tau: ArrayLike, *specdens_args: Any,
|
||||
inverse: bool = True, prefactor: float = None) -> Union[np.ndarray, float]:
|
||||
r"""Calculate T1 under homonuclear dipolar coupling or quadrupolar coupling.
|
||||
|
||||
.. math::
|
||||
\frac{1}{T_1} = C [J(\omega) + 4J(2\omega)]
|
||||
|
||||
Args:
|
||||
omega (array-like): Frequency in 1/s (not Hz)
|
||||
tau (array-like): Correlation times in s
|
||||
*specdens_args: Additional parameter for spectral density.
|
||||
If given this will replace previously set arguments during calculation
|
||||
inverse (bool): Function returs relaxation time if True else relaxation rate. Default is `True`
|
||||
prefactor (float, optional): If given it is used as prefactor `C`
|
||||
instead of previously set coupling prefactor.
|
||||
|
||||
Returns:
|
||||
float or ndarray:
|
||||
A k by n array where k is length of ``omega`` and n is length of ``tau``.
|
||||
If both are of length 1 a number is returned instead of array.
|
||||
|
||||
"""
|
||||
if prefactor is None:
|
||||
prefactor = self._prefactor
|
||||
prefactor = self.prefactor
|
||||
|
||||
if len(args) == 0:
|
||||
args = self._dist_parameter
|
||||
if len(specdens_args) == 0:
|
||||
specdens_args = self.dist_parameter
|
||||
|
||||
rate = prefactor * (self._distribution.specdens(omega, tau, *args) +
|
||||
4*self._distribution.specdens(2*omega, tau, *args))
|
||||
rate = prefactor * (self.distribution.specdens(omega, tau, *specdens_args) +
|
||||
4 * self.distribution.specdens(2 * omega, tau, *specdens_args))
|
||||
if inverse:
|
||||
return 1. / rate
|
||||
else:
|
||||
return rate
|
||||
|
||||
def t1_rho(self, omega, tau, omega_rf, *args, inverse=True, prefactor=None, **kwargs):
|
||||
def t1_csa(self, omega: ArrayLike, tau: ArrayLike, *specdens_args: Any,
|
||||
inverse: bool = True, prefactor: float = None) -> Union[np.ndarray, float]:
|
||||
r"""Calculate T1 under chemical shift anisotropy.
|
||||
|
||||
.. math::
|
||||
\frac{1}{T_1} = C \omega^2 [J(\omega)]
|
||||
|
||||
This relation disregards antisymmetric CSA contributions
|
||||
|
||||
Args:
|
||||
omega (array-like): Frequency in 1/s (not Hz)
|
||||
tau (array-like): Correlation times in s
|
||||
*specdens_args: Additional parameter for spectral density.
|
||||
If given this will replace previously set arguments during calculation
|
||||
inverse (bool): Function returs relaxation time if True else relaxation rate. Default is `True`
|
||||
prefactor (float, optional): If given it is used as prefactor `C`
|
||||
instead of previously set coupling prefactor.
|
||||
|
||||
Returns:
|
||||
float or ndarray:
|
||||
A k by n array where k is length of ``omega`` and n is length of ``tau``.
|
||||
If both are of length 1 a number is returned instead of array.
|
||||
|
||||
"""
|
||||
if prefactor is None:
|
||||
prefactor = self._prefactor
|
||||
prefactor = self.prefactor
|
||||
|
||||
if len(args) == 0:
|
||||
args = self._dist_parameter
|
||||
if len(specdens_args) == 0:
|
||||
specdens_args = self.dist_parameter
|
||||
|
||||
rate = prefactor * (10 * self._distribution.specdens(omega, tau, *args) +
|
||||
4 * self._distribution.specdens(2 * omega, tau, *args) +
|
||||
6 * self._distribution.specdens(omega_rf, tau, *args))
|
||||
rate = prefactor * (self.distribution.specdens(omega, tau, *specdens_args) +
|
||||
4 * self.distribution.specdens(2 * omega, tau, *specdens_args))
|
||||
if inverse:
|
||||
return 1. / rate
|
||||
else:
|
||||
return rate
|
||||
|
||||
def t2(self, omega, tau, *args, inverse=True, prefactor=None, **kwargs):
|
||||
def t1q(self, omega: ArrayLike, tau: ArrayLike, *specdens_args: Any,
|
||||
inverse: bool = True, prefactor: float = None) -> Union[np.ndarray, float]:
|
||||
r"""Calculate T1q for homonuclear dipolar coupling or quadrupolar coupling (I=1).
|
||||
|
||||
.. math::
|
||||
\frac{1}{T_{1q}} = 3 C J(\omega)
|
||||
|
||||
Args:
|
||||
omega (array-like): Frequency in 1/s (not Hz)
|
||||
tau (array-like): Correlation times in s
|
||||
*specdens_args: Additional parameter for spectral density.
|
||||
If given this will replace previously set arguments during calculation
|
||||
inverse (bool): Function returs relaxation time if True else relaxation rate. Default is `True`
|
||||
prefactor (float, optional): If given it is used as prefactor `C`
|
||||
instead of previously set coupling prefactor.
|
||||
|
||||
Returns:
|
||||
float or ndarray:
|
||||
A k by n array where k is length of ``omega`` and n is length of ``tau``.
|
||||
If both are of length 1 a number is returned instead of array.
|
||||
|
||||
"""
|
||||
if prefactor is None:
|
||||
prefactor = self._prefactor / 2.
|
||||
prefactor = self.prefactor
|
||||
|
||||
if len(args) == 0:
|
||||
args = self._dist_parameter
|
||||
if len(specdens_args) == 0:
|
||||
specdens_args = self.dist_parameter
|
||||
|
||||
rate = prefactor * (3 * self._distribution.specdens(0, tau, *args) +
|
||||
5 * self._distribution.specdens(omega, tau, *args) +
|
||||
2 * self._distribution.specdens(2 * omega, tau, *args))
|
||||
rate = 3 * prefactor * self.distribution.specdens(omega, tau, *specdens_args)
|
||||
if inverse:
|
||||
return 1. / rate
|
||||
else:
|
||||
return rate
|
||||
|
||||
def t1_rho(self, omega: ArrayLike, tau: ArrayLike, omega_rf: float, *specdens_args: Any,
|
||||
inverse: bool = True, prefactor: float = None):
|
||||
r"""Calculate T1rho for homonuclear dipolar coupling or quadrupolar coupling.
|
||||
|
||||
.. math::
|
||||
\frac{1}{T_{1\rho}} = \frac{C}{2} [5J(\omega) + 2J(2\omega) + 3J(2\omega_{RF})]
|
||||
|
||||
Args:
|
||||
omega (array-like): Frequency in 1/s (not Hz).
|
||||
tau (array-like): Correlation times in s.
|
||||
omega_rf (float): Frequency of RF lock IN 1/s.
|
||||
*specdens_args: Additional parameter for spectral density.
|
||||
If given this will replace previously set arguments during calculation
|
||||
inverse (bool): Function returs relaxation time if True else relaxation rate. Default is `True`
|
||||
prefactor (float, optional): If given it is used as prefactor `C`
|
||||
instead of previously set coupling prefactor.
|
||||
|
||||
Returns:
|
||||
float or ndarray:
|
||||
A k by n array where k is length of ``omega`` and n is length of ``tau``.
|
||||
If both are of length 1 a number is returned instead of array.
|
||||
|
||||
"""
|
||||
if prefactor is None:
|
||||
prefactor = self.prefactor
|
||||
|
||||
if len(specdens_args) == 0:
|
||||
specdens_args = self.dist_parameter
|
||||
|
||||
rate = prefactor * (5 * self.distribution.specdens(omega, tau, *specdens_args) +
|
||||
2 * self.distribution.specdens(2 * omega, tau, *specdens_args) +
|
||||
3 * self.distribution.specdens(2 * omega_rf, tau, *specdens_args)) / 2
|
||||
if inverse:
|
||||
return 1. / rate
|
||||
else:
|
||||
return rate
|
||||
|
||||
def t2_bpp(self, omega: ArrayLike, tau: ArrayLike, *specdens_args: Any,
|
||||
inverse: bool = True, prefactor: float = None) -> Union[np.ndarray, float]:
|
||||
r"""Calculate T2 under homonuclear dipolar coupling or quadrupolar coupling.
|
||||
|
||||
.. math::
|
||||
\frac{1}{T_2} = \frac{C}{2} [3J() + 5J(2\omega) + 3J(2\omega)]
|
||||
|
||||
Args:
|
||||
omega (array-like): Frequency in 1/s (not Hz)
|
||||
tau (array-like): Correlation times in s
|
||||
*specdens_args (optional): Additional parameter for spectral density.
|
||||
If given this will replace previously set arguments during calculation
|
||||
inverse (bool): Function returs relaxation time if True else relaxation rate. Default is `True`
|
||||
prefactor (float, optional): If given it is used as prefactor `C`
|
||||
instead of previously set coupling prefactor.
|
||||
|
||||
Returns:
|
||||
float or ndarray:
|
||||
A k by n array where k is length of ``omega`` and n is length of ``tau``.
|
||||
If both are of length 1 a number is returned instead of array.
|
||||
|
||||
"""
|
||||
if prefactor is None:
|
||||
prefactor = self.prefactor
|
||||
|
||||
if len(specdens_args) == 0:
|
||||
specdens_args = self.dist_parameter
|
||||
|
||||
rate = prefactor * (3 * self.distribution.specdens(0, tau, *specdens_args) +
|
||||
5 * self.distribution.specdens(omega, tau, *specdens_args) +
|
||||
2 * self.distribution.specdens(2 * omega, tau, *specdens_args)) / 2
|
||||
if inverse:
|
||||
return 1. / rate
|
||||
else:
|
||||
return rate
|
||||
|
||||
def t2_dipolar(self, omega: ArrayLike, tau: ArrayLike, *specdens_args: Any,
|
||||
inverse: bool = True, prefactor: float = None, omega_coup: ArrayLike = None,
|
||||
gamma_coup: str = None, gamma_obs: str = None) -> Union[np.ndarray, float]:
|
||||
r"""Calculate T2 under heteronuclear dipolar coupling.
|
||||
|
||||
.. math::
|
||||
\frac{1}{T_2} = \frac{C}{2} [4J(0) + J(\omega_I - \omega_I) + 3J(\omega_S) + 6J(\omega_I) + 6J(\omega_I + \omega_I)]
|
||||
|
||||
Args:
|
||||
omega (array-like): Frequency in 1/s (not Hz)
|
||||
tau (array-like): Correlation times in s
|
||||
*specdens_args (optional): Additional parameter for spectral density.
|
||||
If given this will replace previously set arguments during calculation
|
||||
inverse (bool): Function returs relaxation time if True else relaxation rate. Default is `True`
|
||||
prefactor (float, optional): If given it is used as prefactor `C`
|
||||
instead of previously set coupling prefactor.
|
||||
omega_coup (array-like, optional): Frequency of coupled isotope must be of same shape as omega.
|
||||
gamma_obs (str, optional): Name of observed isotope ('1H', '23Na', ...).
|
||||
Values is used if `omega_coup` is None.
|
||||
gamma_coup (str, optional): Name of coupled isotope ('1H', '23Na', ...).
|
||||
Values is used if `omega_coup` is None.
|
||||
|
||||
Returns:
|
||||
float or ndarray:
|
||||
A k by n array where k is length of ``omega`` and n is length of ``tau``.
|
||||
If both are of length 1 a number is returned instead of array.
|
||||
|
||||
"""
|
||||
omega = np.asanyarray(omega)
|
||||
|
||||
if omega_coup is not None:
|
||||
omega_coup = np.asanyarray(omega_coup)
|
||||
if omega.shape != omega_coup.shape:
|
||||
raise ValueError('omega and omega_coup have not same shape')
|
||||
omega_2 = omega_coup
|
||||
elif (gamma_coup is not None) and (gamma_obs is not None):
|
||||
omega_2 = gamma_full[gamma_coup] / gamma_full[gamma_obs] * omega
|
||||
else:
|
||||
raise AttributeError('Unknown second frequency. Set `omega_coup`, or `gamma_coup` and `gamma_obs`')
|
||||
|
||||
if prefactor is None:
|
||||
prefactor = self.prefactor
|
||||
|
||||
if len(specdens_args) == 0:
|
||||
specdens_args = self.dist_parameter
|
||||
|
||||
rate = prefactor * (4 * self.distribution.specdens(0, tau, *specdens_args) +
|
||||
self.distribution.specdens(omega - omega_2, tau, *specdens_args) +
|
||||
3 * self.distribution.specdens(omega_2, tau, *specdens_args) +
|
||||
6 * self.distribution.specdens(omega, tau, *specdens_args) +
|
||||
6 * self.distribution.specdens(omega + omega_2, tau, *specdens_args)) / 2.
|
||||
|
||||
if inverse:
|
||||
return 1. / rate
|
||||
else:
|
||||
return rate
|
||||
|
||||
def t2_csa(self, omega: ArrayLike, tau: ArrayLike, *specdens_args: Any,
|
||||
inverse: bool = True, prefactor: float = None) -> Union[np.ndarray, float]:
|
||||
r"""Calculate T1 under chemical shift anisotropy.
|
||||
|
||||
.. math::
|
||||
\frac{1}{T_2} = \frac{C}{2} \omega^2 \bigl[J(0) + \frac{4}{3}J(\omega)\bigr]
|
||||
|
||||
Args:
|
||||
omega (array-like): Frequency in 1/s (not Hz)
|
||||
tau (array-like): Correlation times in s
|
||||
*specdens_args (optional): Additional parameter for spectral density.
|
||||
If given this will replace previously set arguments during calculation
|
||||
inverse (bool): Function returs relaxation time if True else relaxation rate. Default is `True`
|
||||
prefactor (float, optional): If given it is used as prefactor `C`
|
||||
instead of previously set coupling prefactor.
|
||||
|
||||
Returns:
|
||||
float or ndarray:
|
||||
A k by n array where k is length of ``omega`` and n is length of ``tau``.
|
||||
If both are of length 1 a number is returned instead of array.
|
||||
|
||||
"""
|
||||
if prefactor is None:
|
||||
prefactor = self.prefactor
|
||||
|
||||
if len(specdens_args) == 0:
|
||||
specdens_args = self.dist_parameter
|
||||
|
||||
rate = prefactor * (self.distribution.specdens(0, tau, *specdens_args) +
|
||||
4 / 3 * self.distribution.specdens(omega, tau, *specdens_args)) / 2.
|
||||
if inverse:
|
||||
return 1. / rate
|
||||
else:
|
||||
@@ -158,60 +443,31 @@ class RelaxationEvaluation(Relaxation):
|
||||
self.y = None
|
||||
|
||||
def data(self, temp, t1):
|
||||
temp = np.asarray(temp)
|
||||
t1 = np.asarray(t1)
|
||||
temp = np.asanyarray(temp)
|
||||
t1 = np.asanyarray(t1)
|
||||
sortidx = temp.argsort()
|
||||
self.x = temp[sortidx]
|
||||
self.y = t1[sortidx]
|
||||
self.calculate_t1_min()
|
||||
|
||||
def calculate_t1_min(self, interpolate: int = None, trange=None):
|
||||
min_index = np.argmin(self.y)
|
||||
t1_min = (self.x[min_index], self.y[min_index])
|
||||
parabola = None
|
||||
self._interpolate_range = (None, None)
|
||||
def get_increase(self, height: float = None, idx: int = 0, mode: str = None, omega: float = None,
|
||||
dist_parameter: Union[tuple, list] = None, prefactor: Union[tuple, list, float] = None,
|
||||
coupling_kwargs: dict = None):
|
||||
"""
|
||||
Determine a single parameter from a T1 minimum
|
||||
|
||||
if interpolate is not None:
|
||||
if interpolate not in [0, 1, 2, 3]:
|
||||
raise ValueError(f'Unknown interpolation value {interpolate}')
|
||||
Args:
|
||||
height (float, optional): Height of T1 minimum
|
||||
mode (str, {`distribution`, `prefactor`}, optional): If given,
|
||||
idx (int): Default is 0.
|
||||
omega (float, optional): Larmor frequency (in 1/s)
|
||||
dist_parameter (tuple, optional):
|
||||
prefactor (tuple, float, optional):
|
||||
coupling_kwargs (dict, optional):
|
||||
|
||||
if interpolate != 0:
|
||||
if trange is None:
|
||||
left_b = max(min_index-2, 0)
|
||||
right_b = min(len(self.x), min_index+3)
|
||||
else:
|
||||
left_b = np.argmin(np.abs(self.x-trange[0]))
|
||||
right_b = np.argmin(np.abs(self.x-trange[1]))+1
|
||||
Returns:
|
||||
|
||||
temp_range = self.x[left_b:right_b]
|
||||
t1_range = self.y[left_b:right_b]
|
||||
|
||||
try:
|
||||
x_inter = np.linspace(temp_range[0], temp_range[-1], num=51)
|
||||
|
||||
if interpolate == 1:
|
||||
i_func = np.poly1d(np.polyfit(temp_range, t1_range, 2))
|
||||
elif interpolate == 2:
|
||||
i_func = interp1d(temp_range, t1_range, kind='cubic')
|
||||
else:
|
||||
i_func = Akima1DInterpolator(temp_range, t1_range)
|
||||
|
||||
y_inter = i_func(x_inter)
|
||||
t1_min = (x_inter[np.argmin(y_inter)], y_inter[np.argmin(y_inter)])
|
||||
self._interpolate = i_func
|
||||
parabola = (x_inter, y_inter)
|
||||
self._interpolate_range = (temp_range[0], temp_range[-1])
|
||||
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
self.t1min = t1_min
|
||||
|
||||
return t1_min, parabola
|
||||
|
||||
def increase(self, variable: Tuple[str, int], height: float = None, omega: float = None,
|
||||
dist_parameter: list = None, prefactor=None,
|
||||
coupling_kwargs=None):
|
||||
"""
|
||||
|
||||
stretching = mini = np.nan
|
||||
if height is None:
|
||||
@@ -224,44 +480,62 @@ class RelaxationEvaluation(Relaxation):
|
||||
omega = self.omega
|
||||
|
||||
if prefactor is None:
|
||||
prefactor = self._prefactor
|
||||
prefactor = self.prefactor
|
||||
|
||||
if dist_parameter is None:
|
||||
dist_parameter = self._dist_parameter
|
||||
dist_parameter = self.dist_parameter
|
||||
|
||||
tau_lims = np.log10(1/omega)-3, np.log10(1/omega)+3
|
||||
if coupling_kwargs is None:
|
||||
coupling_kwargs = self.coup_kw
|
||||
|
||||
if not variable:
|
||||
if isinstance(prefactor, list):
|
||||
tau_lims = np.log10(1 / omega) - 3, np.log10(1 / omega) + 3
|
||||
|
||||
if mode is None:
|
||||
# nothing is variable -> just calculate minimum for given parameter
|
||||
if isinstance(prefactor, (tuple, list)):
|
||||
if coupling_kwargs is None:
|
||||
coupling_kwargs = self._coup_kw
|
||||
prefactor = self._coupling.func(*prefactor, **coupling_kwargs)
|
||||
coupling_kwargs = self.coup_kw
|
||||
prefactor = self.coupling.relax(*prefactor, **coupling_kwargs)
|
||||
|
||||
return stretching, np.min(self.t1(omega, np.logspace(*tau_lims, num=1001),
|
||||
*dist_parameter, prefactor=prefactor, inverse=True))
|
||||
|
||||
if variable[0] == 'distribution':
|
||||
if isinstance(self._distribution, Debye):
|
||||
return 1.
|
||||
if mode == 'distribution':
|
||||
# width parameter of spectral density is variable
|
||||
|
||||
if isinstance(self.distribution, Debye):
|
||||
# Debye is easy
|
||||
return 1., np.min(self.t1(omega, np.logspace(*tau_lims, num=1001),
|
||||
*dist_parameter, prefactor=prefactor, inverse=True))
|
||||
|
||||
if isinstance(prefactor, (list, tuple)):
|
||||
# calculate prefactor from coupling
|
||||
if self.coupling is None:
|
||||
raise ValueError('Coupling must be set before evaluation of T1 min')
|
||||
|
||||
if isinstance(prefactor, list):
|
||||
if coupling_kwargs is None:
|
||||
coupling_kwargs = self._coup_kw
|
||||
prefactor = self._coupling.func(*prefactor, **coupling_kwargs)
|
||||
coupling_kwargs = self.coup_kw
|
||||
|
||||
prefactor = self.coupling.relax(*prefactor, **coupling_kwargs)
|
||||
|
||||
use_fmin = True
|
||||
if self._distribution.name in ['KWW', 'Cole-Davidson', 'Cole-Cole', 'Log-Gaussian']:
|
||||
|
||||
if self.distribution.name in ['KWW', 'Cole-Davidson', 'Cole-Cole', 'Log-Gaussian']:
|
||||
# using precalculated values is faster than actual calculation, especially KWW and LG
|
||||
from importlib.resources import path
|
||||
with path('resources.nmr', 'T1_min.npz') as fp:
|
||||
if fp.exists():
|
||||
t1min_table = np.load(str(fp))[self._distribution.name.replace('-', '_')]
|
||||
t1min_table = np.load(str(fp))[self.distribution.name.replace('-', '_')]
|
||||
|
||||
debye_min = omega / 1.42517571908650 / prefactor # Zahl kommt von Wolfram alpha
|
||||
ratio = height / debye_min
|
||||
stretching = t1min_table[np.argmin(np.abs(t1min_table[:, 1]-ratio)), 0]
|
||||
stretching = t1min_table[np.argmin(np.abs(t1min_table[:, 1] - ratio)), 0]
|
||||
|
||||
use_fmin = False
|
||||
dist_parameter = [stretching]
|
||||
|
||||
if use_fmin:
|
||||
# use for untabulated spectral densities or if something went wrong
|
||||
#
|
||||
def _f(_x, p, ii):
|
||||
p[ii] = _x[0]
|
||||
t1_calc = np.min(self.t1(omega, np.logspace(*tau_lims, num=1001), *p,
|
||||
@@ -269,40 +543,100 @@ class RelaxationEvaluation(Relaxation):
|
||||
|
||||
return np.abs(t1_calc - height)
|
||||
|
||||
stretching = minimize(_f, np.array([dist_parameter[variable[1]]]),
|
||||
args=(dist_parameter, variable[1]),
|
||||
stretching = minimize(_f, np.array([dist_parameter[idx]]),
|
||||
args=(dist_parameter, idx),
|
||||
method='Nelder-Mead').x[0]
|
||||
dist_parameter[idx] = stretching
|
||||
|
||||
elif mode == 'coupling':
|
||||
# variable is somewhere in the prefcotor
|
||||
|
||||
elif variable[0] == 'coupling':
|
||||
t1_no_coup = np.min(self.t1(omega, np.logspace(*tau_lims, num=1001),
|
||||
*dist_parameter, prefactor=1))
|
||||
|
||||
if isinstance(prefactor, list):
|
||||
def _f(_x, p, ii):
|
||||
p[ii] = _x
|
||||
return np.abs(t1_no_coup/height - self._coupling.func(*p, **coupling_kwargs)[0])
|
||||
if isinstance(prefactor, (tuple, list)):
|
||||
prefactor = list(prefactor)
|
||||
|
||||
def _f(_x, p, ii):
|
||||
p[ii] = _x[0]
|
||||
return np.abs(t1_no_coup / height - self.coupling.relax(*p, **coupling_kwargs))
|
||||
|
||||
stretching = minimize(_f, np.array([prefactor[idx]]),
|
||||
args=(prefactor, idx)).x[0]
|
||||
|
||||
stretching = minimize(_f, np.array([prefactor[variable[1]]]),
|
||||
args=(prefactor, variable[1])).x[0]
|
||||
else:
|
||||
stretching = t1_no_coup / height
|
||||
|
||||
else:
|
||||
raise ValueError('Use "distribution" or "coupling" to set parameter')
|
||||
raise ValueError('Use `distribution` or `coupling` to set parameter')
|
||||
|
||||
if stretching:
|
||||
self._prefactor = prefactor
|
||||
self._dist_parameter = dist_parameter
|
||||
if isinstance(prefactor, list):
|
||||
self._coup_parameter = prefactor
|
||||
mini = np.min(self.t1(omega, np.logspace(*tau_lims, num=1001), *self._dist_parameter,
|
||||
prefactor=self._prefactor))
|
||||
self.prefactor = prefactor
|
||||
self.dist_parameter = dist_parameter
|
||||
if isinstance(prefactor, (tuple, list)):
|
||||
self.coup_parameter = prefactor
|
||||
self.prefactor = self.coupling.relax(*self.coup_parameter, **self.coup_kw)
|
||||
else:
|
||||
self.prefactor = prefactor
|
||||
mini = np.min(self.t1(omega, np.logspace(*tau_lims, num=1001), *self.dist_parameter,
|
||||
prefactor=self.prefactor))
|
||||
|
||||
return stretching, mini
|
||||
|
||||
def correlation_from_t1(self, mode: str = 'raw', interpolate=False, omega=None,
|
||||
dist_parameter: list = None, prefactor: float = None,
|
||||
coupling_param: list = None, coupling_kwargs: dict = None):
|
||||
def calculate_t1_min(self, interpolate: int = None, trange: Tuple[float, float] = None, use_log: bool = False) -> \
|
||||
Tuple[Tuple[float, float], Optional[Tuple[np.ndarray, np.ndarray]]]:
|
||||
min_index = np.argmin(self.y)
|
||||
t1_min = (self.x[min_index], self.y[min_index])
|
||||
parabola = None
|
||||
self._interpolate_range = (None, None)
|
||||
|
||||
if interpolate is not None:
|
||||
if interpolate not in [0, 1, 2, 3]:
|
||||
raise ValueError(f'Unknown interpolation value {interpolate}')
|
||||
|
||||
if interpolate != 0:
|
||||
if trange is None:
|
||||
left_b = max(min_index - 2, 0)
|
||||
right_b = min(len(self.x), min_index + 3)
|
||||
else:
|
||||
left_b = np.argmin(np.abs(self.x - trange[0]))
|
||||
right_b = np.argmin(np.abs(self.x - trange[1])) + 1
|
||||
|
||||
temp_range = self.x[left_b:right_b]
|
||||
t1_range = self.y[left_b:right_b]
|
||||
if use_log:
|
||||
t1_range = np.log10(t1_range)
|
||||
|
||||
try:
|
||||
x_inter = np.linspace(temp_range[0], temp_range[-1], num=51)
|
||||
|
||||
if interpolate == 1:
|
||||
i_func = np.poly1d(np.polyfit(temp_range, t1_range, 2))
|
||||
elif interpolate == 2:
|
||||
i_func = interp1d(temp_range, t1_range, kind='cubic')
|
||||
else:
|
||||
i_func = Akima1DInterpolator(temp_range, t1_range)
|
||||
|
||||
if use_log:
|
||||
y_inter = 10**i_func(x_inter)
|
||||
else:
|
||||
y_inter = i_func(x_inter)
|
||||
t1_min = (x_inter[np.argmin(y_inter)], y_inter[np.argmin(y_inter)])
|
||||
self._interpolate = i_func
|
||||
parabola = (x_inter, y_inter)
|
||||
self._interpolate_range = (temp_range[0], temp_range[-1])
|
||||
t1_min = x_inter[np.argmin(y_inter)], y_inter[np.argmin(y_inter)]
|
||||
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
self.t1min = t1_min
|
||||
|
||||
return t1_min, parabola
|
||||
|
||||
def correlation_from_t1(self, mode: str = 'raw', interpolate: bool = False, omega: float = None,
|
||||
dist_parameter: Union[float, list, tuple] = None, prefactor: Union[float, tuple, list] = None,
|
||||
coupling_param: list = None, coupling_kwargs: dict = None) -> Tuple[np.ndarray, dict]:
|
||||
|
||||
if self.x is None:
|
||||
raise ValueError('Temperature is not set')
|
||||
@@ -317,40 +651,40 @@ class RelaxationEvaluation(Relaxation):
|
||||
|
||||
if prefactor is None:
|
||||
if coupling_kwargs is None:
|
||||
coupling_kwargs = self._coup_kw
|
||||
coupling_kwargs = self.coup_kw
|
||||
|
||||
if coupling_param is None:
|
||||
prefactor = self._prefactor
|
||||
prefactor = self.prefactor
|
||||
else:
|
||||
prefactor = self._coupling.func(*coupling_param, **coupling_kwargs)
|
||||
prefactor = self.coupling.relax(*coupling_param, **coupling_kwargs)
|
||||
|
||||
if dist_parameter is None:
|
||||
dist_parameter = self._dist_parameter
|
||||
dist_parameter = self.dist_parameter
|
||||
|
||||
fast_idx = self.x > self.t1min[0]
|
||||
|
||||
slow_t1 = self.y[~fast_idx]
|
||||
slow_temp = self.x[~fast_idx]
|
||||
|
||||
correlation_times = np.ones(self.x.shape)
|
||||
correlation_times = np.ones_like(self.x)
|
||||
offset = len(slow_t1)
|
||||
|
||||
base_taus = np.logspace(-10, -7, num=1001)
|
||||
min_tau = base_taus[np.argmin(self.t1(omega, base_taus, *dist_parameter, prefactor=prefactor))]
|
||||
|
||||
taus = np.geomspace(min_tau, 100.*min_tau, num=501)
|
||||
taus = np.geomspace(min_tau, 100. * min_tau, num=501)
|
||||
current_t1 = self.t1(omega, taus, *dist_parameter, prefactor=prefactor)
|
||||
|
||||
for i in range(1, len(slow_t1)+1):
|
||||
for i in range(1, len(slow_t1) + 1):
|
||||
t1_i = slow_t1[-i]
|
||||
|
||||
if interpolate and self._interpolate is not None:
|
||||
if slow_temp[-i] >= self._interpolate_range[0]:
|
||||
t1_i = self._interpolate(slow_temp[-i])
|
||||
|
||||
if min(current_t1) > t1_i:
|
||||
if np.min(current_t1) > t1_i:
|
||||
warn('Correlation time could not be calculated')
|
||||
correlation_times[offset-i] = taus[0]
|
||||
correlation_times[offset - i] = taus[0]
|
||||
continue
|
||||
|
||||
cross_idx = np.where(np.diff(np.sign(current_t1 - t1_i)))[0]
|
||||
@@ -359,13 +693,13 @@ class RelaxationEvaluation(Relaxation):
|
||||
current_t1 = self.t1(omega, taus, *dist_parameter, prefactor=prefactor)
|
||||
cross_idx = np.where(np.diff(np.sign(current_t1 - t1_i)))[0]
|
||||
|
||||
lamb = (t1_i - current_t1[cross_idx]) / (current_t1[cross_idx+1]-current_t1[cross_idx])
|
||||
correlation_times[offset-i] = (taus[cross_idx+1] * lamb + (1 - lamb) * taus[cross_idx])[0]
|
||||
lamb = (t1_i - current_t1[cross_idx]) / (current_t1[cross_idx + 1] - current_t1[cross_idx])
|
||||
correlation_times[offset - i] = (taus[cross_idx + 1] * lamb + (1 - lamb) * taus[cross_idx])[0]
|
||||
|
||||
fast_t1 = self.y[fast_idx]
|
||||
fast_temp = self.x[fast_idx]
|
||||
|
||||
taus = np.geomspace(0.01*min_tau, min_tau, num=501)
|
||||
taus = np.geomspace(0.01 * min_tau, min_tau, num=501)
|
||||
current_t1 = self.t1(omega, taus, *dist_parameter, prefactor=prefactor)
|
||||
|
||||
for i in range(len(fast_t1)):
|
||||
@@ -376,8 +710,8 @@ class RelaxationEvaluation(Relaxation):
|
||||
t1_i = self._interpolate(fast_temp[i])
|
||||
|
||||
if current_t1[-1] > t1_i:
|
||||
correlation_times[offset+i] = taus[-1]
|
||||
warn('Correlation time could not be calculated')
|
||||
correlation_times[offset + i] = taus[-1]
|
||||
warn(f'Correlation time for {correlation_times[offset + i]} could not be calculated')
|
||||
continue
|
||||
|
||||
cross_idx = np.where(np.diff(np.sign(current_t1 - t1_i)))[0]
|
||||
@@ -386,19 +720,22 @@ class RelaxationEvaluation(Relaxation):
|
||||
current_t1 = self.t1(omega, taus, *dist_parameter, prefactor=prefactor)
|
||||
cross_idx = np.where(np.diff(np.sign(current_t1 - t1_i)))[0]
|
||||
|
||||
lamb = (t1_i - current_t1[cross_idx]) / (current_t1[cross_idx+1]-current_t1[cross_idx])
|
||||
correlation_times[offset+i] = (taus[cross_idx+1] * lamb + (1-lamb) * taus[cross_idx])[0]
|
||||
lamb = (t1_i - current_t1[cross_idx]) / (current_t1[cross_idx + 1] - current_t1[cross_idx])
|
||||
correlation_times[offset + i] = (taus[cross_idx + 1] * lamb + (1 - lamb) * taus[cross_idx])[0]
|
||||
|
||||
opts = {'distribution': (self._distribution.name, dist_parameter),
|
||||
'coupling': (self._coupling.name, coupling_param, coupling_kwargs),
|
||||
'frequency': omega/2/np.pi}
|
||||
opts = {'distribution': (self.distribution.name, dist_parameter),
|
||||
'frequency': omega / 2 / np.pi}
|
||||
if self.coupling is not None:
|
||||
opts['coupling'] = (self.coupling.name, self.prefactor, coupling_param, coupling_kwargs)
|
||||
else:
|
||||
opts['coupling'] = (self.prefactor,)
|
||||
|
||||
return np.c_[self.x, self._distribution.mean_value(correlation_times, *dist_parameter, mode=mode)], opts
|
||||
return np.c_[self.x, self.distribution.mean_value(correlation_times, *dist_parameter, mode=mode)], opts
|
||||
|
||||
def _create_minimum_file(self, filename):
|
||||
x = np.geomspace(0.1, 1, num=10001)
|
||||
with Path(filename).open('w') as f:
|
||||
f.write('#broadening\tT1_min/min_Debye\n')
|
||||
f.write('# broadening\tT1_min/min_Debye\n')
|
||||
for i, a in enumerate(np.geomspace(0.1, 10, num=10000)):
|
||||
alpha = a
|
||||
t1_i = self.t1_bpp(1, x, alpha)
|
||||
|
||||
@@ -11,7 +11,7 @@ __all__ = ['NA', 'kb_joule', 'h_joule', 'hbar_joule',
|
||||
NA = 6.02214076e23
|
||||
kb_joule = 1.380649e-23
|
||||
e = 1.602176634e-19
|
||||
h_joule = 6.62607015e-23
|
||||
h_joule = 6.62606896e-34
|
||||
mu0 = 1.256637062124e-6
|
||||
epsilon0 = 8.8541878128e-12
|
||||
|
||||
@@ -38,7 +38,7 @@ spintxt = """\
|
||||
9Be 3/2 100 -3.759666 5.288
|
||||
10B 3 19.9 2.8746786 8.459
|
||||
11B 3/2 80.1 8.5847044 4.059
|
||||
13C 1/2 1.07 6.728 284
|
||||
13C 1/2 1.07 6.728284
|
||||
14N 1 99.632 1.9337792 2.044
|
||||
15N 1/2 0.368 -2.71261804
|
||||
17O 5/2 0.038 -3.62808 -2.558
|
||||
|
||||
@@ -16,7 +16,7 @@ small_greek = [
|
||||
r'\f{Symbol}s\f{} \f{Symbol}t\f{} \f{Symbol}u\f{} \f{Symbol}f\f{} \f{Symbol}c\f{} \f{Symbol}y\f{} '
|
||||
r'\f{Symbol}w\f{}',
|
||||
'alpha beta gamma delta epsilon zeta eta theta iota kappa lambda mu nu ' # plain
|
||||
'xi omicron pi rho sigma sigma tau ypsilon phi chi psi omega'
|
||||
'xi omicron pi rho sigma sigma tau ypsilon phi chi psi omega',
|
||||
]
|
||||
big_greek = [
|
||||
r'\Alpha \Beta \Gamma \Delta \Epsilon \Zeta \Eta \Theta \Iota \Kappa \Lambda \Mu ' # tex
|
||||
@@ -28,7 +28,7 @@ big_greek = [
|
||||
r'\f{Symbol}N\f{} \f{Symbol}X\f{} \f{Symbol}O\f{} \f{Symbol}P\f{} \f{Symbol}R\f{} \f{Symbol}S\f{} '
|
||||
r'\f{Symbol}T\f{} \f{Symbol}Y\f{} \f{Symbol}F\f{} \f{Symbol}C\f{} \f{Symbol}U\f{} \f{Symbol}W\f{}',
|
||||
'Alpha Beta Gamma Delta Epsilon Zeta Eta Theta Iota Kappa Lambda Mu ' # plain
|
||||
'Nu Xi Omicron Pi Rho Sigma Tau Ypsilon Phi Chi Psi Omega'
|
||||
'Nu Xi Omicron Pi Rho Sigma Tau Ypsilon Phi Chi Psi Omega',
|
||||
]
|
||||
special_chars = [
|
||||
r'\infty \int \sum \langle \rangle \pm \perp \para \leftarrow \rightarrow \leftrightarrow \cdot \hbar',
|
||||
@@ -36,13 +36,13 @@ special_chars = [
|
||||
r'\f{Symbol}¥\f{} \f{Symbol}ò\f{} \f{Symbol}å\f{} \f{Symbol}á\f{} \f{Symbol}ñ\f{} \f{Symbol}±\f{} '
|
||||
r'\f{Symbol}^\f{} \f{Symbol}||\f{} \f{Symbol}¬\f{} \f{Symbol}®\f{} \f{Symbol}«\f{} \f{Symbol}×\f{Symbol} '
|
||||
r'h\h{-0.6}\v{0.3}-\v{-0.3}\h{0.3}',
|
||||
'infty int sum < > +- perp para <- -> <-> * hbar'
|
||||
r'infty int sum < > \+- perp para <- -> <-> \* hbar',
|
||||
]
|
||||
funcs = [
|
||||
r'\exp \log \ln \sin \cos \tan',
|
||||
'exp log ln sin cos tan',
|
||||
'exp log ln sin cos tan',
|
||||
'exp log ln sin cos tan'
|
||||
'exp log ln sin cos tan',
|
||||
]
|
||||
delims = [
|
||||
[(r'_{', r'}'), (r'<sub>', r'</sub>'), (r'\\s', r'\\N'), (r'_{', r'}')],
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
matplotlib
|
||||
numpy
|
||||
scipy
|
||||
pyqtgraph
|
||||
bsddb3
|
||||
h5py
|
||||
PyQt5
|
||||
|
||||
@@ -353,6 +353,9 @@
|
||||
<addaction name="actionSave"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionMouse_behaviour"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionPrevious"/>
|
||||
<addaction name="actionNext_window"/>
|
||||
</widget>
|
||||
<widget class="QToolBar" name="toolbar_edit">
|
||||
<property name="sizePolicy">
|
||||
|
||||
+188
-73
@@ -6,15 +6,30 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>319</height>
|
||||
<width>544</width>
|
||||
<height>443</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Read BDS data</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="0">
|
||||
<property name="leftMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item row="1" column="0" rowspan="2">
|
||||
<widget class="QListWidget" name="listWidget">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
|
||||
@@ -24,74 +39,150 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Found temperatures</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Read as:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="eps_checkBox">
|
||||
<property name="text">
|
||||
<string>Permittivity ε</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="modul_checkBox">
|
||||
<property name="text">
|
||||
<string>Modulus 1/ε</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cond_checkBox">
|
||||
<property name="text">
|
||||
<string>Conductivity iεω</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string>X Axis</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="freq_button">
|
||||
<property name="text">
|
||||
<string>Frequency</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">buttonGroup</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="temp_button">
|
||||
<property name="text">
|
||||
<string>Temperature</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">buttonGroup</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<item row="2" column="1">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Y Axis</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="eps_checkBox">
|
||||
<property name="text">
|
||||
<string>Permittivity ε</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="modul_checkBox">
|
||||
<property name="text">
|
||||
<string>Modulus 1/ε</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cond_checkBox">
|
||||
<property name="text">
|
||||
<string>Conductivity iεω</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="loss_checkBox">
|
||||
<property name="text">
|
||||
<string>Loss factor tan(δ)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cap_checkBox">
|
||||
<property name="text">
|
||||
<string>Capacity</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="temp_checkBox">
|
||||
<property name="text">
|
||||
<string>Meas. temperature</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="time_checkBox">
|
||||
<property name="text">
|
||||
<string>Meas. time</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
@@ -101,8 +192,29 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Found entries</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>freq_button</tabstop>
|
||||
<tabstop>temp_button</tabstop>
|
||||
<tabstop>eps_checkBox</tabstop>
|
||||
<tabstop>modul_checkBox</tabstop>
|
||||
<tabstop>cond_checkBox</tabstop>
|
||||
<tabstop>listWidget</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
@@ -112,8 +224,8 @@
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
<x>254</x>
|
||||
<y>411</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
@@ -128,8 +240,8 @@
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
<x>322</x>
|
||||
<y>411</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
@@ -138,4 +250,7 @@
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<buttongroups>
|
||||
<buttongroup name="buttonGroup"/>
|
||||
</buttongroups>
|
||||
</ui>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Reference in New Issue
Block a user