screamlab 0.1.0__py3-none-any.whl → 0.3.1__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.
- screamlab/__pycache__/dataset.cpython-310.pyc +0 -0
- screamlab/__pycache__/dataset.cpython-313.pyc +0 -0
- screamlab/__pycache__/functions.cpython-310.pyc +0 -0
- screamlab/__pycache__/functions.cpython-313.pyc +0 -0
- screamlab/__pycache__/io.cpython-310.pyc +0 -0
- screamlab/__pycache__/io.cpython-313.pyc +0 -0
- screamlab/__pycache__/settings.cpython-310.pyc +0 -0
- screamlab/__pycache__/settings.cpython-313.pyc +0 -0
- screamlab/__pycache__/utils.cpython-310.pyc +0 -0
- screamlab/__pycache__/utils.cpython-313.pyc +0 -0
- screamlab/dataset.py +145 -19
- screamlab/functions.py +114 -4
- screamlab/io.py +162 -44
- screamlab/settings.py +18 -11
- screamlab/utils.py +247 -6
- {screamlab-0.1.0.dist-info → screamlab-0.3.1.dist-info}/METADATA +2 -2
- screamlab-0.3.1.dist-info/RECORD +30 -0
- screamlab-0.1.0.dist-info/RECORD +0 -27
- {screamlab-0.1.0.dist-info → screamlab-0.3.1.dist-info}/WHEEL +0 -0
- {screamlab-0.1.0.dist-info → screamlab-0.3.1.dist-info}/licenses/LICENSE +0 -0
- {screamlab-0.1.0.dist-info → screamlab-0.3.1.dist-info}/top_level.txt +0 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
screamlab/dataset.py
CHANGED
|
@@ -12,8 +12,10 @@ Classes:
|
|
|
12
12
|
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
+
import sys
|
|
15
16
|
from datetime import datetime
|
|
16
17
|
import numpy as np
|
|
18
|
+
from lmfit import Parameters
|
|
17
19
|
from screamlab import io, utils, settings, functions
|
|
18
20
|
|
|
19
21
|
|
|
@@ -66,12 +68,17 @@ class Dataset:
|
|
|
66
68
|
print(
|
|
67
69
|
f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}: Start buildup fit."
|
|
68
70
|
)
|
|
71
|
+
|
|
69
72
|
self._start_buildup_fit()
|
|
70
73
|
print(
|
|
71
74
|
f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}: "
|
|
72
75
|
f"Start generating result files. ({self.props.output_folder})"
|
|
73
76
|
)
|
|
74
77
|
self._print_all()
|
|
78
|
+
print(
|
|
79
|
+
f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}: "
|
|
80
|
+
f"Finished with {self.props.path_to_experiment}"
|
|
81
|
+
)
|
|
75
82
|
|
|
76
83
|
def _start_buildup_fit_from_spectra(self):
|
|
77
84
|
"""Starts buildup fitting using data imported from spectra CSV files."""
|
|
@@ -86,6 +93,7 @@ class Dataset:
|
|
|
86
93
|
fitting_type="voigt",
|
|
87
94
|
peak_sign="-",
|
|
88
95
|
line_broadening=None,
|
|
96
|
+
integration_range=None,
|
|
89
97
|
):
|
|
90
98
|
"""
|
|
91
99
|
Adds a peak to the ds.
|
|
@@ -94,7 +102,7 @@ class Dataset:
|
|
|
94
102
|
----------
|
|
95
103
|
center_of_peak (float): Peak position in ppm (chemical shift).
|
|
96
104
|
peak_label (str, optional): Custom label. Defaults to "Peak_at_<ppm>_ppm".
|
|
97
|
-
fitting_type (str, optional): Peak shape:
|
|
105
|
+
fitting_type (str, optional): Peak shape: "gauss", "lorentz", or "voigt" (default).
|
|
98
106
|
peak_sign (str, optional): "+" for upward, "-" for downward peaks. Defaults to "+".
|
|
99
107
|
line_broadening (dict, optional): Dict with "sigma" and "gamma" keys for line width.
|
|
100
108
|
Defaults to {"sigma": {"min": 0, "max": 3}, "gamma": {"min": 0, "max": 3}}.
|
|
@@ -109,6 +117,7 @@ class Dataset:
|
|
|
109
117
|
peak.fitting_type = fitting_type
|
|
110
118
|
peak.peak_sign = peak_sign
|
|
111
119
|
peak.line_broadening = line_broadening
|
|
120
|
+
peak.integration_range = integration_range
|
|
112
121
|
|
|
113
122
|
def _read_in_data_from_topspin(self):
|
|
114
123
|
"""Reads and imports data from TopSpin."""
|
|
@@ -132,7 +141,7 @@ class Dataset:
|
|
|
132
141
|
|
|
133
142
|
def _calculate_peak_intensities(self):
|
|
134
143
|
"""Calculates peak intensities based on fitting methods."""
|
|
135
|
-
if self.props.prefit:
|
|
144
|
+
if self.props.prefit and self.props.spectrum_fit_type != "numint":
|
|
136
145
|
print(
|
|
137
146
|
f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}: Start prefit."
|
|
138
147
|
)
|
|
@@ -148,7 +157,7 @@ class Dataset:
|
|
|
148
157
|
result = self.fitter.fit()
|
|
149
158
|
self.lmfit_result_handler.global_fit = result
|
|
150
159
|
self._get_intensities(result)
|
|
151
|
-
|
|
160
|
+
elif "global" == self.props.spectrum_fit_type:
|
|
152
161
|
print(
|
|
153
162
|
f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}: Start global fit."
|
|
154
163
|
)
|
|
@@ -156,6 +165,15 @@ class Dataset:
|
|
|
156
165
|
result = self.fitter.fit()
|
|
157
166
|
self.lmfit_result_handler.global_fit = result
|
|
158
167
|
self._get_intensities(result)
|
|
168
|
+
elif "numint" == self.props.spectrum_fit_type:
|
|
169
|
+
print(
|
|
170
|
+
f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}: Start integration."
|
|
171
|
+
)
|
|
172
|
+
self._set_integration_calc()
|
|
173
|
+
result = self.fitter.fit()
|
|
174
|
+
self._get_intensities(result)
|
|
175
|
+
else:
|
|
176
|
+
sys.exit()
|
|
159
177
|
|
|
160
178
|
def _start_buildup_fit(self):
|
|
161
179
|
"""Performs buildup fitting using the appropriate fitter classes."""
|
|
@@ -164,7 +182,9 @@ class Dataset:
|
|
|
164
182
|
"biexponential_with_offset": utils.BiexpFitterWithOffset,
|
|
165
183
|
"exponential": utils.ExpFitter,
|
|
166
184
|
"exponential_with_offset": utils.ExpFitterWithOffset,
|
|
167
|
-
"
|
|
185
|
+
"stretched_exponential": utils.StrechedExponentialFitter,
|
|
186
|
+
"exponential_decay": utils.ExpDecayFitter,
|
|
187
|
+
"exponential_decay_with_offset": utils.ExpDecayFitterWithOffset,
|
|
168
188
|
}
|
|
169
189
|
|
|
170
190
|
for b_type in self.props.buildup_types:
|
|
@@ -187,10 +207,18 @@ class Dataset:
|
|
|
187
207
|
"""Sets up a global fitter for all spectra."""
|
|
188
208
|
self.fitter = utils.GlobalFitter(self)
|
|
189
209
|
|
|
210
|
+
def _set_integration_calc(self):
|
|
211
|
+
self.fitter = utils.NumericalIntegration(self)
|
|
212
|
+
|
|
190
213
|
def _get_intensities(self, result):
|
|
191
214
|
"""Extracts intensity values from the fitting results."""
|
|
192
215
|
if isinstance(
|
|
193
|
-
self.fitter,
|
|
216
|
+
self.fitter,
|
|
217
|
+
(
|
|
218
|
+
utils.IndependentFitter,
|
|
219
|
+
utils.GlobalFitter,
|
|
220
|
+
utils.NumericalIntegration,
|
|
221
|
+
),
|
|
194
222
|
):
|
|
195
223
|
for peak in self.peak_list:
|
|
196
224
|
peak.buildup_vals = (result, self.spectra)
|
|
@@ -213,6 +241,21 @@ class Dataset:
|
|
|
213
241
|
if f"{peak.peak_label}_{lw}_0" in result.params
|
|
214
242
|
}
|
|
215
243
|
|
|
244
|
+
def _transform_result_format(self, results):
|
|
245
|
+
print(results)
|
|
246
|
+
params_new = Parameters()
|
|
247
|
+
for result in results:
|
|
248
|
+
for name, par in result.params.items():
|
|
249
|
+
params_new.add(
|
|
250
|
+
name,
|
|
251
|
+
value=par.value,
|
|
252
|
+
vary=par.vary,
|
|
253
|
+
min=par.min,
|
|
254
|
+
max=par.max,
|
|
255
|
+
expr=par.expr,
|
|
256
|
+
)
|
|
257
|
+
return params_new
|
|
258
|
+
|
|
216
259
|
|
|
217
260
|
class Spectra:
|
|
218
261
|
"""
|
|
@@ -257,6 +300,7 @@ class Peak:
|
|
|
257
300
|
self._line_broadening = None
|
|
258
301
|
self._line_broadening_init = None
|
|
259
302
|
self._buildup_vals = None
|
|
303
|
+
self._integration_range = None
|
|
260
304
|
|
|
261
305
|
def __str__(self):
|
|
262
306
|
"""
|
|
@@ -277,6 +321,34 @@ class Peak:
|
|
|
277
321
|
f" {self._format_fitting_range('')}"
|
|
278
322
|
)
|
|
279
323
|
|
|
324
|
+
def to_string(self, spectrum_fit_type):
|
|
325
|
+
"""
|
|
326
|
+
Returns a string representation of the peak.
|
|
327
|
+
|
|
328
|
+
If `spectrum_fit_type` is "numint", detailed peak information
|
|
329
|
+
is returned, including center, label, sign, and integration range.
|
|
330
|
+
Otherwise, the default string representation of the object is returned.
|
|
331
|
+
|
|
332
|
+
Args:
|
|
333
|
+
spectrum_fit_type (str): The type of spectrum fitting.
|
|
334
|
+
Supports "numint" for numerical integration.
|
|
335
|
+
|
|
336
|
+
Returns
|
|
337
|
+
-------
|
|
338
|
+
str: A formatted string with peak information, or the default
|
|
339
|
+
string representation of the object.
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
"""
|
|
343
|
+
if spectrum_fit_type == "numint":
|
|
344
|
+
return (
|
|
345
|
+
f"Peak center: {self.peak_center} ppm\n"
|
|
346
|
+
f"Peak label: {self.peak_label}\n"
|
|
347
|
+
f"Peak sign: {self.peak_sign}\n"
|
|
348
|
+
f"Numerical integration range: {self.integration_range} ppm\n"
|
|
349
|
+
)
|
|
350
|
+
return str(self)
|
|
351
|
+
|
|
280
352
|
def _format_fitting_range(self, fit_type):
|
|
281
353
|
a_max = "0 and inf" if self.peak_sign == "+" else "-inf and 0"
|
|
282
354
|
lb = ""
|
|
@@ -297,6 +369,34 @@ class Peak:
|
|
|
297
369
|
f"{lb}"
|
|
298
370
|
)
|
|
299
371
|
|
|
372
|
+
@property
|
|
373
|
+
def integration_range(self) -> list:
|
|
374
|
+
"""
|
|
375
|
+
Gets the integration range.
|
|
376
|
+
|
|
377
|
+
:return:
|
|
378
|
+
"""
|
|
379
|
+
return self._integration_range
|
|
380
|
+
|
|
381
|
+
@integration_range.setter
|
|
382
|
+
def integration_range(self, args):
|
|
383
|
+
"""
|
|
384
|
+
Sets the integration range.
|
|
385
|
+
|
|
386
|
+
:param args: List containing the integration range for numerical integragtion.
|
|
387
|
+
"""
|
|
388
|
+
if args is not None:
|
|
389
|
+
if (
|
|
390
|
+
not isinstance(args, list)
|
|
391
|
+
or len(args) != 2
|
|
392
|
+
or not all(isinstance(e, (int, float)) for e in args)
|
|
393
|
+
):
|
|
394
|
+
raise TypeError(
|
|
395
|
+
"Integration range must be a list of two numbers or None."
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
self._integration_range = args
|
|
399
|
+
|
|
300
400
|
@property
|
|
301
401
|
def buildup_vals(self) -> list:
|
|
302
402
|
"""
|
|
@@ -315,7 +415,10 @@ class Peak:
|
|
|
315
415
|
"""
|
|
316
416
|
result, spectra = args
|
|
317
417
|
self._buildup_vals = BuildupList()
|
|
318
|
-
|
|
418
|
+
if isinstance(result, dict):
|
|
419
|
+
self._buildup_vals.set_num_int_vals(result[self], spectra)
|
|
420
|
+
else:
|
|
421
|
+
self._buildup_vals.set_vals(result, spectra, self.peak_label)
|
|
319
422
|
|
|
320
423
|
@property
|
|
321
424
|
def line_broadening(self) -> str:
|
|
@@ -600,6 +703,21 @@ class BuildupList:
|
|
|
600
703
|
self._set_intensity(result, label, spectra)
|
|
601
704
|
self._sort_lists()
|
|
602
705
|
|
|
706
|
+
def set_num_int_vals(self, result, spectra):
|
|
707
|
+
"""
|
|
708
|
+
Sets buildup values from numerical integration.
|
|
709
|
+
|
|
710
|
+
Attributes
|
|
711
|
+
----------
|
|
712
|
+
result (list): Intensity values
|
|
713
|
+
spectra (list): Spectrum objects used with result to compute buildup.
|
|
714
|
+
label (str): Peak label used to filter relevant parameters in result.
|
|
715
|
+
|
|
716
|
+
"""
|
|
717
|
+
self._set_tpol(spectra)
|
|
718
|
+
self.intensity = result
|
|
719
|
+
self._sort_lists()
|
|
720
|
+
|
|
603
721
|
def _set_tpol(self, spectra):
|
|
604
722
|
self.tpol = [s.tpol for s in spectra]
|
|
605
723
|
|
|
@@ -607,20 +725,26 @@ class BuildupList:
|
|
|
607
725
|
last_digid = None
|
|
608
726
|
self.intensity = []
|
|
609
727
|
val_list = []
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
728
|
+
res_list = []
|
|
729
|
+
if not isinstance(result, list):
|
|
730
|
+
res_list.append(result)
|
|
731
|
+
else:
|
|
732
|
+
res_list = result
|
|
733
|
+
for single_result in res_list:
|
|
734
|
+
for param in single_result.params:
|
|
735
|
+
if label in param:
|
|
736
|
+
if last_digid != param.split("_")[-1]:
|
|
737
|
+
if val_list:
|
|
738
|
+
self.intensity.append(
|
|
739
|
+
self._calc_integral(
|
|
740
|
+
val_list, spectra[int(last_digid)]
|
|
741
|
+
)
|
|
617
742
|
)
|
|
618
|
-
)
|
|
619
|
-
|
|
620
|
-
val_list
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
val_list.append("gamma")
|
|
743
|
+
last_digid = param.split("_")[-1]
|
|
744
|
+
val_list = []
|
|
745
|
+
val_list.append(float(single_result.params[param].value))
|
|
746
|
+
if param.split("_")[-2] == "gamma":
|
|
747
|
+
val_list.append("gamma")
|
|
624
748
|
self.intensity.append(
|
|
625
749
|
self._calc_integral(val_list, spectra[int(last_digid)])
|
|
626
750
|
)
|
|
@@ -641,6 +765,8 @@ class BuildupList:
|
|
|
641
765
|
"""
|
|
642
766
|
simspec = [0 for _ in range(len(spectrum.x_axis))]
|
|
643
767
|
simspec = functions.calc_peak(spectrum.x_axis, simspec, val_list)
|
|
768
|
+
if hasattr(np, "trapezoid"):
|
|
769
|
+
return np.trapezoid(simspec)
|
|
644
770
|
return np.trapz(simspec)
|
|
645
771
|
|
|
646
772
|
def _sort_lists(self):
|
screamlab/functions.py
CHANGED
|
@@ -169,6 +169,48 @@ def calc_stretched_exponential(time_vals, param):
|
|
|
169
169
|
)
|
|
170
170
|
|
|
171
171
|
|
|
172
|
+
def calc_expdecay(time_vals, param):
|
|
173
|
+
"""
|
|
174
|
+
Compute values of an exponential growth function over time.
|
|
175
|
+
|
|
176
|
+
The function models the equation:
|
|
177
|
+
I(t) = A * exp(-t / t)
|
|
178
|
+
|
|
179
|
+
where:
|
|
180
|
+
- I(t) : The output value at time t
|
|
181
|
+
- A : Amplitude (maximum value the function approaches)
|
|
182
|
+
- t : Time constant (controls the rate of decay)
|
|
183
|
+
|
|
184
|
+
Returns
|
|
185
|
+
-------
|
|
186
|
+
list: Exponential profile evaluated at t.
|
|
187
|
+
|
|
188
|
+
"""
|
|
189
|
+
return list(param[0] * np.exp(-np.asarray(time_vals) / param[1]))
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def calc_expdecaywithoffset(time_vals, param):
|
|
193
|
+
"""
|
|
194
|
+
Compute values of an exponential growth function over time.
|
|
195
|
+
|
|
196
|
+
The function models the equation:
|
|
197
|
+
I(t) = A * exp(-t / t) + I0
|
|
198
|
+
|
|
199
|
+
where:
|
|
200
|
+
- I(t) : The output value at time t
|
|
201
|
+
- A : Amplitude (maximum value the function approaches)
|
|
202
|
+
- t : Time constant (controls the rate of decay)
|
|
203
|
+
|
|
204
|
+
Returns
|
|
205
|
+
-------
|
|
206
|
+
list: Exponential profile evaluated at t.
|
|
207
|
+
|
|
208
|
+
"""
|
|
209
|
+
return list(
|
|
210
|
+
param[0] * np.exp(-np.asarray(time_vals) / param[1]) + param[2]
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
|
|
172
214
|
def calc_biexponential(time_vals, param):
|
|
173
215
|
"""
|
|
174
216
|
Compute values of a biexponential growth function over time.
|
|
@@ -245,7 +287,7 @@ def calc_biexponential_with_offset(time_vals, param):
|
|
|
245
287
|
)
|
|
246
288
|
|
|
247
289
|
|
|
248
|
-
def
|
|
290
|
+
def generate_spectra_param_dict_global(params):
|
|
249
291
|
"""
|
|
250
292
|
Generate a dictionary of spectral parameters from a list of parameter names.
|
|
251
293
|
|
|
@@ -278,6 +320,40 @@ def generate_spectra_param_dict(params):
|
|
|
278
320
|
return param_dict
|
|
279
321
|
|
|
280
322
|
|
|
323
|
+
def generate_spectra_param_dict_individual(params):
|
|
324
|
+
"""
|
|
325
|
+
Generate a dictionary of spectral parameters from a list of parameter names.
|
|
326
|
+
|
|
327
|
+
:param params: Dictionary of parameter names and values.
|
|
328
|
+
:return: Dictionary of structured parameter values.
|
|
329
|
+
"""
|
|
330
|
+
param_dict = {}
|
|
331
|
+
dict_index = -1
|
|
332
|
+
for list_element in params:
|
|
333
|
+
prefix, lastfix = None, None
|
|
334
|
+
param_value_list = []
|
|
335
|
+
for param in list_element.params:
|
|
336
|
+
parts = re.split(r"_(cen|amp|sigma|gamma)_", param)
|
|
337
|
+
if prefix != parts[0]:
|
|
338
|
+
if param_value_list:
|
|
339
|
+
param_dict[dict_index].append(param_value_list)
|
|
340
|
+
prefix = parts[0]
|
|
341
|
+
param_value_list = []
|
|
342
|
+
if lastfix != parts[2]:
|
|
343
|
+
if param_value_list:
|
|
344
|
+
param_dict[dict_index].append(param_value_list)
|
|
345
|
+
param_value_list = []
|
|
346
|
+
lastfix = parts[2]
|
|
347
|
+
dict_index += 1
|
|
348
|
+
if dict_index not in param_dict:
|
|
349
|
+
param_dict[dict_index] = []
|
|
350
|
+
param_value_list.append(float(list_element.params[param].value))
|
|
351
|
+
if parts[1] == "gamma":
|
|
352
|
+
param_value_list.append("gam")
|
|
353
|
+
param_dict[dict_index].append(param_value_list)
|
|
354
|
+
return param_dict
|
|
355
|
+
|
|
356
|
+
|
|
281
357
|
def calc_peak(x_axis, simspec, val):
|
|
282
358
|
"""Simulates spectra based on given parameters."""
|
|
283
359
|
if len(val) == 5:
|
|
@@ -303,8 +379,9 @@ def format_mapping():
|
|
|
303
379
|
"Sf",
|
|
304
380
|
"---",
|
|
305
381
|
"---",
|
|
382
|
+
"---",
|
|
306
383
|
],
|
|
307
|
-
"
|
|
384
|
+
"stretched_exponential": [
|
|
308
385
|
"Af",
|
|
309
386
|
"tf",
|
|
310
387
|
"---",
|
|
@@ -315,6 +392,7 @@ def format_mapping():
|
|
|
315
392
|
"Sf",
|
|
316
393
|
"---",
|
|
317
394
|
"beta",
|
|
395
|
+
"---",
|
|
318
396
|
],
|
|
319
397
|
"exponential_with_offset": [
|
|
320
398
|
"Af",
|
|
@@ -327,6 +405,7 @@ def format_mapping():
|
|
|
327
405
|
"Sf",
|
|
328
406
|
"---",
|
|
329
407
|
"---",
|
|
408
|
+
"---",
|
|
330
409
|
],
|
|
331
410
|
"biexponential": [
|
|
332
411
|
"Af",
|
|
@@ -339,6 +418,7 @@ def format_mapping():
|
|
|
339
418
|
"Sf",
|
|
340
419
|
"Ss",
|
|
341
420
|
"---",
|
|
421
|
+
"---",
|
|
342
422
|
],
|
|
343
423
|
"biexponential_with_offset": [
|
|
344
424
|
"Af",
|
|
@@ -351,6 +431,33 @@ def format_mapping():
|
|
|
351
431
|
"Sf",
|
|
352
432
|
"Ss",
|
|
353
433
|
"---",
|
|
434
|
+
"---",
|
|
435
|
+
],
|
|
436
|
+
"exponential_decay": [
|
|
437
|
+
"Af",
|
|
438
|
+
"tf",
|
|
439
|
+
"---",
|
|
440
|
+
"---",
|
|
441
|
+
"---",
|
|
442
|
+
"Rf",
|
|
443
|
+
"---",
|
|
444
|
+
"Sf",
|
|
445
|
+
"---",
|
|
446
|
+
"---",
|
|
447
|
+
"---",
|
|
448
|
+
],
|
|
449
|
+
"exponential_decay_with_offset": [
|
|
450
|
+
"Af",
|
|
451
|
+
"tf",
|
|
452
|
+
"---",
|
|
453
|
+
"---",
|
|
454
|
+
"---",
|
|
455
|
+
"Rf",
|
|
456
|
+
"---",
|
|
457
|
+
"Sf",
|
|
458
|
+
"---",
|
|
459
|
+
"---",
|
|
460
|
+
"I0",
|
|
354
461
|
],
|
|
355
462
|
}
|
|
356
463
|
|
|
@@ -368,7 +475,8 @@ def buildup_header():
|
|
|
368
475
|
"Rs / 1/s",
|
|
369
476
|
"Sensitivity1 (Af/sqrt(tf))",
|
|
370
477
|
"Sensitivity2 (As/sqrt(ts))",
|
|
371
|
-
"beta",
|
|
478
|
+
"beta / a.u.",
|
|
479
|
+
"I0 / a.u.",
|
|
372
480
|
]
|
|
373
481
|
|
|
374
482
|
|
|
@@ -395,7 +503,9 @@ def return_func_map():
|
|
|
395
503
|
"biexponential": calc_biexponential,
|
|
396
504
|
"exponential_with_offset": calc_exponential_with_offset,
|
|
397
505
|
"biexponential_with_offset": calc_biexponential_with_offset,
|
|
398
|
-
"
|
|
506
|
+
"stretched_exponential": calc_stretched_exponential,
|
|
507
|
+
"exponential_decay": calc_expdecay,
|
|
508
|
+
"exponential_decay_with_offset": calc_expdecaywithoffset,
|
|
399
509
|
}
|
|
400
510
|
|
|
401
511
|
|
screamlab/io.py
CHANGED
|
@@ -113,6 +113,13 @@ class TopspinImporter:
|
|
|
113
113
|
]
|
|
114
114
|
return path_list
|
|
115
115
|
|
|
116
|
+
def _gen_subspectrum(self):
|
|
117
|
+
self._dataset.spectra[-1].x_axis, self._dataset.spectra[-1].y_axis = (
|
|
118
|
+
screamlab.functions.generate_subspec(
|
|
119
|
+
self._dataset.spectra[-1], self._dataset.props.subspec
|
|
120
|
+
)
|
|
121
|
+
)
|
|
122
|
+
|
|
116
123
|
|
|
117
124
|
class ScreamImporter(TopspinImporter):
|
|
118
125
|
"""
|
|
@@ -124,15 +131,6 @@ class ScreamImporter(TopspinImporter):
|
|
|
124
131
|
|
|
125
132
|
"""
|
|
126
133
|
|
|
127
|
-
def _set_number_of_scans(self):
|
|
128
|
-
"""Set the number of scans for the last spectrum in the ds."""
|
|
129
|
-
with open(rf"{self.file}/acqu", "r", encoding="utf-8") as acqu_file:
|
|
130
|
-
for acqu_line in acqu_file:
|
|
131
|
-
if "##$NS=" in acqu_line:
|
|
132
|
-
self._dataset.spectra[-1].number_of_scans = int(
|
|
133
|
-
acqu_line.strip().split(" ")[-1]
|
|
134
|
-
)
|
|
135
|
-
|
|
136
134
|
def import_topspin_data(self):
|
|
137
135
|
"""Import NMR data from TopSpin and process it."""
|
|
138
136
|
files = self._generate_path_to_experiment()
|
|
@@ -142,6 +140,15 @@ class ScreamImporter(TopspinImporter):
|
|
|
142
140
|
self._set_values()
|
|
143
141
|
self._sort_xy_lists()
|
|
144
142
|
|
|
143
|
+
def _set_number_of_scans(self):
|
|
144
|
+
"""Set the number of scans for the last spectrum in the ds."""
|
|
145
|
+
with open(rf"{self.file}/acqus", "r", encoding="utf-8") as acqu_file:
|
|
146
|
+
for acqu_line in acqu_file:
|
|
147
|
+
if "##$NS=" in acqu_line:
|
|
148
|
+
self._dataset.spectra[-1].number_of_scans = int(
|
|
149
|
+
acqu_line.strip().split(" ")[-1]
|
|
150
|
+
)
|
|
151
|
+
|
|
145
152
|
def _set_buildup_time(self):
|
|
146
153
|
"""Set the buildup time for the last spectrum in the ds."""
|
|
147
154
|
delay = self._extract_params_from_acqus("##$D= (0..63)", 64)
|
|
@@ -204,16 +211,49 @@ class ScreamImporter(TopspinImporter):
|
|
|
204
211
|
self._dataset.spectra[-1].number_of_scans,
|
|
205
212
|
)
|
|
206
213
|
|
|
207
|
-
def _gen_subspectrum(self):
|
|
208
|
-
self._dataset.spectra[-1].x_axis, self._dataset.spectra[-1].y_axis = (
|
|
209
|
-
screamlab.functions.generate_subspec(
|
|
210
|
-
self._dataset.spectra[-1], self._dataset.props.subspec
|
|
211
|
-
)
|
|
212
|
-
)
|
|
213
|
-
|
|
214
214
|
|
|
215
215
|
class Pseudo2DImporter(TopspinImporter):
|
|
216
|
-
"""
|
|
216
|
+
"""
|
|
217
|
+
Class for importing and processing Pseudo2D data based on VD list.
|
|
218
|
+
|
|
219
|
+
Automatically reads information about x- and y-axis (chemical shift and intensitys),
|
|
220
|
+
polarization times (t_pol) and the number of scans used for the respective experiment.
|
|
221
|
+
Automatically normalizes the intensitys to the number of scans.
|
|
222
|
+
|
|
223
|
+
"""
|
|
224
|
+
|
|
225
|
+
def import_topspin_data(self):
|
|
226
|
+
"""Import pseudo 2D NMR data from TopSpin and process it."""
|
|
227
|
+
files = self._generate_path_to_experiment()
|
|
228
|
+
self.file = files[0]
|
|
229
|
+
self._set_values()
|
|
230
|
+
|
|
231
|
+
def _set_values(self):
|
|
232
|
+
"""Set internal values including scans, buildup time, x and y data."""
|
|
233
|
+
dic, data = ng.bruker.read_pdata(
|
|
234
|
+
f"{self.file}/pdata/{self._dataset.props.procno}"
|
|
235
|
+
)
|
|
236
|
+
uc = ng.bruker.guess_udic(dic, data)
|
|
237
|
+
vdlist = self._get_vdvals()
|
|
238
|
+
for spectrum_nr in range(data.shape[0]):
|
|
239
|
+
self._add_spectrum()
|
|
240
|
+
self._dataset.spectra[-1].number_of_scans = int(
|
|
241
|
+
dic.get("acqus", {}).get("NS", None)
|
|
242
|
+
)
|
|
243
|
+
self._dataset.spectra[-1].tpol = vdlist[spectrum_nr]
|
|
244
|
+
self._dataset.spectra[-1].x_axis = ng.fileiobase.uc_from_udic(
|
|
245
|
+
uc, dim=1
|
|
246
|
+
).ppm_scale()
|
|
247
|
+
self._dataset.spectra[-1].y_axis = data[spectrum_nr, :]
|
|
248
|
+
if len(self._dataset.props.subspec) == 2:
|
|
249
|
+
self._gen_subspectrum()
|
|
250
|
+
|
|
251
|
+
def _get_vdvals(self):
|
|
252
|
+
vdvals = []
|
|
253
|
+
with open(rf"{self.file}/vdlist", "r", encoding="utf-8") as vdlist:
|
|
254
|
+
for vdline in vdlist:
|
|
255
|
+
vdvals.append(float(vdline.strip()))
|
|
256
|
+
return vdvals
|
|
217
257
|
|
|
218
258
|
|
|
219
259
|
class Exporter:
|
|
@@ -258,18 +298,29 @@ class Exporter:
|
|
|
258
298
|
"""
|
|
259
299
|
self._print_report()
|
|
260
300
|
self._plot_topspin_data()
|
|
261
|
-
self.
|
|
262
|
-
|
|
301
|
+
if self.dataset.props.spectrum_fit_type != "numint":
|
|
302
|
+
self._plot_global_all_together()
|
|
303
|
+
if (
|
|
304
|
+
self.dataset.props.prefit
|
|
305
|
+
and self.dataset.props.spectrum_fit_type != "numint"
|
|
306
|
+
):
|
|
263
307
|
self._plot_prefit()
|
|
264
308
|
self._print_lmfit_prefit_report()
|
|
265
|
-
if
|
|
309
|
+
if (
|
|
310
|
+
"global" in self.dataset.props.spectrum_fit_type
|
|
311
|
+
and self.dataset.props.spectrum_fit_type != "numint"
|
|
312
|
+
):
|
|
266
313
|
self._plot_global_each_individual()
|
|
267
|
-
if
|
|
314
|
+
if (
|
|
315
|
+
"individual" in self.dataset.props.spectrum_fit_type
|
|
316
|
+
and self.dataset.props.spectrum_fit_type != "numint"
|
|
317
|
+
):
|
|
268
318
|
self._plot_global_each_individual()
|
|
269
319
|
for buildup_type in self.dataset.props.buildup_types:
|
|
270
320
|
self._plot_buildup(buildup_type)
|
|
271
|
-
self.
|
|
272
|
-
|
|
321
|
+
if self.dataset.props.spectrum_fit_type != "numint":
|
|
322
|
+
self._write_global_fit_results_to_semicolon_separated_file()
|
|
323
|
+
self._write_buildup_fit_to_semicolon_separated_file()
|
|
273
324
|
self._csv_output()
|
|
274
325
|
|
|
275
326
|
def _plot_topspin_data(self):
|
|
@@ -321,7 +372,7 @@ class Exporter:
|
|
|
321
372
|
self.dataset.props.spectrum_for_prefit
|
|
322
373
|
]
|
|
323
374
|
x_axis, y_axis = spectrum.x_axis, spectrum.y_axis
|
|
324
|
-
valdict = screamlab.functions.
|
|
375
|
+
valdict = screamlab.functions.generate_spectra_param_dict_global(
|
|
325
376
|
self.dataset.lmfit_result_handler.prefit.params
|
|
326
377
|
)
|
|
327
378
|
simspec = [0] * len(y_axis)
|
|
@@ -430,9 +481,19 @@ class Exporter:
|
|
|
430
481
|
|
|
431
482
|
def _plot_global_each_individual(self):
|
|
432
483
|
output_dir = self._generate_output_dir("spectral_deconvolution_plots")
|
|
433
|
-
param_dict =
|
|
434
|
-
|
|
435
|
-
|
|
484
|
+
param_dict = None
|
|
485
|
+
if self.dataset.props.spectrum_fit_type == "global":
|
|
486
|
+
param_dict = (
|
|
487
|
+
screamlab.functions.generate_spectra_param_dict_global(
|
|
488
|
+
self.dataset.lmfit_result_handler.global_fit.params
|
|
489
|
+
)
|
|
490
|
+
)
|
|
491
|
+
elif self.dataset.props.spectrum_fit_type == "individual":
|
|
492
|
+
param_dict = (
|
|
493
|
+
screamlab.functions.generate_spectra_param_dict_individual(
|
|
494
|
+
self.dataset.lmfit_result_handler.global_fit
|
|
495
|
+
)
|
|
496
|
+
)
|
|
436
497
|
|
|
437
498
|
for key, param_list in param_dict.items():
|
|
438
499
|
spectrum = self.dataset.spectra[key]
|
|
@@ -484,10 +545,19 @@ class Exporter:
|
|
|
484
545
|
|
|
485
546
|
def _plot_global_all_together(self):
|
|
486
547
|
output_dir = self._generate_output_dir("spectral_deconvolution_plots")
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
548
|
+
param_dict = None
|
|
549
|
+
if self.dataset.props.spectrum_fit_type == "global":
|
|
550
|
+
param_dict = (
|
|
551
|
+
screamlab.functions.generate_spectra_param_dict_global(
|
|
552
|
+
self.dataset.lmfit_result_handler.global_fit.params
|
|
553
|
+
)
|
|
554
|
+
)
|
|
555
|
+
elif self.dataset.props.spectrum_fit_type == "individual":
|
|
556
|
+
param_dict = (
|
|
557
|
+
screamlab.functions.generate_spectra_param_dict_individual(
|
|
558
|
+
self.dataset.lmfit_result_handler.global_fit
|
|
559
|
+
)
|
|
560
|
+
)
|
|
491
561
|
|
|
492
562
|
num_spectra = len(param_dict)
|
|
493
563
|
cols = 3
|
|
@@ -572,15 +642,23 @@ class Exporter:
|
|
|
572
642
|
f.write("[[Peaks]]\n")
|
|
573
643
|
for peak_nr, peak in enumerate(self.dataset.peak_list):
|
|
574
644
|
f.write(f"[Peak {peak_nr + 1}]\n")
|
|
575
|
-
f.write(
|
|
645
|
+
f.write(peak.to_string(self.dataset.props.spectrum_fit_type))
|
|
576
646
|
f.write("[[Prefit]]\n")
|
|
577
|
-
if
|
|
647
|
+
if (
|
|
648
|
+
self.dataset.props.prefit
|
|
649
|
+
and self.dataset.props.spectrum_fit_type != "numint"
|
|
650
|
+
):
|
|
578
651
|
self._get_prefit_string(f)
|
|
579
652
|
else:
|
|
580
653
|
f.write("No prefit performed.\n")
|
|
581
654
|
|
|
582
|
-
|
|
583
|
-
|
|
655
|
+
if self.dataset.props.spectrum_fit_type != "numint":
|
|
656
|
+
f.write("[[Spectral deconvolution results]]\n")
|
|
657
|
+
self._print_global_fit_results(f)
|
|
658
|
+
|
|
659
|
+
else:
|
|
660
|
+
f.write("[[Numerical integration results]]\n")
|
|
661
|
+
self._print_global_fit_results_numint(f)
|
|
584
662
|
f.write("[[Buildup fit results]]\n")
|
|
585
663
|
self._print_buildup(f)
|
|
586
664
|
|
|
@@ -588,7 +666,7 @@ class Exporter:
|
|
|
588
666
|
for buildup_type in self.dataset.props.buildup_types:
|
|
589
667
|
f.write(f"[{buildup_type}]\n")
|
|
590
668
|
header = screamlab.functions.buildup_header()
|
|
591
|
-
column_widths = [20, 15, 10, 15, 10, 15, 15, 15, 35, 35,
|
|
669
|
+
column_widths = [20, 15, 10, 15, 10, 15, 15, 15, 35, 35, 20, 15]
|
|
592
670
|
f.write(
|
|
593
671
|
"".join(h.ljust(w) for h, w in zip(header, column_widths))
|
|
594
672
|
+ "\n"
|
|
@@ -614,12 +692,42 @@ class Exporter:
|
|
|
614
692
|
+ "\n"
|
|
615
693
|
)
|
|
616
694
|
|
|
617
|
-
def
|
|
618
|
-
|
|
619
|
-
|
|
695
|
+
def _print_global_fit_results_numint(self, f):
|
|
696
|
+
header = ["Label", "Time", "Integral"]
|
|
697
|
+
column_widths = [25, 12, 10]
|
|
698
|
+
f.write(
|
|
699
|
+
"".join(f"{h:<{w}}" for h, w in zip(header, column_widths)) + "\n"
|
|
620
700
|
)
|
|
701
|
+
for peak in self.dataset.peak_list:
|
|
702
|
+
for integral_nr, integral in enumerate(
|
|
703
|
+
peak.buildup_vals.intensity
|
|
704
|
+
):
|
|
705
|
+
line = []
|
|
706
|
+
if integral_nr == 0:
|
|
707
|
+
line.append(peak.peak_label)
|
|
708
|
+
else:
|
|
709
|
+
line.append(" ")
|
|
710
|
+
line.append(peak.buildup_vals.tpol[integral_nr])
|
|
711
|
+
line.append(round(integral, 4))
|
|
712
|
+
f.write(
|
|
713
|
+
"".join(f"{h:<{w}}" for h, w in zip(line, column_widths))
|
|
714
|
+
+ "\n"
|
|
715
|
+
)
|
|
716
|
+
|
|
717
|
+
def _print_global_fit_results(self, f):
|
|
718
|
+
valdict = None
|
|
719
|
+
if self.dataset.props.spectrum_fit_type == "global":
|
|
720
|
+
valdict = screamlab.functions.generate_spectra_param_dict_global(
|
|
721
|
+
self.dataset.lmfit_result_handler.global_fit.params
|
|
722
|
+
)
|
|
723
|
+
elif self.dataset.props.spectrum_fit_type == "individual":
|
|
724
|
+
valdict = (
|
|
725
|
+
screamlab.functions.generate_spectra_param_dict_individual(
|
|
726
|
+
self.dataset.lmfit_result_handler.global_fit
|
|
727
|
+
)
|
|
728
|
+
)
|
|
621
729
|
header = screamlab.functions.spectrum_fit_header()
|
|
622
|
-
column_widths = [25, 12, 15, 20, 15, 15, 22, 20, 20,
|
|
730
|
+
column_widths = [25, 12, 15, 20, 15, 15, 22, 20, 20, 20, 15]
|
|
623
731
|
f.write(
|
|
624
732
|
"".join(f"{h:<{w}}" for h, w in zip(header, column_widths)) + "\n"
|
|
625
733
|
)
|
|
@@ -633,7 +741,7 @@ class Exporter:
|
|
|
633
741
|
)
|
|
634
742
|
|
|
635
743
|
def _get_prefit_string(self, f):
|
|
636
|
-
valdict = screamlab.functions.
|
|
744
|
+
valdict = screamlab.functions.generate_spectra_param_dict_global(
|
|
637
745
|
self.dataset.lmfit_result_handler.prefit.params
|
|
638
746
|
)
|
|
639
747
|
widths = [25, 18, 20, 15, 15]
|
|
@@ -764,9 +872,18 @@ class Exporter:
|
|
|
764
872
|
)
|
|
765
873
|
|
|
766
874
|
with open(output_file_path, "w", encoding="utf-8") as f:
|
|
767
|
-
valdict =
|
|
768
|
-
|
|
769
|
-
|
|
875
|
+
valdict = None
|
|
876
|
+
if self.dataset.props.spectrum_fit_type == "global":
|
|
877
|
+
valdict = (
|
|
878
|
+
screamlab.functions.generate_spectra_param_dict_global(
|
|
879
|
+
self.dataset.lmfit_result_handler.global_fit.params
|
|
880
|
+
)
|
|
881
|
+
)
|
|
882
|
+
elif self.dataset.props.spectrum_fit_type == "individual":
|
|
883
|
+
valdict = screamlab.functions.generate_spectra_param_dict_individual(
|
|
884
|
+
self.dataset.lmfit_result_handler.global_fit
|
|
885
|
+
)
|
|
886
|
+
|
|
770
887
|
header = screamlab.functions.spectrum_fit_header()
|
|
771
888
|
f.write(";".join(str(item) for item in header) + "\n")
|
|
772
889
|
for delay_time in range(0, len(valdict[0])):
|
|
@@ -859,6 +976,7 @@ class Exporter:
|
|
|
859
976
|
return row
|
|
860
977
|
|
|
861
978
|
def _gen_voigt_output(self, values, delay_time, val_nr):
|
|
979
|
+
|
|
862
980
|
return [
|
|
863
981
|
(
|
|
864
982
|
self.dataset.peak_list[delay_time].peak_label
|
screamlab/settings.py
CHANGED
|
@@ -80,12 +80,20 @@ class Properties:
|
|
|
80
80
|
return self._subspec
|
|
81
81
|
|
|
82
82
|
@subspec.setter
|
|
83
|
-
def subspec(self, value
|
|
84
|
-
|
|
85
|
-
if value is not None:
|
|
86
|
-
self._subspec = value
|
|
87
|
-
else:
|
|
83
|
+
def subspec(self, value):
|
|
84
|
+
if value is None:
|
|
88
85
|
self._subspec = []
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
try:
|
|
89
|
+
self._subspec = [float(v) for v in value]
|
|
90
|
+
except (TypeError, ValueError) as exc:
|
|
91
|
+
raise ValueError(
|
|
92
|
+
"subspec must be a sequence of two floats"
|
|
93
|
+
) from exc
|
|
94
|
+
|
|
95
|
+
if len(self._subspec) != 2:
|
|
96
|
+
raise ValueError("subspec must contain exactly two values")
|
|
89
97
|
|
|
90
98
|
@property
|
|
91
99
|
def output_folder(self) -> str:
|
|
@@ -181,7 +189,7 @@ class Properties:
|
|
|
181
189
|
"""
|
|
182
190
|
str, optional: A list specifying the spectrum fit type
|
|
183
191
|
|
|
184
|
-
Options supporded: "global","independent".
|
|
192
|
+
Options supporded: "global", "independent, "numint".
|
|
185
193
|
|
|
186
194
|
"""
|
|
187
195
|
return self._spectrum_fit_type
|
|
@@ -189,10 +197,7 @@ class Properties:
|
|
|
189
197
|
@spectrum_fit_type.setter
|
|
190
198
|
def spectrum_fit_type(self, value: Any):
|
|
191
199
|
"""Sets the spectrum fit type"""
|
|
192
|
-
allowed_values = {
|
|
193
|
-
"global",
|
|
194
|
-
"individual",
|
|
195
|
-
}
|
|
200
|
+
allowed_values = {"global", "individual", "numint"}
|
|
196
201
|
if not isinstance(value, str):
|
|
197
202
|
raise TypeError(
|
|
198
203
|
f"Expected 'spectrum_fit_type' to be of type 'str', got"
|
|
@@ -227,7 +232,9 @@ class Properties:
|
|
|
227
232
|
"biexponential",
|
|
228
233
|
"biexponential_with_offset",
|
|
229
234
|
"exponential_with_offset",
|
|
230
|
-
"
|
|
235
|
+
"stretched_exponential",
|
|
236
|
+
"exponential_decay",
|
|
237
|
+
"exponential_decay_with_offset",
|
|
231
238
|
}
|
|
232
239
|
if not isinstance(value, list):
|
|
233
240
|
raise TypeError(
|
screamlab/utils.py
CHANGED
|
@@ -23,6 +23,7 @@ Classes:
|
|
|
23
23
|
"""
|
|
24
24
|
|
|
25
25
|
import copy
|
|
26
|
+
from datetime import datetime
|
|
26
27
|
import numpy as np
|
|
27
28
|
import lmfit
|
|
28
29
|
from pyDOE3 import lhs
|
|
@@ -33,7 +34,7 @@ class Fitter:
|
|
|
33
34
|
"""
|
|
34
35
|
Base class for spectral fitting using `lmfit`.
|
|
35
36
|
|
|
36
|
-
This class handles parameter initialization and spectral fitting for a
|
|
37
|
+
This class handles parameter initialization and spectral fitting for a dataset.
|
|
37
38
|
|
|
38
39
|
Attributes
|
|
39
40
|
----------
|
|
@@ -44,7 +45,7 @@ class Fitter:
|
|
|
44
45
|
|
|
45
46
|
def __init__(self, dataset):
|
|
46
47
|
"""
|
|
47
|
-
Initializes the Fitter with a
|
|
48
|
+
Initializes the Fitter with a dataset.
|
|
48
49
|
|
|
49
50
|
Args
|
|
50
51
|
----
|
|
@@ -66,7 +67,6 @@ class Fitter:
|
|
|
66
67
|
x_axis, y_axis = self._generate_axis_list()
|
|
67
68
|
params = self._generate_params_list()
|
|
68
69
|
params = self._set_param_expr(params)
|
|
69
|
-
|
|
70
70
|
return self._start_minimize(x_axis, y_axis, params)
|
|
71
71
|
|
|
72
72
|
def _start_minimize(self, x_axis, y_axis, params):
|
|
@@ -194,8 +194,8 @@ class Fitter:
|
|
|
194
194
|
return {
|
|
195
195
|
"name": f"{peak.peak_label}_cen_{nr}",
|
|
196
196
|
"value": peak.peak_center,
|
|
197
|
-
"min": peak.peak_center -
|
|
198
|
-
"max": peak.peak_center +
|
|
197
|
+
"min": peak.peak_center - 1,
|
|
198
|
+
"max": peak.peak_center + 1,
|
|
199
199
|
}
|
|
200
200
|
|
|
201
201
|
def _get_lw_dict(self, peak, nr, lw):
|
|
@@ -240,7 +240,9 @@ class Fitter:
|
|
|
240
240
|
|
|
241
241
|
"""
|
|
242
242
|
residual = copy.deepcopy(y_axis)
|
|
243
|
-
params_dict_list = functions.
|
|
243
|
+
params_dict_list = functions.generate_spectra_param_dict_global(
|
|
244
|
+
params
|
|
245
|
+
)
|
|
244
246
|
for key, val_list in params_dict_list.items():
|
|
245
247
|
for val in val_list:
|
|
246
248
|
simspec = [0 for _ in range(len(x_axis[key]))]
|
|
@@ -281,6 +283,71 @@ class Prefitter(Fitter):
|
|
|
281
283
|
return result
|
|
282
284
|
|
|
283
285
|
|
|
286
|
+
class NumericalIntegration(Fitter):
|
|
287
|
+
"""
|
|
288
|
+
Subclass of Fitter that calculates spectral signal intensities via numerical integration.
|
|
289
|
+
|
|
290
|
+
This class is designed for applications where the area under a curve
|
|
291
|
+
in a given spectral window is of interest, such as quantifying peak
|
|
292
|
+
intensities in spectroscopy or other analytical measurements.
|
|
293
|
+
By integrating over a defined region, it provides a robust measure
|
|
294
|
+
of signal strength that accounts for variations in peak shape
|
|
295
|
+
and baseline fluctuations.
|
|
296
|
+
"""
|
|
297
|
+
|
|
298
|
+
def fit(self):
|
|
299
|
+
"""
|
|
300
|
+
Performs spectral numerical integration using numpy trapz or trapezoid function.
|
|
301
|
+
|
|
302
|
+
Returns
|
|
303
|
+
-------
|
|
304
|
+
dict
|
|
305
|
+
The result of the integration process.
|
|
306
|
+
|
|
307
|
+
"""
|
|
308
|
+
integrals = {}
|
|
309
|
+
for spectrum in self.dataset.spectra:
|
|
310
|
+
for peak in self.dataset.peak_list:
|
|
311
|
+
if peak not in integrals:
|
|
312
|
+
integrals[peak] = []
|
|
313
|
+
self._check_integration_range_in_spectra(spectrum, peak)
|
|
314
|
+
subspec_x_axis, subspec_y_axis = functions.generate_subspec(
|
|
315
|
+
spectrum, peak.integration_range
|
|
316
|
+
)
|
|
317
|
+
if hasattr(np, "trapezoid"):
|
|
318
|
+
integrals[peak].append(
|
|
319
|
+
np.trapezoid(
|
|
320
|
+
subspec_y_axis[::-1], subspec_x_axis[::-1]
|
|
321
|
+
)
|
|
322
|
+
)
|
|
323
|
+
else:
|
|
324
|
+
integrals[peak].append(
|
|
325
|
+
np.trapz(subspec_y_axis[::-1], subspec_x_axis[::-1])
|
|
326
|
+
)
|
|
327
|
+
return integrals
|
|
328
|
+
|
|
329
|
+
def _check_integration_range_in_spectra(self, spectrum, peak):
|
|
330
|
+
self._check_if_integration_range_are_set(peak)
|
|
331
|
+
for integration_boundary in peak.integration_range:
|
|
332
|
+
if not (
|
|
333
|
+
min(spectrum.x_axis)
|
|
334
|
+
<= integration_boundary
|
|
335
|
+
<= max(spectrum.x_axis)
|
|
336
|
+
):
|
|
337
|
+
raise ValueError(
|
|
338
|
+
f"Integration boundary {integration_boundary} is outside the spectrum "
|
|
339
|
+
f"range ({min(spectrum.x_axis)} – {max(spectrum.x_axis)})."
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
def _check_if_integration_range_are_set(self, peak):
|
|
343
|
+
if peak.integration_range is None:
|
|
344
|
+
raise ValueError(
|
|
345
|
+
f"Peak '{peak.peak_label}' ({peak.peak_center:.3f} ppm): "
|
|
346
|
+
f"integration_range is not set. "
|
|
347
|
+
f"Call dataset.add_peak({peak.peak_center}, integration_range=(from_ppm, to_ppm))."
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
|
|
284
351
|
class GlobalFitter(Fitter):
|
|
285
352
|
"""
|
|
286
353
|
Global fit over all spectra at different polarization times.
|
|
@@ -329,6 +396,77 @@ class IndependentFitter(Fitter):
|
|
|
329
396
|
must be ensured that all spectra can be fitted by conditions given in point two.
|
|
330
397
|
"""
|
|
331
398
|
|
|
399
|
+
def _generate_params_list(self):
|
|
400
|
+
"""
|
|
401
|
+
Generates initial fitting parameters based on peak information in the ds.
|
|
402
|
+
|
|
403
|
+
Returns
|
|
404
|
+
-------
|
|
405
|
+
lmfit.Parameters
|
|
406
|
+
The initialized parameters for fitting.
|
|
407
|
+
|
|
408
|
+
"""
|
|
409
|
+
params_list = []
|
|
410
|
+
spectra = self._get_spectra_list()
|
|
411
|
+
lw_types = {
|
|
412
|
+
"voigt": ["sigma", "gamma"],
|
|
413
|
+
"gauss": ["sigma"],
|
|
414
|
+
"lorentz": ["gamma"],
|
|
415
|
+
}
|
|
416
|
+
for spectrum_nr, _ in enumerate(spectra):
|
|
417
|
+
params = lmfit.Parameters()
|
|
418
|
+
for peak in self.dataset.peak_list:
|
|
419
|
+
params.add(**self._get_amplitude_dict(peak, spectrum_nr))
|
|
420
|
+
params.add(**self._get_center_dict(peak, spectrum_nr))
|
|
421
|
+
|
|
422
|
+
for lw_type in lw_types.get(peak.fitting_type, []):
|
|
423
|
+
params.add(
|
|
424
|
+
**self._get_lw_dict(peak, spectrum_nr, lw_type)
|
|
425
|
+
)
|
|
426
|
+
params_list.append(params)
|
|
427
|
+
return params_list
|
|
428
|
+
|
|
429
|
+
def _start_minimize(self, x_axis, y_axis, params):
|
|
430
|
+
all_results = []
|
|
431
|
+
for spectrum_nr, spectrum in enumerate(x_axis):
|
|
432
|
+
print(
|
|
433
|
+
f"\t{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}: "
|
|
434
|
+
f"Start fitting spectrum number {spectrum_nr+1}/{len(x_axis)}"
|
|
435
|
+
)
|
|
436
|
+
result = lmfit.minimize(
|
|
437
|
+
self._spectral_fitting,
|
|
438
|
+
params[spectrum_nr],
|
|
439
|
+
args=(spectrum, y_axis[spectrum_nr]),
|
|
440
|
+
)
|
|
441
|
+
all_results.append(result)
|
|
442
|
+
return all_results
|
|
443
|
+
|
|
444
|
+
def _spectral_fitting(self, params, x_axis, y_axis):
|
|
445
|
+
"""
|
|
446
|
+
Computes the residual between the fitted and experimental spectra.
|
|
447
|
+
|
|
448
|
+
Args
|
|
449
|
+
----
|
|
450
|
+
params (lmfit.Parameters): The fitting parameters.
|
|
451
|
+
x_axis (list): List of x-axis values.
|
|
452
|
+
y_axis (list): List of y-axis values.
|
|
453
|
+
|
|
454
|
+
Returns
|
|
455
|
+
-------
|
|
456
|
+
np.ndarray: The residual between the fitted and experimental spectra.
|
|
457
|
+
|
|
458
|
+
"""
|
|
459
|
+
residual = y_axis.copy()
|
|
460
|
+
params_dict_list = functions.generate_spectra_param_dict_global(
|
|
461
|
+
params
|
|
462
|
+
)
|
|
463
|
+
for _, val_list in params_dict_list.items():
|
|
464
|
+
for val in val_list:
|
|
465
|
+
simspec = [0 for _ in range(len(x_axis))]
|
|
466
|
+
simspec = functions.calc_peak(x_axis, simspec, val)
|
|
467
|
+
residual -= simspec
|
|
468
|
+
return residual
|
|
469
|
+
|
|
332
470
|
|
|
333
471
|
class BuildupFitter:
|
|
334
472
|
"""
|
|
@@ -483,6 +621,27 @@ class BuildupFitter:
|
|
|
483
621
|
"""
|
|
484
622
|
return [params[key].value for key in params]
|
|
485
623
|
|
|
624
|
+
def _get_intensity_offset_dict(self, peak):
|
|
625
|
+
"""
|
|
626
|
+
Generate intensity offset parameter dictionary.
|
|
627
|
+
|
|
628
|
+
:param peak: Peak object containing buildup values.
|
|
629
|
+
:return: Dictionary with default intensity parameter values.
|
|
630
|
+
"""
|
|
631
|
+
return (
|
|
632
|
+
{
|
|
633
|
+
"value": 10,
|
|
634
|
+
"min": max(peak.buildup_vals.intensity) * -3,
|
|
635
|
+
"max": max(peak.buildup_vals.intensity) * 3,
|
|
636
|
+
}
|
|
637
|
+
if peak.peak_sign == "+"
|
|
638
|
+
else {
|
|
639
|
+
"value": 10,
|
|
640
|
+
"max": min(peak.buildup_vals.intensity) * -3,
|
|
641
|
+
"min": min(peak.buildup_vals.intensity) * 3,
|
|
642
|
+
}
|
|
643
|
+
)
|
|
644
|
+
|
|
486
645
|
def _get_intensity_dict(self, peak):
|
|
487
646
|
"""
|
|
488
647
|
Generate intensity parameter dictionary.
|
|
@@ -559,6 +718,88 @@ class BiexpFitter(BuildupFitter):
|
|
|
559
718
|
return functions.calc_biexponential(tdel, param)
|
|
560
719
|
|
|
561
720
|
|
|
721
|
+
class ExpDecayFitter(BuildupFitter):
|
|
722
|
+
"""
|
|
723
|
+
Class for fitting exponential decay models to experimental data.
|
|
724
|
+
|
|
725
|
+
The exponential decay model fits decay curves using one exponential term
|
|
726
|
+
characterized by an amplitude (A) and a time constants (t).
|
|
727
|
+
|
|
728
|
+
The model function is defined as:
|
|
729
|
+
I(t_pol) = A * exp(-t_pol / t))
|
|
730
|
+
|
|
731
|
+
where:
|
|
732
|
+
- A : amplitudes of the exponential components
|
|
733
|
+
- t : time constants of the exponential components (t > 0)
|
|
734
|
+
- t_pol : polarization time (independent variable)
|
|
735
|
+
- I(t_pol) : peak intensity at polarization time t_pol
|
|
736
|
+
"""
|
|
737
|
+
|
|
738
|
+
def _get_default_param_dict(self, peak):
|
|
739
|
+
"""
|
|
740
|
+
Define default parameters for exponential decay fitting.
|
|
741
|
+
|
|
742
|
+
:param peak: Peak object containing peak_sign and buildup values.
|
|
743
|
+
:return: Dictionary of default parameters with keys: A, t.
|
|
744
|
+
"""
|
|
745
|
+
return {
|
|
746
|
+
"Af": self._get_intensity_dict(peak),
|
|
747
|
+
"tf": self._get_time_dict(peak),
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
def _calc_intensity(self, tdel, param):
|
|
751
|
+
"""
|
|
752
|
+
Calculate exponential decay intensity.
|
|
753
|
+
|
|
754
|
+
:param tdel: Time delays.
|
|
755
|
+
:param param: List of parameters.
|
|
756
|
+
:return: Calculated intensity values.
|
|
757
|
+
"""
|
|
758
|
+
return functions.calc_expdecay(tdel, param)
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
class ExpDecayFitterWithOffset(BuildupFitter):
|
|
762
|
+
"""
|
|
763
|
+
Class for fitting exponential decay models to experimental data.
|
|
764
|
+
|
|
765
|
+
The exponential decay model fits decay curves using one exponential term
|
|
766
|
+
characterized by an amplitude (A), an intensity offset (I0) and a time constants (t).
|
|
767
|
+
|
|
768
|
+
The model function is defined as:
|
|
769
|
+
I(t_pol) = I0 + A * exp(-t_pol / t))
|
|
770
|
+
|
|
771
|
+
where:
|
|
772
|
+
- A : amplitudes of the exponential components
|
|
773
|
+
- t : time constants of the exponential components (t > 0)
|
|
774
|
+
- t_pol : polarization time (independent variable)
|
|
775
|
+
- I0 : Intensity offset
|
|
776
|
+
- I(t_pol) : peak intensity at polarization time t_pol
|
|
777
|
+
"""
|
|
778
|
+
|
|
779
|
+
def _get_default_param_dict(self, peak):
|
|
780
|
+
"""
|
|
781
|
+
Define default parameters for exponential decay fitting with offset.
|
|
782
|
+
|
|
783
|
+
:param peak: Peak object containing peak_sign and buildup values.
|
|
784
|
+
:return: Dictionary of default parameters with keys: A, t, I0.
|
|
785
|
+
"""
|
|
786
|
+
return {
|
|
787
|
+
"Af": self._get_intensity_dict(peak),
|
|
788
|
+
"tf": self._get_time_dict(peak),
|
|
789
|
+
"I0": self._get_intensity_offset_dict(peak),
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
def _calc_intensity(self, tdel, param):
|
|
793
|
+
"""
|
|
794
|
+
Calculate exponential decay intensity.
|
|
795
|
+
|
|
796
|
+
:param tdel: Time delays.
|
|
797
|
+
:param param: List of parameters.
|
|
798
|
+
:return: Calculated intensity values.
|
|
799
|
+
"""
|
|
800
|
+
return functions.calc_expdecaywithoffset(tdel, param)
|
|
801
|
+
|
|
802
|
+
|
|
562
803
|
class BiexpFitterWithOffset(BuildupFitter):
|
|
563
804
|
"""
|
|
564
805
|
Class for fitting biexponential models with offset to buildup data.
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: screamlab
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Package for reproducible evaluation of SCREAM-DNP data.
|
|
5
5
|
Home-page: https://github.com/FlorianTaube/screamlab
|
|
6
6
|
Author: Florian Taube
|
|
7
7
|
Author-email: florian.taube@uni-rostock.de
|
|
8
8
|
License: BSD
|
|
9
|
-
Project-URL: Documentation, https://
|
|
9
|
+
Project-URL: Documentation, https://screamlab.readthedocs.io
|
|
10
10
|
Project-URL: Source, https://github.com/FlorianTaube/screamlab/screamlab
|
|
11
11
|
Keywords: Buildup Time Calculator,NMR,DNP,SCREAM_DNP,TopspinExport
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
screamlab/__init__.py,sha256=M2vfzk7vI3qvlxE2uIHyGYmPSrFYNUacFjkjiLX00wc,63
|
|
2
|
+
screamlab/dataset.py,sha256=d4Ztrr-gAYiNA8nGi5zhkQeRvdBdI6FblYBst3XLnzQ,26837
|
|
3
|
+
screamlab/functions.py,sha256=zujGG87iwcBfl8clg1JUG1SYYTIO4XutNmCVhlZME4M,14638
|
|
4
|
+
screamlab/io.py,sha256=deZEbf9gFn3OIr3lCS_ZbklGni-YG3_DBuNMEt710EQ,42968
|
|
5
|
+
screamlab/settings.py,sha256=4lUsrTj6Ne9RQPmjSy1EAM5M2I8EwaM4ZpVE8-GP-JM,9459
|
|
6
|
+
screamlab/utils.py,sha256=Ca82YmSmKsZf-EX9dra0WEcZrJWYcWdsnzs3xQDtHoQ,34268
|
|
7
|
+
screamlab/__pycache__/__init__.cpython-310.pyc,sha256=nGadfOt1WUjQxMQAWhkaJ8HQIBoFYgJOXOcou6xtrJ4,245
|
|
8
|
+
screamlab/__pycache__/__init__.cpython-312.pyc,sha256=Vo2caaR8VoJ8_NleXq-XOrPKFsFfwxsYJtyJjRxMaAQ,253
|
|
9
|
+
screamlab/__pycache__/__init__.cpython-313.pyc,sha256=yQf9pPv0AH2e55exkKjAhkP7dXZn17lnMgudh9XDnxo,249
|
|
10
|
+
screamlab/__pycache__/__init__.cpython-38.pyc,sha256=M5eZ95kmsA8r__ZH_prnNqi--MGl8WclL9g0yUXGTAo,176
|
|
11
|
+
screamlab/__pycache__/dataset.cpython-310.pyc,sha256=wIfwBQFdYvUbanp8okbVfyF4Txdx0DwPrID_AG054QM,24961
|
|
12
|
+
screamlab/__pycache__/dataset.cpython-312.pyc,sha256=6I6pajXpcAGzgMMWz8Uz8RBFvHhamr8ldh4LYvgmVHY,29658
|
|
13
|
+
screamlab/__pycache__/dataset.cpython-313.pyc,sha256=SZHG1m9zZ8iKTrETEkd1IyrZ0ts41wyONaug2zFYPNc,33499
|
|
14
|
+
screamlab/__pycache__/functions.cpython-310.pyc,sha256=QFIT-SC3IypiT3FjxR4rdFaX3mBCuNmI7gwHj3IPIWI,12089
|
|
15
|
+
screamlab/__pycache__/functions.cpython-312.pyc,sha256=3XGaxzxxkM11879B0tD2igT9kx-_s3lk1fhABYwIPec,13136
|
|
16
|
+
screamlab/__pycache__/functions.cpython-313.pyc,sha256=ozCyYcTb6HU6BBgz0Byag3-EYVdqTTNHmU1VqAFnVNU,12631
|
|
17
|
+
screamlab/__pycache__/io.cpython-310.pyc,sha256=qbi2IVwZfOTr8nZ1u4vxsgQtsqlxMAJxDUYUIZn3ZaM,33595
|
|
18
|
+
screamlab/__pycache__/io.cpython-312.pyc,sha256=MMoeXwNuzsekbKxuU151oMMqJ_Hn-gQ-GmwSHULdVOA,53564
|
|
19
|
+
screamlab/__pycache__/io.cpython-313.pyc,sha256=FqolkwTPOY7ViOsfEGFzf5pYfif4ikfFPDlXoG-tdlA,51707
|
|
20
|
+
screamlab/__pycache__/settings.cpython-310.pyc,sha256=JDbCK7P7CAU_xoh3uwcKJhyge-7TXD59RKRG6RcV6Hs,8642
|
|
21
|
+
screamlab/__pycache__/settings.cpython-312.pyc,sha256=C_E_orj-EgVwor9s8eqF0hBs-vejOFEglWI7DN1U6Yo,11907
|
|
22
|
+
screamlab/__pycache__/settings.cpython-313.pyc,sha256=m1P8CXfLTds2c40e-pmtBuWTfKI6X3YaroXsZL-mV7Q,12019
|
|
23
|
+
screamlab/__pycache__/utils.cpython-310.pyc,sha256=vG1o5HO3D_tTicOQJv1gYdkAg-9PhB2jnXxA5QVoSSU,32103
|
|
24
|
+
screamlab/__pycache__/utils.cpython-312.pyc,sha256=NkdFr8Yr-ywDfFqTJK3QUro_YMGyezhQ04YF5Va4yGo,30799
|
|
25
|
+
screamlab/__pycache__/utils.cpython-313.pyc,sha256=6nj3DrKpIb11HUDjo1ubnP4ymMBE8S2vuacY3zGjhNk,29120
|
|
26
|
+
screamlab-0.3.1.dist-info/licenses/LICENSE,sha256=Ic_N1IFIdReR7m-W6XtN_4k6B8IgJ33dQFLc6Xi5C2E,1291
|
|
27
|
+
screamlab-0.3.1.dist-info/METADATA,sha256=cPkvLs3GDzSemCu4hNU0Xnq4-C6RxaYBw2Et0VrLQYU,2942
|
|
28
|
+
screamlab-0.3.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
29
|
+
screamlab-0.3.1.dist-info/top_level.txt,sha256=XjErA4-PG_phESo1eWs-bMzdnBWu4gNxB-RX78zZqqw,10
|
|
30
|
+
screamlab-0.3.1.dist-info/RECORD,,
|
screamlab-0.1.0.dist-info/RECORD
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
screamlab/__init__.py,sha256=M2vfzk7vI3qvlxE2uIHyGYmPSrFYNUacFjkjiLX00wc,63
|
|
2
|
-
screamlab/dataset.py,sha256=QZSGwm3au08ZEBLdH-pmkyXaeatwBVMZQKs-37j3IQ4,22537
|
|
3
|
-
screamlab/functions.py,sha256=vq6YseAt6L0YlduI6LV3_WBcLhFaOqDRGDIIDaZ3kfc,11398
|
|
4
|
-
screamlab/io.py,sha256=B5I9oxQ4uH2dExnoDlO7Glbk3KcrXSVJcWTEl05eLOk,38017
|
|
5
|
-
screamlab/settings.py,sha256=XBlzWjsdcAO3Fq_d9UyxYViirT9qmpw5_UVrYd-lt0o,9130
|
|
6
|
-
screamlab/utils.py,sha256=VbHaH_QWfcj2WM-TrraPCdjMr7qJ5ybkc2Vgguqhvxg,25503
|
|
7
|
-
screamlab/__pycache__/__init__.cpython-310.pyc,sha256=nGadfOt1WUjQxMQAWhkaJ8HQIBoFYgJOXOcou6xtrJ4,245
|
|
8
|
-
screamlab/__pycache__/__init__.cpython-312.pyc,sha256=Vo2caaR8VoJ8_NleXq-XOrPKFsFfwxsYJtyJjRxMaAQ,253
|
|
9
|
-
screamlab/__pycache__/__init__.cpython-313.pyc,sha256=yQf9pPv0AH2e55exkKjAhkP7dXZn17lnMgudh9XDnxo,249
|
|
10
|
-
screamlab/__pycache__/__init__.cpython-38.pyc,sha256=M5eZ95kmsA8r__ZH_prnNqi--MGl8WclL9g0yUXGTAo,176
|
|
11
|
-
screamlab/__pycache__/dataset.cpython-310.pyc,sha256=_faFHwzFWVQ12RQn_JEmVacRbG3kfRjiq2oZMFC0lKU,21615
|
|
12
|
-
screamlab/__pycache__/dataset.cpython-312.pyc,sha256=6I6pajXpcAGzgMMWz8Uz8RBFvHhamr8ldh4LYvgmVHY,29658
|
|
13
|
-
screamlab/__pycache__/dataset.cpython-313.pyc,sha256=hUv5iLuEbc1ZvsghDsBs5fMJX8P2c9LVODugU9AUq6Q,28966
|
|
14
|
-
screamlab/__pycache__/functions.cpython-310.pyc,sha256=NCAC7EjK1gJCi5sdEcferiMHbFVOhTL6jme0-0-5xKg,10221
|
|
15
|
-
screamlab/__pycache__/functions.cpython-312.pyc,sha256=3XGaxzxxkM11879B0tD2igT9kx-_s3lk1fhABYwIPec,13136
|
|
16
|
-
screamlab/__pycache__/io.cpython-310.pyc,sha256=5l2qLRHXu0_xQIeBtpRf4on_aX8z9M8Y-pvPWRNNxMM,30699
|
|
17
|
-
screamlab/__pycache__/io.cpython-312.pyc,sha256=MMoeXwNuzsekbKxuU151oMMqJ_Hn-gQ-GmwSHULdVOA,53564
|
|
18
|
-
screamlab/__pycache__/settings.cpython-310.pyc,sha256=NEb0GFRJNxnFdCyQSv5WPBp6yPT15dwC8_IHQVpB0Us,8263
|
|
19
|
-
screamlab/__pycache__/settings.cpython-312.pyc,sha256=C_E_orj-EgVwor9s8eqF0hBs-vejOFEglWI7DN1U6Yo,11907
|
|
20
|
-
screamlab/__pycache__/settings.cpython-313.pyc,sha256=bkB5KCTnxR8rKtrYezCt753f-cnjwhTfXZL2LsVTiaE,11800
|
|
21
|
-
screamlab/__pycache__/utils.cpython-310.pyc,sha256=1ADymFGnozfp-gqDCH8PMxPKGDAutepRqsPheXKcuQ0,25452
|
|
22
|
-
screamlab/__pycache__/utils.cpython-312.pyc,sha256=NkdFr8Yr-ywDfFqTJK3QUro_YMGyezhQ04YF5Va4yGo,30799
|
|
23
|
-
screamlab-0.1.0.dist-info/licenses/LICENSE,sha256=Ic_N1IFIdReR7m-W6XtN_4k6B8IgJ33dQFLc6Xi5C2E,1291
|
|
24
|
-
screamlab-0.1.0.dist-info/METADATA,sha256=rJ7ubHx5d35-l0izBb6ax6N4Sosrjb6kqlVZwaCLnEU,2956
|
|
25
|
-
screamlab-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
26
|
-
screamlab-0.1.0.dist-info/top_level.txt,sha256=XjErA4-PG_phESo1eWs-bMzdnBWu4gNxB-RX78zZqqw,10
|
|
27
|
-
screamlab-0.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|