spinlab 0.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- spinlab/__init__.py +27 -0
- spinlab/analysis/__init__.py +6 -0
- spinlab/analysis/hydration.py +555 -0
- spinlab/analysis/peaks.py +249 -0
- spinlab/analysis/relaxation_fit.py +29 -0
- spinlab/analysis/simulate_enhancement_profiles.py +109 -0
- spinlab/config/__init__.py +0 -0
- spinlab/config/config.py +81 -0
- spinlab/constants/__init__.py +5 -0
- spinlab/constants/constants.py +27 -0
- spinlab/constants/mrProperties.py +236 -0
- spinlab/constants/radicalProperties.py +147 -0
- spinlab/core/__init__.py +5 -0
- spinlab/core/base.py +1268 -0
- spinlab/core/coord.py +225 -0
- spinlab/core/data.py +335 -0
- spinlab/core/ufunc.py +67 -0
- spinlab/core/util.py +159 -0
- spinlab/fitting/__init__.py +3 -0
- spinlab/fitting/general.py +106 -0
- spinlab/io/__init__.py +19 -0
- spinlab/io/bes3t.py +453 -0
- spinlab/io/cnsi.py +139 -0
- spinlab/io/delta.py +523 -0
- spinlab/io/h5.py +215 -0
- spinlab/io/load.py +334 -0
- spinlab/io/load_csv.py +98 -0
- spinlab/io/logs.py +84 -0
- spinlab/io/mat.py +153 -0
- spinlab/io/power.py +109 -0
- spinlab/io/prospa.py +340 -0
- spinlab/io/random.py +36 -0
- spinlab/io/rs2d.py +111 -0
- spinlab/io/save.py +52 -0
- spinlab/io/specman.py +358 -0
- spinlab/io/tnmr.py +178 -0
- spinlab/io/topspin.py +625 -0
- spinlab/io/vna.py +76 -0
- spinlab/io/vnmrj.py +236 -0
- spinlab/io/winepr.py +205 -0
- spinlab/math/__init__.py +6 -0
- spinlab/math/lineshape.py +142 -0
- spinlab/math/pulses.py +255 -0
- spinlab/math/relaxation.py +133 -0
- spinlab/math/window.py +161 -0
- spinlab/plotting/__init__.py +9 -0
- spinlab/plotting/colors.py +9 -0
- spinlab/plotting/general.py +285 -0
- spinlab/plotting/image.py +68 -0
- spinlab/plotting/stack_plot.py +69 -0
- spinlab/processing/__init__.py +12 -0
- spinlab/processing/align.py +76 -0
- spinlab/processing/apodization.py +96 -0
- spinlab/processing/average.py +28 -0
- spinlab/processing/conversion.py +247 -0
- spinlab/processing/fft.py +182 -0
- spinlab/processing/helpers.py +577 -0
- spinlab/processing/integration.py +130 -0
- spinlab/processing/interpolation.py +70 -0
- spinlab/processing/offset.py +133 -0
- spinlab/processing/phase.py +508 -0
- spinlab/reporting/__init__.py +1 -0
- spinlab/version.py +1 -0
- spinlab/widgets/__init__.py +4 -0
- spinlab/widgets/align_widget.py +77 -0
- spinlab/widgets/phase_widget.py +118 -0
- spinlab-0.0.0.dist-info/METADATA +56 -0
- spinlab-0.0.0.dist-info/RECORD +71 -0
- spinlab-0.0.0.dist-info/WHEEL +5 -0
- spinlab-0.0.0.dist-info/licenses/LICENSE.txt +21 -0
- spinlab-0.0.0.dist-info/top_level.txt +1 -0
spinlab/__init__.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""SpinLab - Bringing the Power of Python to Spin-NMR Spectroscopy"""
|
|
2
|
+
|
|
3
|
+
from .core.data import SpinData
|
|
4
|
+
from .core.ufunc import *
|
|
5
|
+
from .core.util import *
|
|
6
|
+
|
|
7
|
+
from .constants import *
|
|
8
|
+
from .fitting import *
|
|
9
|
+
from .math import *
|
|
10
|
+
|
|
11
|
+
from .io import *
|
|
12
|
+
from .io.save import save
|
|
13
|
+
from .io.load import load
|
|
14
|
+
|
|
15
|
+
from .analysis.relaxation_fit import *
|
|
16
|
+
from .analysis.hydration import hydration
|
|
17
|
+
from .analysis.simulate_enhancement_profiles import *
|
|
18
|
+
from .analysis.peaks import *
|
|
19
|
+
|
|
20
|
+
from .processing import *
|
|
21
|
+
from .widgets import *
|
|
22
|
+
from .plotting import *
|
|
23
|
+
from .reporting import *
|
|
24
|
+
from .version import __version__
|
|
25
|
+
|
|
26
|
+
# config
|
|
27
|
+
from .config.config import SpinLAB_CONFIG
|
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
import numpy as _np
|
|
2
|
+
from scipy import optimize
|
|
3
|
+
import warnings
|
|
4
|
+
from ..constants import constants as _const
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def calculate_smax(spin_C=False):
|
|
8
|
+
r"""Returns maximal saturation factor.
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
spin_C (float): unpaired spin concentration (M)
|
|
12
|
+
|
|
13
|
+
Returns:
|
|
14
|
+
smax (float): maximal saturation factor (unitless)
|
|
15
|
+
|
|
16
|
+
.. math::
|
|
17
|
+
\mathrm{s_{max}} = 1 - (2 / (3 + (3 * (\mathrm{spin\_C} * 198.7))))
|
|
18
|
+
|
|
19
|
+
M.T. Türke, M. Bennati, Phys. Chem. Chem. Phys. 13 (2011) 3630. & J. Hyde, J. Chien, J. Freed, J. Chem. Phys. 48 (1968) 4211.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
return 1 - (2 / (3 + (3 * (spin_C * 198.7))))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def interpolate_T1(
|
|
26
|
+
E_powers=False,
|
|
27
|
+
T1_powers=False,
|
|
28
|
+
T1_array=False,
|
|
29
|
+
interpolate_method="linear",
|
|
30
|
+
delta_T1_water=False,
|
|
31
|
+
T1_water=False,
|
|
32
|
+
macro_C=False,
|
|
33
|
+
spin_C=1,
|
|
34
|
+
T10=2.0,
|
|
35
|
+
T100=2.5,
|
|
36
|
+
):
|
|
37
|
+
"""Returns interpolated T1 data.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
E_powers (numpy.array): The microwave powers at which to evaluate
|
|
41
|
+
T1_powers (numpy.array): The microwave powers of the T1s to interpolate
|
|
42
|
+
T1_array (numpy.array): The original T1s (s)
|
|
43
|
+
interpolate_method (str): "second_order" or "linear"
|
|
44
|
+
spin_C (float): unpaired electron spin concentration (M)
|
|
45
|
+
T10 (float): T1 measured with unpaired electrons (s)
|
|
46
|
+
T100 (float): T1 measured without unpaired electrons (s)
|
|
47
|
+
delta_T1_water (optional) (float): change in T1 of water at max microwave power (s)
|
|
48
|
+
T1_water (optional) (float): T1 of pure water (s)
|
|
49
|
+
macro_C (optional) (float): concentration of macromolecule (M)
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
interpolated_T1 (numpy.array): Array of T1 values same shape as E_powers and E_array
|
|
53
|
+
|
|
54
|
+
T1 data is interpolated using Eq. 39 of http://dx.doi.org/10.1016/j.pnmrs.2013.06.001 for "linear" or Eq. 22 of https://doi.org/10.1016/bs.mie.2018.09.024 for "second_order"
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
# 2nd order fit, Franck and Han MIE (Eq. 22) and (Eq. 23)
|
|
58
|
+
if interpolate_method == "second_order":
|
|
59
|
+
if not macro_C:
|
|
60
|
+
macro_C = spin_C
|
|
61
|
+
|
|
62
|
+
if not delta_T1_water:
|
|
63
|
+
delta_T1_water = T1_array[-1] - T1_array[0]
|
|
64
|
+
if not T1_water:
|
|
65
|
+
T1_water = T100
|
|
66
|
+
|
|
67
|
+
kHH = (1.0 / T10 - 1.0 / T1_water) / macro_C
|
|
68
|
+
krp = (
|
|
69
|
+
(1.0 / T1_array)
|
|
70
|
+
- (1.0 / (T1_water + delta_T1_water * T1_powers))
|
|
71
|
+
- (kHH * (macro_C))
|
|
72
|
+
) / (spin_C)
|
|
73
|
+
|
|
74
|
+
p = _np.polyfit(T1_powers, krp, 2)
|
|
75
|
+
T1_fit_2order = _np.polyval(p, E_powers)
|
|
76
|
+
|
|
77
|
+
interpolated_T1 = 1.0 / (
|
|
78
|
+
((spin_C) * T1_fit_2order)
|
|
79
|
+
+ (1.0 / (T1_water + delta_T1_water * E_powers))
|
|
80
|
+
+ (kHH * (macro_C))
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# linear fit, Franck et al. PNMRS (Eq. 39)
|
|
84
|
+
elif interpolate_method == "linear":
|
|
85
|
+
linear_t1 = 1.0 / ((1.0 / T1_array) - (1.0 / T10) + (1.0 / T100))
|
|
86
|
+
|
|
87
|
+
p = _np.polyfit(T1_powers, linear_t1, 1)
|
|
88
|
+
T1_fit_linear = _np.polyval(p, E_powers)
|
|
89
|
+
|
|
90
|
+
interpolated_T1 = T1_fit_linear / (
|
|
91
|
+
1.0 + (T1_fit_linear / T10) - (T1_fit_linear / T100)
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
else:
|
|
95
|
+
raise Exception("invalid interpolate_method")
|
|
96
|
+
|
|
97
|
+
return interpolated_T1
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def calculate_ksigma_array(powers=False, ksigma_smax=95.4, p_12=False):
|
|
101
|
+
"""Function to calcualte ksig array for any given ksigma and p_12
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
powers (numpy.array): Array of powers
|
|
105
|
+
ksigma_smax (float): product of ksigma and smax (s^-1 * M^-1)
|
|
106
|
+
p_12 (float): power at half max for ksigma fit
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
ksig_fit (numpy.array): calculated ksigma array
|
|
110
|
+
|
|
111
|
+
J.M. Franck et al. / Progress in Nuclear Magnetic Resonance Spectroscopy 74 (2013) 33–56
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
# Right side of Eq. 42. This function should fit to ksig_sp
|
|
115
|
+
ksig_fit = (ksigma_smax * powers) / (p_12 + powers)
|
|
116
|
+
|
|
117
|
+
return ksig_fit
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def calculate_ksigma(ksigma_sp=False, powers=False, smax=1):
|
|
121
|
+
"""Get ksigma and E_power at half max of ksig
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
ksig (numpy.array): Array of ksigmas
|
|
125
|
+
powers (numpy.array): Array of E_powers
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
ksigma (float): calculated ksigma
|
|
129
|
+
ksigma_stdd (float): standard deviation in ksigma
|
|
130
|
+
p_12 (float): power at half max for ksigma fit
|
|
131
|
+
|
|
132
|
+
J.M. Franck et al. / Progress in Nuclear Magnetic Resonance Spectroscopy 74 (2013) 33–56
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
# curve fitting
|
|
136
|
+
# see https://docs.scipy.org/doc/scipy/reference/optimize.html
|
|
137
|
+
popt, pcov = optimize.curve_fit(
|
|
138
|
+
calculate_ksigma_array,
|
|
139
|
+
powers,
|
|
140
|
+
ksigma_sp,
|
|
141
|
+
p0=[95.4 / 2, (max(powers) * 0.1)],
|
|
142
|
+
method="lm",
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
assert popt[0] > 0, "Unexpected ksigma value: %d < 0" % popt[0]
|
|
146
|
+
|
|
147
|
+
ksigma_smax = popt[0]
|
|
148
|
+
p_12 = popt[1]
|
|
149
|
+
ksigma_std = _np.sqrt(_np.diag(pcov))
|
|
150
|
+
ksigma_stdd = ksigma_std[0] / smax
|
|
151
|
+
|
|
152
|
+
ksigma_fit = calculate_ksigma_array(powers, ksigma_smax, p_12)
|
|
153
|
+
|
|
154
|
+
ksigma = ksigma_smax / smax
|
|
155
|
+
|
|
156
|
+
return ksigma, ksigma_stdd, ksigma_fit
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def calculate_xi(tcorr=54e-12, omega_e=0.0614, omega_H=9.3231e-05):
|
|
160
|
+
"""Returns coupling_factor for any given tcorr
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
tcorr (float): translational diffusion correlation time (s)
|
|
164
|
+
omega_e (float): electron gyromagnetic ratio
|
|
165
|
+
omega_H (float): proton gyromagnetic ratio
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
xi (float): coupling factor
|
|
169
|
+
|
|
170
|
+
J.M. Franck et al. / Progress in Nuclear Magnetic Resonance Spectroscopy 74 (2013) 33–56
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
# Using Franck et al. PNMRS (2013)
|
|
174
|
+
if tcorr < 0.1:
|
|
175
|
+
tcorr *= 1e12
|
|
176
|
+
|
|
177
|
+
zdiff = _np.sqrt(1j * (omega_e - omega_H) * tcorr)
|
|
178
|
+
zsum = _np.sqrt(1j * (omega_e + omega_H) * tcorr)
|
|
179
|
+
zH = _np.sqrt(1j * omega_H * tcorr)
|
|
180
|
+
|
|
181
|
+
# (Eq. 2)
|
|
182
|
+
Jdiff = (1 + (zdiff / 4)) / (1 + zdiff + ((4 * (zdiff**2)) / 9) + ((zdiff**3) / 9))
|
|
183
|
+
|
|
184
|
+
Jsum = (1 + (zsum / 4)) / (1 + zsum + ((4 * (zsum**2)) / 9) + ((zsum**3) / 9))
|
|
185
|
+
|
|
186
|
+
JH = (1 + (zH / 4)) / (1 + zH + ((4 * (zH**2)) / 9) + ((zH**3) / 9))
|
|
187
|
+
|
|
188
|
+
# (Eq. 23) calculation of coupling_factor from the spectral density functions
|
|
189
|
+
xi = ((6 * _np.real(Jdiff)) - _np.real(Jsum)) / (
|
|
190
|
+
(6 * _np.real(Jdiff)) + (3 * _np.real(JH)) + _np.real(Jsum)
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
return xi
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def calculate_tcorr(coupling_factor=0.27, omega_e=0.0614, omega_H=9.3231e-05):
|
|
197
|
+
"""Returns translational correlation time (tcorr) in pico second
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
coupling_factor (float): coupling factor
|
|
201
|
+
omega_e (float): electron gyromagnetic ratio
|
|
202
|
+
omega_H (float): proton gyromagnetic ratio
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
tcorr (float): translational diffusion correlation time (s)
|
|
206
|
+
|
|
207
|
+
J.M. Franck et al. / Progress in Nuclear Magnetic Resonance Spectroscopy 74 (2013) 33–56
|
|
208
|
+
"""
|
|
209
|
+
|
|
210
|
+
# root finding
|
|
211
|
+
# see https://docs.scipy.org/doc/scipy/reference/optimize.html
|
|
212
|
+
result = optimize.root_scalar(
|
|
213
|
+
lambda t_corr: calculate_xi(t_corr, omega_e=omega_e, omega_H=omega_H)
|
|
214
|
+
- coupling_factor,
|
|
215
|
+
method="brentq",
|
|
216
|
+
bracket=[1, 1e5],
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
if not result.converged:
|
|
220
|
+
raise ValueError("Could not find tcorr")
|
|
221
|
+
|
|
222
|
+
tcorr = result.root
|
|
223
|
+
return tcorr * 1e-12
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def calculate_uncorrected_Ep(
|
|
227
|
+
uncorrected_xi=0.33,
|
|
228
|
+
p_12_unc=0,
|
|
229
|
+
E_powers=False,
|
|
230
|
+
T10=2.0,
|
|
231
|
+
T100=2.5,
|
|
232
|
+
omega_ratio=658.5792,
|
|
233
|
+
smax=1,
|
|
234
|
+
):
|
|
235
|
+
"""Function for E(p) for any given xi and p_12
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
uncorrected_xi (float): uncorrected coupling factor
|
|
239
|
+
p_12_unc (float): power at half max for uncorrected_xi fit
|
|
240
|
+
E_array (numpy.array): Array of enhancements
|
|
241
|
+
E_powers (numpy.array): Array of E_powers
|
|
242
|
+
T10 (float): T1(0), proton T1 with microwave power=0 (s)
|
|
243
|
+
T100 (float): T10(0), proton T1 with spin_C=0 and microwave power=0 (s)
|
|
244
|
+
omega_ratio (float): ratio of electron & proton gyromagnetic ratios
|
|
245
|
+
smax (float): maximal saturation factor
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
Ep_fit (numpy.array): uncorrected enhancement curve
|
|
249
|
+
|
|
250
|
+
J.M. Franck et al. / Progress in Nuclear Magnetic Resonance Spectroscopy 74 (2013) 33–56
|
|
251
|
+
"""
|
|
252
|
+
|
|
253
|
+
# Right side of Eq. 42. This function should fit to ksig_sp
|
|
254
|
+
Ep_fit = 1 - (
|
|
255
|
+
(uncorrected_xi * (1 - (T10 / T100)) * omega_ratio)
|
|
256
|
+
* ((E_powers * smax) / (p_12_unc + E_powers))
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
return Ep_fit
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def _residual_Ep(
|
|
263
|
+
x,
|
|
264
|
+
E_array: _np.array,
|
|
265
|
+
E_powers: _np.array,
|
|
266
|
+
T10: float,
|
|
267
|
+
T100: float,
|
|
268
|
+
omega_ratio: float,
|
|
269
|
+
smax: float,
|
|
270
|
+
):
|
|
271
|
+
"""Function for residuals between E(p) for any given xi and p_12 and the experimental E_array
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
x (list): [uncorrected coupling factor, power at half max for uncorrected_xi fit]
|
|
275
|
+
E_array (numpy.array): Array of enhancements
|
|
276
|
+
E_powers (numpy.array): Array of E_power
|
|
277
|
+
T10 (float): T1(0), proton T1 with microwave power=0 (s)
|
|
278
|
+
T100 (float): T10(0), proton T1 with spin_C=0 and microwave power=0 (s)
|
|
279
|
+
omega_ratio (float): ratio of electron & proton gyromagnetic ratios
|
|
280
|
+
smax (float): maximal saturation factor
|
|
281
|
+
|
|
282
|
+
Returns:
|
|
283
|
+
Ep_fit (numpy.array): uncorrected enhancement curve
|
|
284
|
+
|
|
285
|
+
J.M. Franck et al. / Progress in Nuclear Magnetic Resonance Spectroscopy 74 (2013) 33–56
|
|
286
|
+
"""
|
|
287
|
+
|
|
288
|
+
return E_array - calculate_uncorrected_Ep(
|
|
289
|
+
uncorrected_xi=x[0],
|
|
290
|
+
p_12_unc=x[1],
|
|
291
|
+
E_powers=E_powers,
|
|
292
|
+
T10=T10,
|
|
293
|
+
T100=T100,
|
|
294
|
+
omega_ratio=omega_ratio,
|
|
295
|
+
smax=smax,
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def calculate_uncorrected_xi(
|
|
300
|
+
E_array=False,
|
|
301
|
+
E_powers=False,
|
|
302
|
+
T10=2.0,
|
|
303
|
+
T100=2.5,
|
|
304
|
+
omega_ratio=658.5792,
|
|
305
|
+
smax=1,
|
|
306
|
+
):
|
|
307
|
+
"""Get coupling_factor and E_power at half saturation
|
|
308
|
+
|
|
309
|
+
Args:
|
|
310
|
+
E_array (numpy.array): Array of enhancements
|
|
311
|
+
E_powers (numpy.array): Array of powers
|
|
312
|
+
T10 (float): T1(0), proton T1 with microwave power=0 (s)
|
|
313
|
+
T100 (float): T10(0), proton T1 with spin_C=0 and microwave power=0 (s)
|
|
314
|
+
omega_ratio (float): ratio of electron & proton gyromagnetic ratios
|
|
315
|
+
smax (float): maximal saturation factor
|
|
316
|
+
|
|
317
|
+
Returns:
|
|
318
|
+
uncorrected_xi (float): uncorrected coupling factor
|
|
319
|
+
p_12_unc (float): power at half max for uncorrected_xi fit
|
|
320
|
+
|
|
321
|
+
J.M. Franck et al.; Progress in Nuclear Magnetic Resonance Spectroscopy 74 (2013) 33–56
|
|
322
|
+
"""
|
|
323
|
+
|
|
324
|
+
# least-squares fitting.
|
|
325
|
+
# see https://docs.scipy.org/doc/scipy/reference/optimize.html
|
|
326
|
+
results = optimize.least_squares(
|
|
327
|
+
fun=_residual_Ep,
|
|
328
|
+
x0=[0.27, (max(E_powers) * 0.1)],
|
|
329
|
+
args=(E_array, E_powers, T10, T100, omega_ratio, smax),
|
|
330
|
+
jac="2-point",
|
|
331
|
+
method="lm",
|
|
332
|
+
)
|
|
333
|
+
if not results.success:
|
|
334
|
+
raise ValueError("Could not fit Ep")
|
|
335
|
+
assert results.x[0] > 0, "Unexpected coupling_factor value: %d < 0" % results.x[0]
|
|
336
|
+
|
|
337
|
+
uncorrected_xi = results.x[0]
|
|
338
|
+
p_12_unc = results.x[1]
|
|
339
|
+
|
|
340
|
+
return uncorrected_xi, p_12_unc
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def hydration(data={}, constants={}):
|
|
344
|
+
"""Function for performing ODNP calculations
|
|
345
|
+
|
|
346
|
+
Args:
|
|
347
|
+
data (dict) : keys and values are described in the example
|
|
348
|
+
constants (dict) : (optional) keys and values are described in the example
|
|
349
|
+
|
|
350
|
+
Returns:
|
|
351
|
+
(dict) : keys and values are described in the example
|
|
352
|
+
|
|
353
|
+
J.M. Franck et al.; Progress in Nuclear Magnetic Resonance Spectroscopy 74 (2013) 33–56
|
|
354
|
+
https://www.sciencedirect.com/science/article/abs/pii/S0079656513000629
|
|
355
|
+
|
|
356
|
+
J.M. Franck, S. Han; Methods in Enzymology, Chapter 5, Volume 615, (2019) 131-175
|
|
357
|
+
https://www.sciencedirect.com/science/article/abs/pii/S0076687918303872
|
|
358
|
+
"""
|
|
359
|
+
|
|
360
|
+
if not data:
|
|
361
|
+
raise ValueError("Please supply a valid data dictionary, see example")
|
|
362
|
+
|
|
363
|
+
if "hydration_inputs" in data.keys():
|
|
364
|
+
warnings.warn(
|
|
365
|
+
"The workspace concept is depreciated, see the example in the docs for the new syntax"
|
|
366
|
+
)
|
|
367
|
+
_data = data["hydration_inputs"]
|
|
368
|
+
if "hydration_constants" in data.keys():
|
|
369
|
+
constants = data["hydration_constants"]
|
|
370
|
+
data = _data
|
|
371
|
+
|
|
372
|
+
if "tcorr_bulk" in constants.keys() and constants["tcorr_bulk"] > 0.1:
|
|
373
|
+
warnings.warn(
|
|
374
|
+
"tcorr_bulk should be given in seconds, support for picoseconds will be removed in a future release"
|
|
375
|
+
)
|
|
376
|
+
constants["tcorr_bulk"] *= 1e-12
|
|
377
|
+
|
|
378
|
+
if "macro_C" in constants.keys() and constants["macro_C"] > 0.1:
|
|
379
|
+
warnings.warn(
|
|
380
|
+
"macro_C should be given in molar, support for micromolar will be removed in a future release"
|
|
381
|
+
)
|
|
382
|
+
constants["macro_C"] *= 1e-6
|
|
383
|
+
|
|
384
|
+
if data["spin_C"] > 0.1:
|
|
385
|
+
warnings.warn(
|
|
386
|
+
"spin_C should be given in molar, support for micromolar will be removed in a future release"
|
|
387
|
+
)
|
|
388
|
+
data["spin_C"] *= 1e-6
|
|
389
|
+
|
|
390
|
+
if "field" in data.keys():
|
|
391
|
+
warnings.warn(
|
|
392
|
+
"keyword 'field' is depreciated, please use 'magnetic_field' from now on"
|
|
393
|
+
)
|
|
394
|
+
if "magnetic_field" in data.keys():
|
|
395
|
+
warnings.warn(
|
|
396
|
+
"you supplied both 'field' and 'magnetic_field', only 'magnetic_field' will be used"
|
|
397
|
+
)
|
|
398
|
+
else:
|
|
399
|
+
data["magnetic_field"] = data["field"]
|
|
400
|
+
data.pop("field")
|
|
401
|
+
|
|
402
|
+
if data["magnetic_field"] > 3:
|
|
403
|
+
warnings.warn(
|
|
404
|
+
"magnetic_field should be given in T, support for mT will be removed in a future release"
|
|
405
|
+
)
|
|
406
|
+
data["magnetic_field"] *= 1e-3
|
|
407
|
+
|
|
408
|
+
standard_constants = {
|
|
409
|
+
"ksigma_bulk": 95.4,
|
|
410
|
+
"krho_bulk": 353.4,
|
|
411
|
+
"klow_bulk": 366,
|
|
412
|
+
"tcorr_bulk": 54e-12,
|
|
413
|
+
"D_H2O": 2.3e-9,
|
|
414
|
+
"D_SL": 4.1e-10,
|
|
415
|
+
"delta_T1_water": False,
|
|
416
|
+
"T1_water": False,
|
|
417
|
+
"macro_C": False,
|
|
418
|
+
}
|
|
419
|
+
# these constants have been compiled from the various ODNP literature
|
|
420
|
+
|
|
421
|
+
odnp_constants = {**standard_constants, **constants}
|
|
422
|
+
|
|
423
|
+
if data["smax_model"] == "tethered":
|
|
424
|
+
# Option 1, tether spin label
|
|
425
|
+
s_max = 1 # (section 2.2) maximal saturation factor
|
|
426
|
+
|
|
427
|
+
elif data["smax_model"] == "free":
|
|
428
|
+
# Option 2, free spin probe
|
|
429
|
+
s_max = calculate_smax(data["spin_C"]) # from:
|
|
430
|
+
# M.T. Türke, M. Bennati, Phys. Chem. Chem. Phys. 13 (2011) 3630. &
|
|
431
|
+
# J. Hyde, J. Chien, J. Freed, J. Chem. Phys. 48 (1968) 4211.
|
|
432
|
+
|
|
433
|
+
elif isinstance(data["smax_model"], float):
|
|
434
|
+
# Option 3, manual input of smax
|
|
435
|
+
if not (data["smax_model"] <= 1 and data["smax_model"] > 0):
|
|
436
|
+
raise ValueError(
|
|
437
|
+
"if given directly, smax must be type float between 0 and 1"
|
|
438
|
+
)
|
|
439
|
+
s_max = data["smax_model"]
|
|
440
|
+
else:
|
|
441
|
+
raise ValueError(
|
|
442
|
+
"'smax_model' must be 'tethered', 'free', or a float between 0 and 1"
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
omega_e = 1.76085963023e-1 * data["magnetic_field"]
|
|
446
|
+
# gamma_e in 1/ps for the tcorr unit, then correct by magnetic_field in T.
|
|
447
|
+
# gamma_e is from NIST. The magnetic_field cancels in the following omega_ratio but you
|
|
448
|
+
# need these individually for the spectral density functions later.
|
|
449
|
+
|
|
450
|
+
omega_H = 2.6752218744e-4 * data["magnetic_field"]
|
|
451
|
+
# gamma_H in 1/ps for the tcorr unit, then correct by magnetic_field in T.
|
|
452
|
+
# gamma_H is from NIST. The magnetic_field cancels in the following omega_ratio but you
|
|
453
|
+
# need these individually for the spectral density functions later.
|
|
454
|
+
|
|
455
|
+
omega_ratio = (omega_e / (2 * _const.pi)) / (omega_H / (2 * _const.pi))
|
|
456
|
+
# (Eq. 4-6) ratio of omega_e and omega_H, divide by (2*pi) to get angular
|
|
457
|
+
# frequency units in order to correspond to S_0/I_0, this is also ~= to the
|
|
458
|
+
# ratio of the resonance frequencies for the experiment, i.e. MW freq/RF freq
|
|
459
|
+
|
|
460
|
+
if "T1_powers" in data.keys():
|
|
461
|
+
T1p = interpolate_T1(
|
|
462
|
+
E_powers=data["E_powers"],
|
|
463
|
+
T1_powers=data["T1_powers"],
|
|
464
|
+
T1_array=data["T1_array"],
|
|
465
|
+
interpolate_method=data["interpolate_method"],
|
|
466
|
+
delta_T1_water=odnp_constants["delta_T1_water"],
|
|
467
|
+
T1_water=odnp_constants["T1_water"],
|
|
468
|
+
macro_C=odnp_constants["macro_C"],
|
|
469
|
+
spin_C=data["spin_C"],
|
|
470
|
+
T10=data["T10"],
|
|
471
|
+
T100=data["T100"],
|
|
472
|
+
)
|
|
473
|
+
else:
|
|
474
|
+
if len(data["T1_array"]) == len(data["E_array"]):
|
|
475
|
+
T1p = data["T1_array"]
|
|
476
|
+
else:
|
|
477
|
+
raise ValueError(
|
|
478
|
+
"'T1_array' must be equal in length to 'E_array'. Otherwise give 'T1_powers' equal in length to 'T1_array' in order to interpolate."
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
ksigma_array = (1 - data["E_array"]) / (data["spin_C"] * omega_ratio * T1p)
|
|
482
|
+
# (Eq. 41) this calculates the array of ksigma*s(p) from the enhancement array,
|
|
483
|
+
# dividing by the T1 array for the "corrected" analysis
|
|
484
|
+
|
|
485
|
+
ksigma, ksigma_stdd, ksigma_fit = calculate_ksigma(
|
|
486
|
+
ksigma_array, data["E_powers"], s_max
|
|
487
|
+
)
|
|
488
|
+
# fit to the right side of Eq. 42 to get (ksigma*smax) and half of the E_power at s_max, called p_12 here
|
|
489
|
+
|
|
490
|
+
krho = ((1 / data["T10"]) - (1 / data["T100"])) / (
|
|
491
|
+
data["spin_C"]
|
|
492
|
+
) # (Eq. 36) "self" relaxivity, unit is s^-1 M^-1
|
|
493
|
+
|
|
494
|
+
coupling_factor = ksigma / krho # coupling factor, unitless
|
|
495
|
+
|
|
496
|
+
tcorr = calculate_tcorr(coupling_factor, omega_e, omega_H)
|
|
497
|
+
# (Eq. 21-23) this calls the fit to the spectral density functions. The fit
|
|
498
|
+
# optimizes the value of tcorr in the calculation of coupling_factor, the correct tcorr
|
|
499
|
+
# is the one for which the calculation of coupling_factor from the spectral density
|
|
500
|
+
# functions matches the coupling_factor found experimentally. tcorr unit is ps
|
|
501
|
+
|
|
502
|
+
Dlocal = (odnp_constants["tcorr_bulk"] / tcorr) * (
|
|
503
|
+
odnp_constants["D_H2O"] + odnp_constants["D_SL"]
|
|
504
|
+
)
|
|
505
|
+
# (Eq. 19-20) local diffusivity, i.e. diffusivity of the water near the spin label
|
|
506
|
+
|
|
507
|
+
klow = ((5 * krho) - (7 * ksigma)) / 3
|
|
508
|
+
# section 6, (Eq. 13). this describes the relatively slowly diffusing water
|
|
509
|
+
# near the spin label, sometimes called "bound" water.
|
|
510
|
+
# This is defined in its most compact form in:
|
|
511
|
+
# Frank, JM and Han, SI; Chapter Five - Overhauser Dynamic Nuclear Polarization
|
|
512
|
+
# for the Study of Hydration Dynamics, Explained. Methods in Enzymology, Volume 615, 2019
|
|
513
|
+
# But also explained well in:
|
|
514
|
+
# Franck, JM, et. al.; "Anomalously Rapid Hydration Water Diffusion Dynamics
|
|
515
|
+
# Near DNA Surfaces" J. Am. Chem. Soc. 2015, 137, 12013−12023.
|
|
516
|
+
|
|
517
|
+
xi_unc, p_12_unc = calculate_uncorrected_xi(
|
|
518
|
+
data["E_array"],
|
|
519
|
+
data["E_powers"],
|
|
520
|
+
data["T10"],
|
|
521
|
+
data["T100"],
|
|
522
|
+
omega_ratio,
|
|
523
|
+
s_max,
|
|
524
|
+
)
|
|
525
|
+
# (Eqs. 7 and 44) this calculates the coupling factor using the "uncorrected" analysis
|
|
526
|
+
|
|
527
|
+
uncorrected_Ep = calculate_uncorrected_Ep(
|
|
528
|
+
xi_unc,
|
|
529
|
+
p_12_unc,
|
|
530
|
+
data["E_powers"],
|
|
531
|
+
data["T10"],
|
|
532
|
+
data["T100"],
|
|
533
|
+
omega_ratio,
|
|
534
|
+
s_max,
|
|
535
|
+
)
|
|
536
|
+
# (Eqs. 7 and 44) this calculates the "uncorrected" enhancement array using xi_unc
|
|
537
|
+
|
|
538
|
+
return {
|
|
539
|
+
"uncorrected_Ep": uncorrected_Ep,
|
|
540
|
+
"uncorrected_xi": xi_unc,
|
|
541
|
+
"interpolated_T1": T1p,
|
|
542
|
+
"ksigma_array": ksigma_array,
|
|
543
|
+
"ksigma_fit": ksigma_fit,
|
|
544
|
+
"ksigma": ksigma,
|
|
545
|
+
"ksigma_stdd": ksigma_stdd,
|
|
546
|
+
"ksigma_bulk_ratio": ksigma / odnp_constants["ksigma_bulk"],
|
|
547
|
+
"krho": krho,
|
|
548
|
+
"krho_bulk_ratio": krho / odnp_constants["krho_bulk"],
|
|
549
|
+
"klow": klow,
|
|
550
|
+
"klow_bulk_ratio": klow / odnp_constants["klow_bulk"],
|
|
551
|
+
"coupling_factor": coupling_factor,
|
|
552
|
+
"tcorr": tcorr,
|
|
553
|
+
"tcorr_bulk_ratio": tcorr / odnp_constants["tcorr_bulk"],
|
|
554
|
+
"Dlocal": Dlocal,
|
|
555
|
+
}
|