doc added; reading of isochronal bds
This commit is contained in:
parent
a222072b28
commit
6cd630245c
9
doc/examples/README.rst
Normal file
9
doc/examples/README.rst
Normal file
@ -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.
|
6
doc/examples/distribution/README.rst
Normal file
6
doc/examples/distribution/README.rst
Normal file
@ -0,0 +1,6 @@
|
||||
.. _distribution_examples:
|
||||
|
||||
.. _distribution-examples-index:
|
||||
|
||||
Distribution of correlation times
|
||||
=================================
|
50
doc/examples/distribution/plot_ColeCole.py
Normal file
50
doc/examples/distribution/plot_ColeCole.py
Normal file
@ -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()
|
50
doc/examples/distribution/plot_ColeDavidson.py
Normal file
50
doc/examples/distribution/plot_ColeDavidson.py
Normal file
@ -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()
|
53
doc/examples/distribution/plot_HavriliakNegami.py
Normal file
53
doc/examples/distribution/plot_HavriliakNegami.py
Normal file
@ -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()
|
50
doc/examples/distribution/plot_KWW.py
Normal file
50
doc/examples/distribution/plot_KWW.py
Normal file
@ -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()
|
50
doc/examples/distribution/plot_LogGaussian.py
Normal file
50
doc/examples/distribution/plot_LogGaussian.py
Normal file
@ -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()
|
6
doc/examples/nmr/README.rst
Normal file
6
doc/examples/nmr/README.rst
Normal file
@ -0,0 +1,6 @@
|
||||
.. _nmr_examples:
|
||||
|
||||
.. _nmr-examples-index:
|
||||
|
||||
NMR specifics
|
||||
=============
|
67
doc/examples/nmr/plot_RelaxationEvaluation.py
Normal file
67
doc/examples/nmr/plot_RelaxationEvaluation.py
Normal file
@ -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 |
14
doc/source/_templates/autosummary/class.rst
Normal file
14
doc/source/_templates/autosummary/class.rst
Normal file
@ -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>
|
5
doc/source/api/data.rst
Normal file
5
doc/source/api/data.rst
Normal file
@ -0,0 +1,5 @@
|
||||
.. automodule:: nmreval.data
|
||||
:no-members:
|
||||
:no-inherited-members:
|
||||
:no-special-members:
|
||||
|
4
doc/source/api/distributions.rst
Normal file
4
doc/source/api/distributions.rst
Normal file
@ -0,0 +1,4 @@
|
||||
.. automodule:: nmreval.distributions
|
||||
:no-members:
|
||||
:no-inherited-members:
|
||||
:no-special-members:
|
13
doc/source/api/index.rst
Normal file
13
doc/source/api/index.rst
Normal file
@ -0,0 +1,13 @@
|
||||
=========
|
||||
Reference
|
||||
=========
|
||||
|
||||
.. toctree::
|
||||
:caption: Table of contents
|
||||
:maxdepth: 1
|
||||
:glob:
|
||||
|
||||
data
|
||||
models
|
||||
distributions
|
||||
nmr
|
4
doc/source/api/models.rst
Normal file
4
doc/source/api/models.rst
Normal file
@ -0,0 +1,4 @@
|
||||
.. automodule:: nmreval.models
|
||||
:no-members:
|
||||
:no-inherited-members:
|
||||
:no-special-members:
|
4
doc/source/api/nmr.rst
Normal file
4
doc/source/api/nmr.rst
Normal file
@ -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
|
||||
|
179
doc/source/gallery/index.rst
Normal file
179
doc/source/gallery/index.rst
Normal file
@ -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>
|
||||
|
3
doc/source/gallery/searchindex.bak
Normal file
3
doc/source/gallery/searchindex.bak
Normal file
@ -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)
|
BIN
doc/source/gallery/searchindex.dat
Normal file
BIN
doc/source/gallery/searchindex.dat
Normal file
Binary file not shown.
3
doc/source/gallery/searchindex.dir
Normal file
3
doc/source/gallery/searchindex.dir
Normal file
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
30
nmreval/lib/decorator.py
Normal file
30
nmreval/lib/decorator.py
Normal file
@ -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')
|
||||
|
||||
|
||||
|
@ -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 *
|
||||
|
@ -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
|
||||
|
236
nmreval/nmr/coupling.py
Normal file
236
nmreval/nmr/coupling.py
Normal file
@ -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
|
@ -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'}')],
|
||||
|
8
requirements.txt
Normal file
8
requirements.txt
Normal file
@ -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">
|
||||
|
@ -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>
|
||||
|
9901
resources/nmr/Cole-Cole_min.dat
Normal file
9901
resources/nmr/Cole-Cole_min.dat
Normal file
File diff suppressed because it is too large
Load Diff
9901
resources/nmr/Cole-Davidson_min.dat
Normal file
9901
resources/nmr/Cole-Davidson_min.dat
Normal file
File diff suppressed because it is too large
Load Diff
9001
resources/nmr/KWW_min.dat
Normal file
9001
resources/nmr/KWW_min.dat
Normal file
File diff suppressed because it is too large
Load Diff
10003
resources/nmr/Log-Gaussian_min.dat
Normal file
10003
resources/nmr/Log-Gaussian_min.dat
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Loading…
Reference in New Issue
Block a user