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.
Files changed (71) hide show
  1. spinlab/__init__.py +27 -0
  2. spinlab/analysis/__init__.py +6 -0
  3. spinlab/analysis/hydration.py +555 -0
  4. spinlab/analysis/peaks.py +249 -0
  5. spinlab/analysis/relaxation_fit.py +29 -0
  6. spinlab/analysis/simulate_enhancement_profiles.py +109 -0
  7. spinlab/config/__init__.py +0 -0
  8. spinlab/config/config.py +81 -0
  9. spinlab/constants/__init__.py +5 -0
  10. spinlab/constants/constants.py +27 -0
  11. spinlab/constants/mrProperties.py +236 -0
  12. spinlab/constants/radicalProperties.py +147 -0
  13. spinlab/core/__init__.py +5 -0
  14. spinlab/core/base.py +1268 -0
  15. spinlab/core/coord.py +225 -0
  16. spinlab/core/data.py +335 -0
  17. spinlab/core/ufunc.py +67 -0
  18. spinlab/core/util.py +159 -0
  19. spinlab/fitting/__init__.py +3 -0
  20. spinlab/fitting/general.py +106 -0
  21. spinlab/io/__init__.py +19 -0
  22. spinlab/io/bes3t.py +453 -0
  23. spinlab/io/cnsi.py +139 -0
  24. spinlab/io/delta.py +523 -0
  25. spinlab/io/h5.py +215 -0
  26. spinlab/io/load.py +334 -0
  27. spinlab/io/load_csv.py +98 -0
  28. spinlab/io/logs.py +84 -0
  29. spinlab/io/mat.py +153 -0
  30. spinlab/io/power.py +109 -0
  31. spinlab/io/prospa.py +340 -0
  32. spinlab/io/random.py +36 -0
  33. spinlab/io/rs2d.py +111 -0
  34. spinlab/io/save.py +52 -0
  35. spinlab/io/specman.py +358 -0
  36. spinlab/io/tnmr.py +178 -0
  37. spinlab/io/topspin.py +625 -0
  38. spinlab/io/vna.py +76 -0
  39. spinlab/io/vnmrj.py +236 -0
  40. spinlab/io/winepr.py +205 -0
  41. spinlab/math/__init__.py +6 -0
  42. spinlab/math/lineshape.py +142 -0
  43. spinlab/math/pulses.py +255 -0
  44. spinlab/math/relaxation.py +133 -0
  45. spinlab/math/window.py +161 -0
  46. spinlab/plotting/__init__.py +9 -0
  47. spinlab/plotting/colors.py +9 -0
  48. spinlab/plotting/general.py +285 -0
  49. spinlab/plotting/image.py +68 -0
  50. spinlab/plotting/stack_plot.py +69 -0
  51. spinlab/processing/__init__.py +12 -0
  52. spinlab/processing/align.py +76 -0
  53. spinlab/processing/apodization.py +96 -0
  54. spinlab/processing/average.py +28 -0
  55. spinlab/processing/conversion.py +247 -0
  56. spinlab/processing/fft.py +182 -0
  57. spinlab/processing/helpers.py +577 -0
  58. spinlab/processing/integration.py +130 -0
  59. spinlab/processing/interpolation.py +70 -0
  60. spinlab/processing/offset.py +133 -0
  61. spinlab/processing/phase.py +508 -0
  62. spinlab/reporting/__init__.py +1 -0
  63. spinlab/version.py +1 -0
  64. spinlab/widgets/__init__.py +4 -0
  65. spinlab/widgets/align_widget.py +77 -0
  66. spinlab/widgets/phase_widget.py +118 -0
  67. spinlab-0.0.0.dist-info/METADATA +56 -0
  68. spinlab-0.0.0.dist-info/RECORD +71 -0
  69. spinlab-0.0.0.dist-info/WHEEL +5 -0
  70. spinlab-0.0.0.dist-info/licenses/LICENSE.txt +21 -0
  71. spinlab-0.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,249 @@
