screamlab 0.1.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.
- screamlab/__init__.py +1 -0
- screamlab/__pycache__/__init__.cpython-310.pyc +0 -0
- screamlab/__pycache__/__init__.cpython-312.pyc +0 -0
- screamlab/__pycache__/__init__.cpython-313.pyc +0 -0
- screamlab/__pycache__/__init__.cpython-38.pyc +0 -0
- screamlab/__pycache__/dataset.cpython-310.pyc +0 -0
- screamlab/__pycache__/dataset.cpython-312.pyc +0 -0
- screamlab/__pycache__/dataset.cpython-313.pyc +0 -0
- screamlab/__pycache__/functions.cpython-310.pyc +0 -0
- screamlab/__pycache__/functions.cpython-312.pyc +0 -0
- screamlab/__pycache__/io.cpython-310.pyc +0 -0
- screamlab/__pycache__/io.cpython-312.pyc +0 -0
- screamlab/__pycache__/settings.cpython-310.pyc +0 -0
- screamlab/__pycache__/settings.cpython-312.pyc +0 -0
- screamlab/__pycache__/settings.cpython-313.pyc +0 -0
- screamlab/__pycache__/utils.cpython-310.pyc +0 -0
- screamlab/__pycache__/utils.cpython-312.pyc +0 -0
- screamlab/dataset.py +655 -0
- screamlab/functions.py +415 -0
- screamlab/io.py +984 -0
- screamlab/settings.py +281 -0
- screamlab/utils.py +729 -0
- screamlab-0.1.0.dist-info/METADATA +90 -0
- screamlab-0.1.0.dist-info/RECORD +27 -0
- screamlab-0.1.0.dist-info/WHEEL +5 -0
- screamlab-0.1.0.dist-info/licenses/LICENSE +24 -0
- screamlab-0.1.0.dist-info/top_level.txt +1 -0
screamlab/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Package for reproducible evaluation of SCREAM-DNP data."""
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
screamlab/dataset.py
ADDED
|
@@ -0,0 +1,655 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Spectral and Peak information containing module.
|
|
3
|
+
|
|
4
|
+
This moduel provides classes for handling and processing of spectral/peak information as well as
|
|
5
|
+
results from spectral fitting.
|
|
6
|
+
|
|
7
|
+
Classes:
|
|
8
|
+
Dataset: Represents the hole dataset and provides all functions needed to start analysis.
|
|
9
|
+
Spectra: Represents spectral data from NMR (Nuclear Magnetic Resonance) experiments.
|
|
10
|
+
Peak: Represents a peak with its properties.
|
|
11
|
+
BuildupList: Represents a list of buildup values used for fitting delay times and intensities.
|
|
12
|
+
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from datetime import datetime
|
|
16
|
+
import numpy as np
|
|
17
|
+
from screamlab import io, utils, settings, functions
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Dataset:
|
|
21
|
+
"""Represents a dataset containing NMR spectra, peak fitting, and buildup fitting."""
|
|
22
|
+
|
|
23
|
+
def __init__(self, props=settings.Properties()):
|
|
24
|
+
"""
|
|
25
|
+
Initialize the Dataset with default or specified properties.
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
props : settings.Properties, optional
|
|
30
|
+
Experiment properties used to configure the ds.
|
|
31
|
+
Defaults to settings.Properties().
|
|
32
|
+
|
|
33
|
+
"""
|
|
34
|
+
self.importer = None
|
|
35
|
+
self.props = props
|
|
36
|
+
self.spectra = []
|
|
37
|
+
self.fitter = None
|
|
38
|
+
self.peak_list = []
|
|
39
|
+
self.lmfit_result_handler = io.LmfitResultHandler()
|
|
40
|
+
|
|
41
|
+
def __str__(self):
|
|
42
|
+
"""Returns a string representation of the ds."""
|
|
43
|
+
return (
|
|
44
|
+
f"[[Dataset]]\n"
|
|
45
|
+
f"Fitted {len(self.peak_list)} peaks per spectrum in {len(self.spectra)} spectra."
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
def start_analysis(self):
|
|
49
|
+
"""
|
|
50
|
+
Starting the analysis process.
|
|
51
|
+
|
|
52
|
+
The analysis is carried out in three stages: first, the spectral data are imported
|
|
53
|
+
from the Topspin file format; second, spectral deconvolution is performed; and finally,
|
|
54
|
+
a buildup fit is applied.
|
|
55
|
+
|
|
56
|
+
"""
|
|
57
|
+
print(
|
|
58
|
+
f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}: "
|
|
59
|
+
f"Start loading data from topspin: {self.props.path_to_experiment}"
|
|
60
|
+
)
|
|
61
|
+
self._read_in_data_from_topspin()
|
|
62
|
+
print(
|
|
63
|
+
f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}: Start fitting."
|
|
64
|
+
)
|
|
65
|
+
self._calculate_peak_intensities()
|
|
66
|
+
print(
|
|
67
|
+
f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}: Start buildup fit."
|
|
68
|
+
)
|
|
69
|
+
self._start_buildup_fit()
|
|
70
|
+
print(
|
|
71
|
+
f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}: "
|
|
72
|
+
f"Start generating result files. ({self.props.output_folder})"
|
|
73
|
+
)
|
|
74
|
+
self._print_all()
|
|
75
|
+
|
|
76
|
+
def _start_buildup_fit_from_spectra(self):
|
|
77
|
+
"""Starts buildup fitting using data imported from spectra CSV files."""
|
|
78
|
+
|
|
79
|
+
def _start_buildup_from_intensitys(self):
|
|
80
|
+
"""Placeholder for starting buildup fitting from intensity values."""
|
|
81
|
+
|
|
82
|
+
def add_peak(
|
|
83
|
+
self,
|
|
84
|
+
center_of_peak,
|
|
85
|
+
peak_label="",
|
|
86
|
+
fitting_type="voigt",
|
|
87
|
+
peak_sign="-",
|
|
88
|
+
line_broadening=None,
|
|
89
|
+
):
|
|
90
|
+
"""
|
|
91
|
+
Adds a peak to the ds.
|
|
92
|
+
|
|
93
|
+
Attributes
|
|
94
|
+
----------
|
|
95
|
+
center_of_peak (float): Peak position in ppm (chemical shift).
|
|
96
|
+
peak_label (str, optional): Custom label. Defaults to "Peak_at_<ppm>_ppm".
|
|
97
|
+
fitting_type (str, optional): Peak shape: "gauss", "lorentz", or "voigt" (default).
|
|
98
|
+
peak_sign (str, optional): "+" for upward, "-" for downward peaks. Defaults to "+".
|
|
99
|
+
line_broadening (dict, optional): Dict with "sigma" and "gamma" keys for line width.
|
|
100
|
+
Defaults to {"sigma": {"min": 0, "max": 3}, "gamma": {"min": 0, "max": 3}}.
|
|
101
|
+
|
|
102
|
+
"""
|
|
103
|
+
if line_broadening is None:
|
|
104
|
+
line_broadening = {}
|
|
105
|
+
self.peak_list.append(Peak())
|
|
106
|
+
peak = self.peak_list[-1]
|
|
107
|
+
peak.peak_center = center_of_peak
|
|
108
|
+
peak.peak_label = peak_label
|
|
109
|
+
peak.fitting_type = fitting_type
|
|
110
|
+
peak.peak_sign = peak_sign
|
|
111
|
+
peak.line_broadening = line_broadening
|
|
112
|
+
|
|
113
|
+
def _read_in_data_from_topspin(self):
|
|
114
|
+
"""Reads and imports data from TopSpin."""
|
|
115
|
+
self._setup_correct_topspin_importer()
|
|
116
|
+
self.importer.import_topspin_data()
|
|
117
|
+
|
|
118
|
+
def _setup_correct_topspin_importer(self):
|
|
119
|
+
"""Sets up the appropriate TopSpin importer based on experiment properties."""
|
|
120
|
+
if len(self.props.expno) == 1:
|
|
121
|
+
self.importer = io.Pseudo2DImporter(self)
|
|
122
|
+
else:
|
|
123
|
+
self.importer = io.ScreamImporter(self)
|
|
124
|
+
|
|
125
|
+
def _print_all(self):
|
|
126
|
+
"""Prints all results using an exporter."""
|
|
127
|
+
exporter = io.Exporter(self)
|
|
128
|
+
exporter.print()
|
|
129
|
+
|
|
130
|
+
def _read_in_data_from_csv(self):
|
|
131
|
+
"""Placeholder function for reading data from CSV files."""
|
|
132
|
+
|
|
133
|
+
def _calculate_peak_intensities(self):
|
|
134
|
+
"""Calculates peak intensities based on fitting methods."""
|
|
135
|
+
if self.props.prefit:
|
|
136
|
+
print(
|
|
137
|
+
f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}: Start prefit."
|
|
138
|
+
)
|
|
139
|
+
self._set_prefitter()
|
|
140
|
+
result = self.fitter.fit()
|
|
141
|
+
self.lmfit_result_handler.prefit = result
|
|
142
|
+
self._update_line_broadening(result)
|
|
143
|
+
if "individual" == self.props.spectrum_fit_type:
|
|
144
|
+
print(
|
|
145
|
+
f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}: Start individual fit."
|
|
146
|
+
)
|
|
147
|
+
self._set_single_fitter()
|
|
148
|
+
result = self.fitter.fit()
|
|
149
|
+
self.lmfit_result_handler.global_fit = result
|
|
150
|
+
self._get_intensities(result)
|
|
151
|
+
if "global" == self.props.spectrum_fit_type:
|
|
152
|
+
print(
|
|
153
|
+
f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}: Start global fit."
|
|
154
|
+
)
|
|
155
|
+
self._set_global_fitter()
|
|
156
|
+
result = self.fitter.fit()
|
|
157
|
+
self.lmfit_result_handler.global_fit = result
|
|
158
|
+
self._get_intensities(result)
|
|
159
|
+
|
|
160
|
+
def _start_buildup_fit(self):
|
|
161
|
+
"""Performs buildup fitting using the appropriate fitter classes."""
|
|
162
|
+
fitter_classes = {
|
|
163
|
+
"biexponential": utils.BiexpFitter,
|
|
164
|
+
"biexponential_with_offset": utils.BiexpFitterWithOffset,
|
|
165
|
+
"exponential": utils.ExpFitter,
|
|
166
|
+
"exponential_with_offset": utils.ExpFitterWithOffset,
|
|
167
|
+
"streched_exponential": utils.StrechedExponentialFitter,
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
for b_type in self.props.buildup_types:
|
|
171
|
+
fitter_class = fitter_classes.get(b_type)
|
|
172
|
+
if fitter_class:
|
|
173
|
+
fitter = fitter_class(self)
|
|
174
|
+
self.lmfit_result_handler.buildup_fit[b_type] = (
|
|
175
|
+
fitter.perform_fit()
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
def _set_prefitter(self):
|
|
179
|
+
"""Sets up a pre-fitter."""
|
|
180
|
+
self.fitter = utils.Prefitter(self)
|
|
181
|
+
|
|
182
|
+
def _set_single_fitter(self):
|
|
183
|
+
"""Sets up a single-spectrum fitter."""
|
|
184
|
+
self.fitter = utils.IndependentFitter(self)
|
|
185
|
+
|
|
186
|
+
def _set_global_fitter(self):
|
|
187
|
+
"""Sets up a global fitter for all spectra."""
|
|
188
|
+
self.fitter = utils.GlobalFitter(self)
|
|
189
|
+
|
|
190
|
+
def _get_intensities(self, result):
|
|
191
|
+
"""Extracts intensity values from the fitting results."""
|
|
192
|
+
if isinstance(
|
|
193
|
+
self.fitter, (utils.IndependentFitter, utils.GlobalFitter)
|
|
194
|
+
):
|
|
195
|
+
for peak in self.peak_list:
|
|
196
|
+
peak.buildup_vals = (result, self.spectra)
|
|
197
|
+
|
|
198
|
+
def _update_line_broadening(self, result):
|
|
199
|
+
"""Updates line broadening values based on fitting results."""
|
|
200
|
+
for peak in self.peak_list:
|
|
201
|
+
peak.line_broadening = {
|
|
202
|
+
lw: {
|
|
203
|
+
"min": result.params.get(
|
|
204
|
+
f"{peak.peak_label}_{lw}_0"
|
|
205
|
+
).value
|
|
206
|
+
* 0.9,
|
|
207
|
+
"max": result.params.get(
|
|
208
|
+
f"{peak.peak_label}_{lw}_0"
|
|
209
|
+
).value
|
|
210
|
+
* 1.1,
|
|
211
|
+
}
|
|
212
|
+
for lw in ["sigma", "gamma"]
|
|
213
|
+
if f"{peak.peak_label}_{lw}_0" in result.params
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class Spectra:
|
|
218
|
+
"""
|
|
219
|
+
Represents spectral data for NMR (Nuclear Magnetic Resonance) experiments.
|
|
220
|
+
|
|
221
|
+
Attributes
|
|
222
|
+
----------
|
|
223
|
+
number_of_scans : list of int or None
|
|
224
|
+
The number of scans performed in the NMR experiment.
|
|
225
|
+
tpol : float or None
|
|
226
|
+
The polarization time used in the experiment.
|
|
227
|
+
x_axis : array-like or None
|
|
228
|
+
The x-axis values representing frequency domain data data.
|
|
229
|
+
y_axis : array-like or None
|
|
230
|
+
The y-axis values representing intensity or amplitude data.
|
|
231
|
+
|
|
232
|
+
"""
|
|
233
|
+
|
|
234
|
+
def __init__(self):
|
|
235
|
+
"""Initializes Spectra attributes."""
|
|
236
|
+
self.number_of_scans = None
|
|
237
|
+
self.tpol = None
|
|
238
|
+
self.x_axis = None
|
|
239
|
+
self.y_axis = None
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
class Peak:
|
|
243
|
+
"""
|
|
244
|
+
Represents a peak with its properties.
|
|
245
|
+
|
|
246
|
+
Such as peak center, peak label, fitting group, fitting type,
|
|
247
|
+
peak sign, line broadening, and buildup values.
|
|
248
|
+
|
|
249
|
+
"""
|
|
250
|
+
|
|
251
|
+
def __init__(self):
|
|
252
|
+
"""Initializes a Peak object with default values set to None."""
|
|
253
|
+
self._peak_center = None
|
|
254
|
+
self._peak_label = None
|
|
255
|
+
self._fitting_type = None
|
|
256
|
+
self._peak_sign = None
|
|
257
|
+
self._line_broadening = None
|
|
258
|
+
self._line_broadening_init = None
|
|
259
|
+
self._buildup_vals = None
|
|
260
|
+
|
|
261
|
+
def __str__(self):
|
|
262
|
+
"""
|
|
263
|
+
Returns a formatted string representation of the peak.
|
|
264
|
+
|
|
265
|
+
:return: A string describing the peak's attributes.
|
|
266
|
+
"""
|
|
267
|
+
return (
|
|
268
|
+
f"Peak center: {self.peak_center}\n"
|
|
269
|
+
f"Peak label: {self.peak_label}\n"
|
|
270
|
+
f"Peak shape: {self.fitting_type}\n"
|
|
271
|
+
f"Peak sign: {self.peak_sign}\n"
|
|
272
|
+
f"During the prefit stage, variables are permitted to vary within "
|
|
273
|
+
f"the following ranges:\n"
|
|
274
|
+
f" {self._format_fitting_range('prefit')}"
|
|
275
|
+
f"During the main analysis stage, variables are permitted to vary "
|
|
276
|
+
f"within the following ranges:\n"
|
|
277
|
+
f" {self._format_fitting_range('')}"
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
def _format_fitting_range(self, fit_type):
|
|
281
|
+
a_max = "0 and inf" if self.peak_sign == "+" else "-inf and 0"
|
|
282
|
+
lb = ""
|
|
283
|
+
line_broadening = (
|
|
284
|
+
self._line_broadening_init
|
|
285
|
+
if fit_type == "prefit"
|
|
286
|
+
else self._line_broadening
|
|
287
|
+
)
|
|
288
|
+
for keys in line_broadening:
|
|
289
|
+
lb += (
|
|
290
|
+
f"\t{keys}:\t\t\tBetween {line_broadening[keys]['min']} ppm"
|
|
291
|
+
f" and {line_broadening[keys]['max']} ppm.\n"
|
|
292
|
+
)
|
|
293
|
+
return (
|
|
294
|
+
f"\tCenter (µ):\t\tBetween {self.peak_center-1} ppm and"
|
|
295
|
+
f" {self.peak_center+1} ppm.\n"
|
|
296
|
+
f"\tAmplitude (A):\tBetween {a_max}.\n"
|
|
297
|
+
f"{lb}"
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
@property
|
|
301
|
+
def buildup_vals(self) -> list:
|
|
302
|
+
"""
|
|
303
|
+
Gets the buildup values.
|
|
304
|
+
|
|
305
|
+
:return: A list of buildup values.
|
|
306
|
+
"""
|
|
307
|
+
return self._buildup_vals
|
|
308
|
+
|
|
309
|
+
@buildup_vals.setter
|
|
310
|
+
def buildup_vals(self, args):
|
|
311
|
+
"""
|
|
312
|
+
Sets the buildup values.
|
|
313
|
+
|
|
314
|
+
:param args: Tuple containing result and spectra.
|
|
315
|
+
"""
|
|
316
|
+
result, spectra = args
|
|
317
|
+
self._buildup_vals = BuildupList()
|
|
318
|
+
self._buildup_vals.set_vals(result, spectra, self.peak_label)
|
|
319
|
+
|
|
320
|
+
@property
|
|
321
|
+
def line_broadening(self) -> str:
|
|
322
|
+
"""
|
|
323
|
+
Gets the line broadening parameters.
|
|
324
|
+
|
|
325
|
+
:return: A dictionary representing line broadening values.
|
|
326
|
+
"""
|
|
327
|
+
return self._line_broadening
|
|
328
|
+
|
|
329
|
+
@line_broadening.setter
|
|
330
|
+
def line_broadening(self, value):
|
|
331
|
+
"""
|
|
332
|
+
Sets the line broadening parameters after validation.
|
|
333
|
+
|
|
334
|
+
:param value: Dictionary containing line broadening parameters.
|
|
335
|
+
"""
|
|
336
|
+
allowed_values = ["sigma", "gamma"]
|
|
337
|
+
inner_allowed_values = ["min", "max"]
|
|
338
|
+
self._check_if_value_is_dict(value)
|
|
339
|
+
self._check_for_invalid_keys(value, allowed_values)
|
|
340
|
+
self._check_for_invalid_dict(value)
|
|
341
|
+
self._check_for_invalid_inner_keys(value, inner_allowed_values)
|
|
342
|
+
params = self._set_init_params()
|
|
343
|
+
self._overwrite_init_params(
|
|
344
|
+
value, allowed_values, inner_allowed_values, params
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
self._line_broadening = params
|
|
348
|
+
if self._line_broadening_init is None:
|
|
349
|
+
self._line_broadening_init = params
|
|
350
|
+
|
|
351
|
+
@property
|
|
352
|
+
def peak_sign(self) -> str:
|
|
353
|
+
"""
|
|
354
|
+
Gets the peak sign.
|
|
355
|
+
|
|
356
|
+
:return: The peak sign ('+' or '-').
|
|
357
|
+
"""
|
|
358
|
+
return self._peak_sign
|
|
359
|
+
|
|
360
|
+
@peak_sign.setter
|
|
361
|
+
def peak_sign(self, value):
|
|
362
|
+
"""
|
|
363
|
+
Sets the peak sign after validation.
|
|
364
|
+
|
|
365
|
+
:param value: A string representing the peak sign ('+' or '-').
|
|
366
|
+
"""
|
|
367
|
+
allowed_values = {"+", "-"}
|
|
368
|
+
if not isinstance(value, str):
|
|
369
|
+
raise TypeError(
|
|
370
|
+
f"'peak_sign' must be of type 'str', but got {type(value)}."
|
|
371
|
+
)
|
|
372
|
+
if value not in allowed_values:
|
|
373
|
+
raise ValueError(
|
|
374
|
+
f"'peak_sign' must be one of {sorted(allowed_values)}."
|
|
375
|
+
)
|
|
376
|
+
self._peak_sign = value
|
|
377
|
+
|
|
378
|
+
@property
|
|
379
|
+
def fitting_type(self) -> str:
|
|
380
|
+
"""
|
|
381
|
+
Gets the peak fitting type.
|
|
382
|
+
|
|
383
|
+
:return: The fitting type as a string.
|
|
384
|
+
"""
|
|
385
|
+
return self._fitting_type
|
|
386
|
+
|
|
387
|
+
@fitting_type.setter
|
|
388
|
+
def fitting_type(self, value):
|
|
389
|
+
"""
|
|
390
|
+
Sets the peak fitting type after validation.
|
|
391
|
+
|
|
392
|
+
:param value: A string representing the fitting type.
|
|
393
|
+
"""
|
|
394
|
+
allowed_values = {"voigt", "lorentz", "gauss"}
|
|
395
|
+
if not isinstance(value, str):
|
|
396
|
+
raise TypeError(
|
|
397
|
+
f"'fitting_type' must be of type 'str', but got {type(value)}."
|
|
398
|
+
)
|
|
399
|
+
if value not in allowed_values:
|
|
400
|
+
raise ValueError(
|
|
401
|
+
f"'fitting_type' must be one of {sorted(allowed_values)}."
|
|
402
|
+
)
|
|
403
|
+
self._fitting_type = value
|
|
404
|
+
|
|
405
|
+
@property
|
|
406
|
+
def peak_center(self) -> (int, float):
|
|
407
|
+
"""
|
|
408
|
+
Gets the peak center.
|
|
409
|
+
|
|
410
|
+
:return: The peak center as an integer or float.
|
|
411
|
+
"""
|
|
412
|
+
return self._peak_center
|
|
413
|
+
|
|
414
|
+
@peak_center.setter
|
|
415
|
+
def peak_center(self, value):
|
|
416
|
+
"""
|
|
417
|
+
Sets the peak center after validation.
|
|
418
|
+
|
|
419
|
+
:param value: An integer or float representing the peak center.
|
|
420
|
+
"""
|
|
421
|
+
if not isinstance(value, (int, float)):
|
|
422
|
+
raise TypeError(
|
|
423
|
+
f"'peak_center' must be of type 'int' or 'float', but got {type(value)}."
|
|
424
|
+
)
|
|
425
|
+
self._peak_center = float(value)
|
|
426
|
+
|
|
427
|
+
@property
|
|
428
|
+
def peak_label(self) -> str:
|
|
429
|
+
"""
|
|
430
|
+
Get or set the label for a peak.
|
|
431
|
+
|
|
432
|
+
Returns
|
|
433
|
+
-------
|
|
434
|
+
str: The current peak label.
|
|
435
|
+
|
|
436
|
+
"""
|
|
437
|
+
return self._peak_label
|
|
438
|
+
|
|
439
|
+
@peak_label.setter
|
|
440
|
+
def peak_label(self, value):
|
|
441
|
+
"""
|
|
442
|
+
Set the label for a peak.
|
|
443
|
+
|
|
444
|
+
If an empty string is provided, a default label in the format
|
|
445
|
+
'Peak_at_<center>_ppm' is generated, where <center> is the integer
|
|
446
|
+
value of `self.peak_center` (with '-' replaced by 'm').
|
|
447
|
+
|
|
448
|
+
Args
|
|
449
|
+
----
|
|
450
|
+
value (str): The label to assign to the peak.
|
|
451
|
+
|
|
452
|
+
Raises
|
|
453
|
+
------
|
|
454
|
+
TypeError: If the provided value is not a string.
|
|
455
|
+
|
|
456
|
+
"""
|
|
457
|
+
if not isinstance(value, str):
|
|
458
|
+
raise TypeError(
|
|
459
|
+
f"'peak_label' must be of type 'str', but got {type(value)}."
|
|
460
|
+
)
|
|
461
|
+
if value == "":
|
|
462
|
+
name = str(int(self.peak_center)).replace("-", "m")
|
|
463
|
+
value = f"Peak_at_{name}_ppm"
|
|
464
|
+
self._peak_label = value
|
|
465
|
+
|
|
466
|
+
def _return_default_dict(self):
|
|
467
|
+
"""
|
|
468
|
+
Returns the default dictionary for line broadening values.
|
|
469
|
+
|
|
470
|
+
:return: A dictionary with default values for 'sigma' and 'gamma'.
|
|
471
|
+
"""
|
|
472
|
+
return {
|
|
473
|
+
"sigma": {"min": 0, "max": 3},
|
|
474
|
+
"gamma": {"min": 0, "max": 3},
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
def _check_if_value_is_dict(self, value):
|
|
478
|
+
"""
|
|
479
|
+
Checks if the given value is a dictionary.
|
|
480
|
+
|
|
481
|
+
:param value: The value to check.
|
|
482
|
+
"""
|
|
483
|
+
if not isinstance(value, dict):
|
|
484
|
+
raise TypeError(
|
|
485
|
+
f"'line_broadening' must be a 'dict', but got {type(value)}."
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
def _check_for_invalid_keys(self, value, allowed_values):
|
|
489
|
+
invalid_keys = [
|
|
490
|
+
key for key in value.keys() if key not in allowed_values
|
|
491
|
+
]
|
|
492
|
+
if invalid_keys:
|
|
493
|
+
raise ValueError(
|
|
494
|
+
f"Invalid keys found in the dictionary: {invalid_keys}. "
|
|
495
|
+
f"Allowed keys are: {allowed_values}."
|
|
496
|
+
)
|
|
497
|
+
|
|
498
|
+
def _check_for_invalid_dict(self, value):
|
|
499
|
+
if not all(isinstance(v, dict) for v in value.values()):
|
|
500
|
+
raise TypeError(
|
|
501
|
+
"Each value in the 'line_broadening' dictionary must be of type 'dict'."
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
def _check_for_invalid_inner_keys(self, value, inner_allowed_values):
|
|
505
|
+
for key, inner_dict in value.items():
|
|
506
|
+
invalid_inner_keys = [
|
|
507
|
+
inner_key
|
|
508
|
+
for inner_key in inner_dict.keys()
|
|
509
|
+
if inner_key not in inner_allowed_values
|
|
510
|
+
]
|
|
511
|
+
if invalid_inner_keys:
|
|
512
|
+
raise ValueError(
|
|
513
|
+
f"Invalid inner keys for '{key}': {invalid_inner_keys}. "
|
|
514
|
+
f"Allowed inner keys are: {inner_allowed_values}."
|
|
515
|
+
)
|
|
516
|
+
|
|
517
|
+
def _set_init_params(self):
|
|
518
|
+
"""
|
|
519
|
+
Sets initial parameters based on fitting type.
|
|
520
|
+
|
|
521
|
+
:return: A dictionary of initial parameters.
|
|
522
|
+
"""
|
|
523
|
+
params = self._return_default_dict()
|
|
524
|
+
if self.fitting_type == "gauss":
|
|
525
|
+
params = {"sigma": params["sigma"]}
|
|
526
|
+
elif self.fitting_type == "lorentz":
|
|
527
|
+
params = {"gamma": params["gamma"]}
|
|
528
|
+
return params
|
|
529
|
+
|
|
530
|
+
def _overwrite_init_params(
|
|
531
|
+
self, value, allowed_values, inner_allowed_values, params
|
|
532
|
+
):
|
|
533
|
+
"""
|
|
534
|
+
Overwrites initial parameters with provided values.
|
|
535
|
+
|
|
536
|
+
:param value: Dictionary containing new parameter values.
|
|
537
|
+
:param allowed_values: List of allowed outer dictionary keys.
|
|
538
|
+
:param inner_allowed_values: List of allowed inner dictionary keys.
|
|
539
|
+
:param params: Dictionary of existing parameters to be updated.
|
|
540
|
+
|
|
541
|
+
"""
|
|
542
|
+
for key in allowed_values:
|
|
543
|
+
if key in value:
|
|
544
|
+
for inner_key in inner_allowed_values:
|
|
545
|
+
inner_value = value[key].get(inner_key)
|
|
546
|
+
if inner_value is not None:
|
|
547
|
+
if not isinstance(inner_value, (int, float)):
|
|
548
|
+
raise TypeError(
|
|
549
|
+
f"'{inner_key}' value must be an 'int' or 'float', "
|
|
550
|
+
f"but got {type(inner_value)}."
|
|
551
|
+
)
|
|
552
|
+
params[key][inner_key] = float(inner_value)
|
|
553
|
+
return params
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
class BuildupList:
|
|
557
|
+
"""
|
|
558
|
+
Represents a list of buildup values used for fitting delay times and intensities.
|
|
559
|
+
|
|
560
|
+
Attributes
|
|
561
|
+
----------
|
|
562
|
+
tpol (list): List of delay times.
|
|
563
|
+
intensity (list): List of intensity values.
|
|
564
|
+
|
|
565
|
+
"""
|
|
566
|
+
|
|
567
|
+
def __init__(self):
|
|
568
|
+
"""Initializes an empty BuildupList with None values for attributes."""
|
|
569
|
+
self.tpol = None
|
|
570
|
+
self.intensity = None
|
|
571
|
+
|
|
572
|
+
def __str__(self):
|
|
573
|
+
"""
|
|
574
|
+
Returns a formatted string representation of the buildup values.
|
|
575
|
+
|
|
576
|
+
Returns
|
|
577
|
+
-------
|
|
578
|
+
str: A formatted string listing delay times and intensities.
|
|
579
|
+
|
|
580
|
+
"""
|
|
581
|
+
return (
|
|
582
|
+
"Parameters for buildup fitting:\nDelay times:\t"
|
|
583
|
+
+ "\t\t\t".join(str(x) for x in self.tpol)
|
|
584
|
+
+ "\nIntegral:\t"
|
|
585
|
+
+ "\t".join(str(x) for x in self.intensity)
|
|
586
|
+
)
|
|
587
|
+
|
|
588
|
+
def set_vals(self, result, spectra, label):
|
|
589
|
+
"""
|
|
590
|
+
Sets buildup values using the result parameters and spectra.
|
|
591
|
+
|
|
592
|
+
Attributes
|
|
593
|
+
----------
|
|
594
|
+
result (object): Fitted parameter values used for calculating buildup.
|
|
595
|
+
spectra (list): Spectrum objects used with result to compute buildup.
|
|
596
|
+
label (str): Peak label used to filter relevant parameters in result.
|
|
597
|
+
|
|
598
|
+
"""
|
|
599
|
+
self._set_tpol(spectra)
|
|
600
|
+
self._set_intensity(result, label, spectra)
|
|
601
|
+
self._sort_lists()
|
|
602
|
+
|
|
603
|
+
def _set_tpol(self, spectra):
|
|
604
|
+
self.tpol = [s.tpol for s in spectra]
|
|
605
|
+
|
|
606
|
+
def _set_intensity(self, result, label, spectra):
|
|
607
|
+
last_digid = None
|
|
608
|
+
self.intensity = []
|
|
609
|
+
val_list = []
|
|
610
|
+
for param in result.params:
|
|
611
|
+
if label in param:
|
|
612
|
+
if last_digid != param.split("_")[-1]:
|
|
613
|
+
if val_list:
|
|
614
|
+
self.intensity.append(
|
|
615
|
+
self._calc_integral(
|
|
616
|
+
val_list, spectra[int(last_digid)]
|
|
617
|
+
)
|
|
618
|
+
)
|
|
619
|
+
last_digid = param.split("_")[-1]
|
|
620
|
+
val_list = []
|
|
621
|
+
val_list.append(float(result.params[param].value))
|
|
622
|
+
if param.split("_")[-2] == "gamma":
|
|
623
|
+
val_list.append("gamma")
|
|
624
|
+
self.intensity.append(
|
|
625
|
+
self._calc_integral(val_list, spectra[int(last_digid)])
|
|
626
|
+
)
|
|
627
|
+
|
|
628
|
+
def _calc_integral(self, val_list, spectrum):
|
|
629
|
+
"""
|
|
630
|
+
Computes the numerical integral of the simulated spectrum.
|
|
631
|
+
|
|
632
|
+
Args
|
|
633
|
+
----
|
|
634
|
+
val_list (list): List of values used for peak calculation.
|
|
635
|
+
spectrum (object): Spectrum object containing x-axis data.
|
|
636
|
+
|
|
637
|
+
Returns
|
|
638
|
+
-------
|
|
639
|
+
float: The computed integral of the spectrum.
|
|
640
|
+
|
|
641
|
+
"""
|
|
642
|
+
simspec = [0 for _ in range(len(spectrum.x_axis))]
|
|
643
|
+
simspec = functions.calc_peak(spectrum.x_axis, simspec, val_list)
|
|
644
|
+
return np.trapz(simspec)
|
|
645
|
+
|
|
646
|
+
def _sort_lists(self):
|
|
647
|
+
"""
|
|
648
|
+
Sorting method.
|
|
649
|
+
|
|
650
|
+
Sorts the delay times and corresponding intensity values in
|
|
651
|
+
ascending order of delay times.
|
|
652
|
+
"""
|
|
653
|
+
self.tpol, self.intensity = map(
|
|
654
|
+
list, zip(*sorted(zip(self.tpol, self.intensity)))
|
|
655
|
+
)
|