screamlab 0.1.0__tar.gz → 0.3.1__tar.gz

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 (48) hide show
  1. {screamlab-0.1.0/screamlab.egg-info → screamlab-0.3.1}/PKG-INFO +2 -2
  2. screamlab-0.3.1/VERSION +1 -0
  3. screamlab-0.3.1/screamlab/__pycache__/dataset.cpython-310.pyc +0 -0
  4. screamlab-0.3.1/screamlab/__pycache__/dataset.cpython-313.pyc +0 -0
  5. {screamlab-0.1.0 → screamlab-0.3.1}/screamlab/__pycache__/functions.cpython-310.pyc +0 -0
  6. screamlab-0.3.1/screamlab/__pycache__/functions.cpython-313.pyc +0 -0
  7. screamlab-0.3.1/screamlab/__pycache__/io.cpython-310.pyc +0 -0
  8. screamlab-0.3.1/screamlab/__pycache__/io.cpython-313.pyc +0 -0
  9. screamlab-0.3.1/screamlab/__pycache__/settings.cpython-310.pyc +0 -0
  10. {screamlab-0.1.0 → screamlab-0.3.1}/screamlab/__pycache__/settings.cpython-313.pyc +0 -0
  11. {screamlab-0.1.0 → screamlab-0.3.1}/screamlab/__pycache__/utils.cpython-310.pyc +0 -0
  12. screamlab-0.3.1/screamlab/__pycache__/utils.cpython-313.pyc +0 -0
  13. {screamlab-0.1.0 → screamlab-0.3.1}/screamlab/dataset.py +145 -19
  14. {screamlab-0.1.0 → screamlab-0.3.1}/screamlab/functions.py +114 -4
  15. {screamlab-0.1.0 → screamlab-0.3.1}/screamlab/io.py +162 -44
  16. {screamlab-0.1.0 → screamlab-0.3.1}/screamlab/settings.py +18 -11
  17. {screamlab-0.1.0 → screamlab-0.3.1}/screamlab/utils.py +247 -6
  18. {screamlab-0.1.0 → screamlab-0.3.1/screamlab.egg-info}/PKG-INFO +2 -2
  19. {screamlab-0.1.0 → screamlab-0.3.1}/screamlab.egg-info/SOURCES.txt +4 -0
  20. {screamlab-0.1.0 → screamlab-0.3.1}/setup.py +1 -1
  21. {screamlab-0.1.0 → screamlab-0.3.1}/tests/test_exporter_with_real_data.py +4 -2
  22. screamlab-0.3.1/tests/test_exporter_with_real_data_pseudo_2D.py +92 -0
  23. {screamlab-0.1.0 → screamlab-0.3.1}/tests/test_utils.py +12 -9
  24. screamlab-0.1.0/VERSION +0 -1
  25. screamlab-0.1.0/screamlab/__pycache__/dataset.cpython-310.pyc +0 -0
  26. screamlab-0.1.0/screamlab/__pycache__/dataset.cpython-313.pyc +0 -0
  27. screamlab-0.1.0/screamlab/__pycache__/io.cpython-310.pyc +0 -0
  28. screamlab-0.1.0/screamlab/__pycache__/settings.cpython-310.pyc +0 -0
  29. {screamlab-0.1.0 → screamlab-0.3.1}/LICENSE +0 -0
  30. {screamlab-0.1.0 → screamlab-0.3.1}/MANIFEST.in +0 -0
  31. {screamlab-0.1.0 → screamlab-0.3.1}/README.rst +0 -0
  32. {screamlab-0.1.0 → screamlab-0.3.1}/screamlab/__init__.py +0 -0
  33. {screamlab-0.1.0 → screamlab-0.3.1}/screamlab/__pycache__/__init__.cpython-310.pyc +0 -0
  34. {screamlab-0.1.0 → screamlab-0.3.1}/screamlab/__pycache__/__init__.cpython-312.pyc +0 -0
  35. {screamlab-0.1.0 → screamlab-0.3.1}/screamlab/__pycache__/__init__.cpython-313.pyc +0 -0
  36. {screamlab-0.1.0 → screamlab-0.3.1}/screamlab/__pycache__/__init__.cpython-38.pyc +0 -0
  37. {screamlab-0.1.0 → screamlab-0.3.1}/screamlab/__pycache__/dataset.cpython-312.pyc +0 -0
  38. {screamlab-0.1.0 → screamlab-0.3.1}/screamlab/__pycache__/functions.cpython-312.pyc +0 -0
  39. {screamlab-0.1.0 → screamlab-0.3.1}/screamlab/__pycache__/io.cpython-312.pyc +0 -0
  40. {screamlab-0.1.0 → screamlab-0.3.1}/screamlab/__pycache__/settings.cpython-312.pyc +0 -0
  41. {screamlab-0.1.0 → screamlab-0.3.1}/screamlab/__pycache__/utils.cpython-312.pyc +0 -0
  42. {screamlab-0.1.0 → screamlab-0.3.1}/screamlab.egg-info/dependency_links.txt +0 -0
  43. {screamlab-0.1.0 → screamlab-0.3.1}/screamlab.egg-info/requires.txt +0 -0
  44. {screamlab-0.1.0 → screamlab-0.3.1}/screamlab.egg-info/top_level.txt +0 -0
  45. {screamlab-0.1.0 → screamlab-0.3.1}/setup.cfg +0 -0
  46. {screamlab-0.1.0 → screamlab-0.3.1}/tests/test_dataset.py +0 -0
  47. {screamlab-0.1.0 → screamlab-0.3.1}/tests/test_io.py +0 -0
  48. {screamlab-0.1.0 → screamlab-0.3.1}/tests/test_settings.py +0 -0
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: screamlab
3
- Version: 0.1.0
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://github.com/FlorianTaube/screamlab/docs
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 @@
1
+ 0.3.1
@@ -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: "gauss", "lorentz", or "voigt" (default).
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
- if "global" == self.props.spectrum_fit_type:
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
- "streched_exponential": utils.StrechedExponentialFitter,
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, (utils.IndependentFitter, utils.GlobalFitter)
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
- self._buildup_vals.set_vals(result, spectra, self.peak_label)
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
- 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)]
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
- 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")
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):
@@ -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 generate_spectra_param_dict(params):
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
- "streched_exponential": [
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
- "streched_exponential": calc_stretched_exponential,
506
+ "stretched_exponential": calc_stretched_exponential,
507
+ "exponential_decay": calc_expdecay,
508
+ "exponential_decay_with_offset": calc_expdecaywithoffset,
399
509
  }
400
510
 
401
511