1
+ import numpy as _np
2
+ import scipy.signal as _spsig
3
+ import spinlab as _sl
4
+ import warnings
5
+
6
+
7
+ def find_peaks(
8
+ data,
9
+ dims="f2",
10
+ normalize=True,
11
+ regions=None,
12
+ height=0.5,
13
+ threshold=None,
14
+ distance=None,
15
+ prominence=None,
16
+ width=None,
17
+ wlen=None,
18
+ rel_height=0.5,
19
+ plateau_size=None,
20
+ ):
21
+ """Find peaks in spectrum
22
+
23
+ Find peaks in spectrum (sldata object) and returns peak index, chemical shift (ppm), peak height, peak width (Hz) and peak width height. The function uses the SciPy functions "find_peaks" and "peak_widths".
24
+
25
+ Args:
26
+ data (SpinData): Data object
27
+ dims (str): Dimension to find peaks
28
+ regions (None, list): List of tuples defining the region to find peaks
29
+ normalize (boolean): Normalize data to a maximum value of 1. Default is True
30
+ height (float or numpy.array): Optionally, height of peaks. If an array is supplied, the first element is minimum and the second is maximum
31
+ threshold (float or numpy.array): Optionally, threshold of minimum peak height to be counted. If an array is supplied, the first element is minimum and the second is maximum
32
+ distance (float): Optionally, minimal horizontal distance in samples between peaks. Smaller peaks are removed first until the condition is fulfilled for all remaining peaks.
33
+ prominence (float or numpy.array): Optionally, prominence of peaks. If an array is supplied, the first element is minimum and the second is maximum
34
+ width (float or numpy.array): Optionally, width of peaks. If an array is supplied, the first element is minimum and the second is maximum
35
+ wlen (int): Optionally, for calculating the peaks prominences. Only valid if prominence is given
36
+ rel_height (float): Optionally, relative height at which peak width is measured. Default is 0.5 for FWHH
37
+ plateau_size (float or numpy.array): Optionally, size of the flat top of peaks in samples. If an array is supplied, the first element is minimum and the second is maximum
38
+ peak_info (boolean): If True print output to terminal
39
+
40
+ Returns:
41
+ data (SpinData): nd array of peak index, peak width and relative peak height. The linewidth is returned in (Hz), based on the spectrometer frequency
42
+
43
+ Examples:
44
+ Find peaks in entire data region:
45
+
46
+ >>> peak_list = sl.find_peaks(data)
47
+
48
+ Find peaks with an amplitude > 0.01 (after normalization):
49
+
50
+ >>> peak_list = sl.find_peaks(data, peak_height = 0.05)
51
+
52
+ Find peaks with an amplitude > 500 (data not normalized):
53
+
54
+ >>> peak_list = sl.find_peaks(data, peak_height = 500, normalize = False)
55
+
56
+ """
57
+
58
+ if len(data.dims) == 2:
59
+ data_list = []
60
+ second_dim = data.dims[-1]
61
+ second_coord = data.coords[second_dim]
62
+ for i in range(len(second_coord)):
63
+ sub_data = data[second_dim, i].sum(second_dim)
64
+ data_list.append(
65
+ find_peaks(
66
+ sub_data,
67
+ dims,
68
+ normalize,
69
+ regions=regions,
70
+ height=height,
71
+ threshold=threshold,
72
+ distance=distance,
73
+ prominence=prominence,
74
+ width=width,
75
+ wlen=wlen,
76
+ rel_height=rel_height,
77
+ plateau_size=plateau_size,
78
+ )
79
+ )
80
+
81
+ # data_list, second_coord = _peak_list_checker(
82
+ # data_list, second_coord, second_dim
83
+ # )
84
+
85
+ return _sl.concat(
86
+ data_list, dim=second_dim, coord=second_coord, casting="unsafe"
87
+ )
88
+
89
+ elif len(data.dims) == 1:
90
+ out = data.copy()
91
+ out.attrs["experiment_type"] = "peak_list"
92
+ out.attrs["data_type"] = "peak_list"
93
+
94
+ resolution = _np.sum(_np.diff(out.coords)) / _np.size(out.coords)
95
+ frequency = out.attrs["nmr_frequency"]
96
+
97
+ coords = out.coords[dims]
98
+ append_index = 0
99
+
100
+ if regions:
101
+ out = out[dims, regions]
102
+ append_index = _np.where(coords == out.coords[dims][0])[0][0]
103
+
104
+ if normalize == True:
105
+ out = _sl.normalize(out)
106
+
107
+ # In the case of the negative peaks
108
+ real_array = out.values.real
109
+ max_value = _np.max(real_array)
110
+ min_value = _np.min(real_array)
111
+ if _np.abs(max_value) < _np.abs(min_value):
112
+ out.values *= -1
113
+
114
+ peak_index, _ = _spsig.find_peaks(
115
+ out.values,
116
+ height=height,
117
+ threshold=threshold,
118
+ distance=distance,
119
+ prominence=prominence,
120
+ width=width,
121
+ wlen=wlen,
122
+ rel_height=rel_height,
123
+ plateau_size=plateau_size,
124
+ )
125
+ peak_width_height = _spsig.peak_widths(
126
+ out.values.real, peaks=peak_index, rel_height=rel_height
127
+ )
128
+ peak_width = peak_width_height[0] * resolution * 1e-6 * frequency
129
+ peak_width_height = peak_width_height[1]
130
+
131
+ peak_index = [x + append_index for x in peak_index]
132
+ peak_values = [data.values.real[x] for x in peak_index]
133
+ peak_shift = [coords[int(x)] for x in peak_index]
134
+
135
+ out.values = _np.vstack(
136
+ (peak_index, peak_shift, peak_values, peak_width, peak_width_height)
137
+ )
138
+
139
+ out = _sl.update_axis(
140
+ out, dim=0, new_dims="peak_info", start_stop=(0, len(out.values) - 1)
141
+ )
142
+ out.coords.append(dim="index", coord=_np.arange(0, len(peak_index), 1))
143
+
144
+ proc_attr_name = "peak_list"
145
+ proc_parameters = {
146
+ "dims": dims,
147
+ "normalize": normalize,
148
+ "regions": regions,
149
+ "height": height,
150
+ "threshold": threshold,
151
+ "distance": distance,
152
+ "prominence": prominence,
153
+ "width": width,
154
+ "wlen": wlen,
155
+ "rel_height": rel_height,
156
+ "plateau_size": plateau_size,
157
+ }
158
+
159
+ out.add_proc_attrs(proc_attr_name, proc_parameters)
160
+
161
+ return out
162
+
163
+ else:
164
+ raise ValueError("The function only works with 1d or 2d datasets")
165
+
166
+
167
+ def peak_info(data):
168
+ """
169
+ Print peak list in human readable form
170
+
171
+ Function to print the peak list in a human readable form. You first have to run find_peaks to create a sldata object that includes a peak list.
172
+
173
+ Args:
174
+ data (SpinData): SpinData object created by find_peaks
175
+
176
+ Returns:
177
+ Output (str): Peak list table
178
+ """
179
+
180
+ if data.attrs["experiment_type"] != "peak_list":
181
+ print("Peak list required as input")
182
+ return
183
+
184
+ if len(data.dims) == 3:
185
+ dim = data.dims[-1]
186
+ coord = data.coords[dim]
187
+ for i in range(len(coord)):
188
+ sub_data = data[dim, i].sum(dim)
189
+ print("Dim: %s, Dim Index: %d, Dim Value: %0.01f" % (dim, i, coord[i]))
190
+ print("--------------------------------------------")
191
+ peak_info(sub_data)
192
+
193
+ elif len(data.dims) == 2:
194
+ for peak_number in range(len(data.coords["index"])):
195
+ values = data["index", peak_number].sum("index").values
196
+ if any(_np.isnan(values)):
197
+ print("Peak #%d Information Not Available." % (peak_number + 1))
198
+ else:
199
+ print(
200
+ "Peak #%d: Index: %5d, Shift (ppm): %0.02f, Height : %4.2f, Width (Hz): %4.2f, Width Height: %2.2f"
201
+ % (
202
+ peak_number + 1,
203
+ values[0],
204
+ values[1],
205
+ values[2],
206
+ values[3],
207
+ values[4],
208
+ )
209
+ )
210
+ print("--------------------------------------------")
211
+ else:
212
+ raise ValueError("The function only works with 1d or 2d datasets")
213
+
214
+
215
+ def _peak_list_checker(peak_list, coord, dim):
216
+ """
217
+ Check peak list before concat. It will remove the inconsistent peak data from list.
218
+
219
+ Args:
220
+ peak_list (list): list of peak data
221
+ coord (numpy.array): an array of coord
222
+ dim (str): the dim for concat
223
+
224
+ Returns:
225
+ new_peak_list (list): concat-able list of peak data
226
+ new_coord (numpy.array): a new array of coord
227
+ """
228
+
229
+ ref = peak_list[-1]
230
+ ref_shape = _np.shape(ref)
231
+ new_peak_list = [peak for peak in peak_list if _np.shape(peak) == ref_shape]
232
+ new_coord = _np.array(
233
+ [
234
+ coord[i]
235
+ for i in range(len(peak_list))
236
+ if _np.shape(peak_list[i]) == ref_shape
237
+ ]
238
+ )
239
+
240
+ if new_peak_list != peak_list:
241
+ print("In dim %s, the following datasets are removed." % dim)
242
+ for i in range(len(peak_list)):
243
+ if peak_list[i] not in new_peak_list:
244
+ print(
245
+ "Index: %i, Value: %0.01f, Number of Peaks Found: %i"
246
+ % (i, coord[i], len(peak_list[i].coords[1]))
247
+ )
248
+
249
+ return new_peak_list, new_coord
@@ -0,0 +1,29 @@
1
+ """Modules which provide function to analyse relaxation measurements]"""
2
+
3
+ import numpy as _np
4
+ from ..fitting import fit
5
+ from ..math import *
6
+
7
+
8
+ def inversion_recovery_fit(integrals):
9
+ # Estimate an initial guess from experimental data
10
+
11
+ initial_guess = (2.0, -4000, 4000)
12
+
13
+ fit_results = fit(relaxation.t1, integrals.real, dim="t1", p0=initial_guess)
14
+
15
+ # fit returns dictionary with results
16
+
17
+ print(fit_results)
18
+
19
+ # print(fit_results['fit'])
20
+
21
+ # fit = out['fit']
22
+ # popt = out['popt']
23
+ # err = out['err']
24
+
25
+ # T1 = popt['popt',0]
26
+ # M_0 = popt['popt',1]
27
+ # M_inf = popt['popt',2]
28
+
29
+ # return out
@@ -0,0 +1,109 @@
1
+ """Modules to calculate Spin enhancement profiles"""
2
+
3
+ import numpy as _np
4
+ from scipy import optimize
5
+ import spinlab as _sl
6
+ import warnings
7
+ from ..constants import constants
8
+
9
+
10
+ def sim_sl_profile(
11
+ data,
12
+ B0,
13
+ nucleus="1H",
14
+ sl_process="SE",
15
+ add_details=False,
16
+ remove_background=True,
17
+ normalize=True,
18
+ integrate=True,
19
+ ):
20
+ """Simulate Spin enhancment profile
21
+
22
+ Simulate Spin enhancement profile based on the EPR spectrum. For more details:
23
+
24
+ Banerjee, D., D. Shimon, A. Feintuch, S. Vega, and D. Goldfarb. “The Interplay between the Solid Effect and the Cross Effect Mechanisms in Solid State (1)(3)C Spin at 95 GHz Using Trityl Radicals.” Journal of Magnetic Resonance 230 (May 2013): 212–19.
25
+ https://doi.org/10.1016/j.jmr.2013.02.010.
26
+
27
+ Args:
28
+ data (Spindata): EPR spectrum
29
+ B0 (float): Field position for the Spin experiment in (T)
30
+ nucleus (int): Nucleus for Spin experiment
31
+ sl_process (int): Select Spin mechanism, SE - Solid Effect, CE/TM - Cross-Effect/Thermal Mixing
32
+ add_details (boolean): Add individual spectra to proc_attrs. Default is False
33
+ remove_background (boolean): Remove 0th order background from EPR spectrum. Default is True
34
+ normalize (boolean): Normalize EPR spectrum to maximum amplitude of 1. Default is True
35
+ integrate (boolean): Integrate EPR spectrum. Default is True
36
+
37
+ Returns:
38
+ data (Spindata): Simulated Spin enhancement profile
39
+
40
+ .. math::
41
+
42
+ """
43
+ out = data.copy()
44
+
45
+ # Some error checks:
46
+ if out.attrs.get("experiment_type") == None:
47
+ print("Error: Key experiment_type not present")
48
+ return
49
+
50
+ if out.attrs["experiment_type"] != "epr_spectrum":
51
+ print("Error: EPR spectrum required as input.")
52
+ return
53
+
54
+ if len(out.dims) > 1:
55
+ print("Error: This function requires a 1D EPR spectrum as input.")
56
+ return
57
+
58
+ if remove_background == True:
59
+ out = _sl.remove_background(out, dim="B0", deg=0) # Remove background
60
+
61
+ if normalize == True:
62
+ out = _sl.normalize(out)
63
+
64
+ if integrate == True:
65
+ out = _sl.cumulative_integrate(out, dim="B0") # Calculate cumsum
66
+
67
+ ## Calculate number of points to shift
68
+ slLarmorFrequency = _sl.mr_properties(nucleus, B0) # Nuclear Larmor Frequency
69
+ slLarmorFrequency_G = slLarmorFrequency / (
70
+ 1000 * _sl.mr_properties("0e") / 2 / pi
71
+ ) # Nuclear Larmor Frequency in [G]
72
+ deltaB0_G = (out.coords["B0"][1] - out.coords["B0"][0]) * 10
73
+ points_to_shift = round(slLarmorFrequency_G / deltaB0_G)
74
+
75
+ ## Shift EPR spectra using mumpy's roll function
76
+ EPRdataPos = out.copy()
77
+ EPRdataPos.values = _np.roll(EPRdataPos.values, points_to_shift)
78
+
79
+ EPRdataNeg = out.copy()
80
+ EPRdataNeg.values = _np.roll(out.values, (-1) * points_to_shift)
81
+ EPRdataNeg.values = (-1) * EPRdataNeg.values
82
+
83
+ if sl_process == "SE":
84
+ out = EPRdataPos + EPRdataNeg
85
+ elif sl_process == "CE/TM":
86
+ out = out.values * (EPRdataPos + EPRdataNeg)
87
+
88
+ proc_attr_name = "sim_sl_profile"
89
+ proc_parameters = {
90
+ "nucleus": nucleus,
91
+ "sl_process": sl_process,
92
+ "remove_background": remove_background,
93
+ "normalize": normalize,
94
+ "integrate": integrate,
95
+ }
96
+
97
+ out.add_proc_attrs(proc_attr_name, proc_parameters)
98
+
99
+ if add_details == True:
100
+ sim_data = _np.array([out.values, EPRdataNeg.values, EPRdataPos.values])
101
+
102
+ proc_attr_name = "sim_sl_profile"
103
+ proc_parameters = {
104
+ "sim_data": sim_data,
105
+ }
106
+
107
+ out.add_proc_attrs(proc_attr_name, proc_parameters)
108
+
109
+ return out
File without changes
@@ -0,0 +1,81 @@
1
+ """
2
+ global config
3
+ """
4
+
5
+ import configparser
6
+ from pathlib import Path
7
+ import warnings
8
+
9
+ import logging
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ def _escape_split(s, delim=",", escape="\\"):
15
+ tokens = []
16
+ previous_escape = False
17
+ subtoken = ""
18
+ for k in range(len(s)):
19
+ if s[k] == delim and (not previous_escape):
20
+ if len(subtoken) > 0:
21
+ tokens.append(subtoken)
22
+ subtoken = "" # reset subtoken
23
+ else:
24
+ # ESCAPE DELIM -> DELIM
25
+ if previous_escape and s[k] != escape and s[k] == delim:
26
+ subtoken = subtoken[:-1] + s[k]
27
+ else:
28
+ # add to subtoken
29
+ subtoken += s[k]
30
+ # set previous_escape flag:
31
+ # If for current char is escape (True and s[k]=='\\') and previous_escape is False -> set it to True
32
+ # If for current char is escape (True and s[k]=='\\') and previous_escape is True -> case of '\\\\' -> escaping an escape character -> set it back to False
33
+ # if current char is no escape character -> set it to false
34
+ previous_escape = (not previous_escape) and (s[k] == escape)
35
+ if len(subtoken) > 0:
36
+ tokens.append(subtoken)
37
+ return tokens
38
+
39
+
40
+ def _kwarg_converter(s: str):
41
+ tokens = _escape_split(s, ",", escape="\\")
42
+ args = []
43
+ kwargs = {}
44
+ for k in tokens:
45
+ subtokens = _escape_split(k, "=", escape="\\")
46
+ if len(subtokens) == 1:
47
+ args.append(subtokens[0])
48
+ else:
49
+ kwargs[subtokens[0].strip()] = subtokens[1].strip()
50
+ return args, kwargs
51
+
52
+
53
+ def _get_sl_config(configname="spinlab.cfg"):
54
+ config = configparser.ConfigParser(
55
+ converters={
56
+ "list": lambda x: list(x.strip("[").strip("]").split(",")),
57
+ "args_kwargs": _kwarg_converter,
58
+ }
59
+ )
60
+
61
+ # define three possible locations:
62
+ spinlab_current_config = Path.cwd() / configname
63
+ spinlab_home_config = Path.home() / configname
64
+
65
+ spinlab_cfg_folder = str(
66
+ Path(__file__).parent
67
+ ) # / configname #.with_name("config"))
68
+ spinlab_global_config = Path(spinlab_cfg_folder) / configname
69
+
70
+ config_read_list = [
71
+ spinlab_global_config,
72
+ spinlab_home_config,
73
+ spinlab_current_config,
74
+ ]
75
+
76
+ # user defined takes precedence
77
+ config.read(config_read_list)
78
+ return config
79
+
80
+
81
+ SpinLAB_CONFIG = _get_sl_config()
@@ -0,0 +1,5 @@
1
+ """Modules to store physical properties"""
2
+
3
+ from .constants import *
4
+ from .mrProperties import *
5
+ from .radicalProperties import *
@@ -0,0 +1,27 @@
1
+ from scipy.constants import mu_0, c, pi, epsilon_0, hbar, h, N_A, m_e, eV
2
+ from scipy.constants import e as e_charge
3
+
4
+ from scipy.constants import physical_constants as pc
5
+
6
+ e_gyro = pc["electron gyromag. ratio in MHz/T"][0]
7
+ p_gyro = pc["proton gyromag. ratio in MHz/T"][0]
8
+ mub = pc["Bohr magneton"][0]
9
+ mub_Hz = pc["Bohr magneton in Hz/T"][0]
10
+
11
+
12
+ __all__ = [
13
+ "mu_0",
14
+ "c",
15
+ "pi",
16
+ "epsilon_0",
17
+ "hbar",
18
+ "h",
19
+ "N_A",
20
+ "m_e",
21
+ "e_charge",
22
+ "e_gyro",
23
+ "p_gyro",
24
+ "eV",
25
+ "mub",
26
+ "mub_Hz",
27
+ ]