Skip to content

Commit 86f86f1

Browse files
authored
Merge pull request #1 from davistdaniel/feature/workflows
Feature/workflows
2 parents ebf89ab + 9ad9ca4 commit 86f86f1

File tree

16 files changed

+1281
-179
lines changed

16 files changed

+1281
-179
lines changed

CHANGELOG

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
v0.9.0b1
2+
3+
* Added Workflows for HYSCORE, ESEEM experiments
4+
* Updated tests in accordance with change of the parameter default for `npts`.
5+
* Updated docs with Workflow examples
6+
* Other minor updates to documentation
7+
* Logo adapted also for dark backgrounds.
8+
* Added exponential decay as a background correction function.
9+
* Added workflow method to EprData, detects experiment type from pulse program

README.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,10 @@ For EPRpy documentation, see [here](https://davistdaniel.github.io/EPRpy/). Sour
2525
## Features
2626

2727
* Read and export EPR data acquired on Bruker EPR spectrometers.
28-
* Basic processing capabilities such as [interactive baseline correction](https://davistdaniel.github.io/EPRpy/notebooks/examples.html#Baseline-correction), integration etc.
28+
* Data processing capabilities such as [interactive baseline correction](https://davistdaniel.github.io/EPRpy/notebooks/examples.html#Baseline-correction), integration etc.
2929
* [Interactive data inspection](https://davistdaniel.github.io/EPRpy/plotting.html#interactive-plots) for 1D and 2D datasets.
3030
* Generate quick plots of 1D and 2D datasets, compare different datasets.
31-
32-
## Upcoming
33-
* Automated workflow templates for specific EPR experiments
31+
* [Automated processing pipelines](https://davistdaniel.github.io/EPRpy/notebooks/examples.html#Workflows) for specific pulse programs. Read more about [workflows](https://davistdaniel.github.io/EPRpy/workflows.html).
3432

3533
## Limitations
3634
* Supports reading of files only in Bruker BES3T format v.1.2 and upto 2D datasets.

docs/source/Examples.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ Examples
33

44

55
.. toctree::
6-
:maxdepth: 2
6+
:maxdepth: 3
77
:caption: Contents:
88

99
notebooks/examples.ipynb

docs/source/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
project = 'EPRpy'
1010
copyright = '2024, Davis Thomas Daniel'
1111
author = 'Davis Thomas Daniel'
12-
release = '0.9.0a8'
12+
release = '0.9.0b1'
1313

1414
# -- General configuration ---------------------------------------------------
1515
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration

docs/source/images/eprpy_logo.png

-43.5 KB
Loading

docs/source/index.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Welcome to **EPRpy**'s documentation
1010
:width: 400px
1111
:align: center
1212

13+
1314
**EPRpy** is a Python library designed to streamline the handling, inspection, and processing of Electron Paramagnetic Resonance (EPR) spectroscopic data acquired on Bruker EPR spectrometers.
1415
**EPRpy** focusses on ease of use, enabling quick data visualization, data comparisons, and having transperent as well as highly customisable control over data analysis.
1516

@@ -49,6 +50,7 @@ Using EPRpy
4950
loading
5051
processing
5152
plotting
53+
workflows
5254
EprData
5355
Examples
5456

docs/source/notebooks/examples.ipynb

Lines changed: 375 additions & 72 deletions
Large diffs are not rendered by default.

docs/source/plotting.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ For complex data, imaginary part can be be visualized by setting ``plot_imag`` t
3131
.. image:: images/plt_with_imag.png
3232
:width: 400px
3333

34-
See more `plotting examples <notebooks/examples.html#Plotting>`_ for 1D data.
34+
See more plotting `examples <notebooks/examples.html#Plotting>`_ for 1D data.
3535

3636

3737
2D data can be visualized similarly and has a selection of plot types as shown below.

docs/source/workflows.rst

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
Workflows
2+
===============
3+
4+
The ``workflow`` module in **EPRpy** provides high-level processing pipelines for
5+
common pulsed-EPR experiments. It is designed to take an ``EprData`` object as input
6+
and return a fully processed ``EprData`` object, with all intermediate results and
7+
processing steps preserved for reproducibility in the ``data_dict`` method.
8+
9+
Currently, the module supports workflows for:
10+
11+
* **HYSCORE** (Hyperfine Sublevel Correlation) experiments
12+
* **ESEEM** (Electron Spin Echo Envelope Modulation) experiments
13+
14+
These workflows include baseline correction, windowing, zero-filling, Fourier
15+
transformation, and frequency axis generation. Optionally, symmetrisation of HYSCORE
16+
spectra and background fitting strategies for ESEEM are provided.
17+
18+
A workflow can be simply used by loading experimental data with a compatible pulse program :
19+
20+
.. code-block:: python
21+
22+
# import EPRpy
23+
import eprpy as epr
24+
25+
# Load data by providing path to .DSC or .DTA file
26+
hyscore_data = epr.load('hyscore_exp.DSC')
27+
28+
# process using a workflow, zero fill 1024 points
29+
hyscore_processed = hyscore_data.workflow(zf=1024)
30+
31+
32+
Have a look at `workflow examples <notebooks/examples.html#Workflows>`_.
33+
34+
Internally, the :ref:`eprworkflowclass` is used when ``EprData.workflow()`` is called. In the example above, ``hyscore_processed`` is an ``EprData`` object, so all methods of ``EprData`` object are also accessible.
35+
Other parameters which are supported for a workflow is explained in the section :ref:`eprworkflowclass`. In the following, the workflow processing steps are elaborated.
36+
37+
HYSCORE processing
38+
--------------------
39+
40+
The workflow supports pulse programs:
41+
- ``HYSCORE``
42+
43+
44+
HYSCORE datasets are procssed by by:
45+
- Applying 2D polynomial baseline correction.
46+
- Windowing with Hamming functions in both dimensions.
47+
- Zero-filling (optional).
48+
- Performing a 2D FFT and shifting the result.
49+
- Symmetrising the FFT data (optional).
50+
- Returning all results (raw, intermediate, final) in a dictionary.
51+
52+
The output contains frequency axes in MHz and processed spectra suitable for
53+
plotting and further analysis.
54+
55+
If you have a processed ``EprData`` object from a ``HYSCORE`` data set, the processed arrays can be accessed using ``EprData.data_dict``:
56+
57+
.. code-block:: python
58+
59+
- 'proc_param': Processing parameters used.
60+
- 'raw_data': Original raw data.
61+
- 'baseline_dim1', 'baseline_dim2': Baseline correction results for both dimensions.
62+
- 'bc_data': Baseline-corrected data.
63+
- 'window_dim1', 'window_dim2': Window functions applied.
64+
- 'bc_w_data': Baseline-corrected and windowed data.
65+
- 'time_axis1', 'time_axis2': Time axes (extended if zero-filled).
66+
- 'bc_w_zf_data': Zero-filled, baseline-corrected, windowed data.
67+
- 'frequency_axis1', 'frequency_axis2': Frequency axes for FFT.
68+
- 'FFT_data': 2D FFT of processed data.
69+
- 'FFT_shifted_data': FFT data after shift.
70+
- 'data': Absolute value of shifted FFT (final processed data, optionally symmeterised).
71+
- 'dims': Frequency axes for plotting.
72+
- 'is_complex': Boolean indicating if data is complex (always False).
73+
- 'history': List of processing steps.
74+
75+
76+
ESEEM processing
77+
--------------------
78+
79+
The workflow supports pulse programs:
80+
- ``2P ESEEM``
81+
- ``3P ESEEM``
82+
- ``2P ESEEM vs. B0``
83+
- ``3P ESEEM vs. B0``
84+
- ``3P ESEEM vs tau``
85+
86+
ESEEM datasets are processed by:
87+
- Applying background correction (exponential or polynomial).
88+
- Windowing with a Hamming function.
89+
- Zero-filling (optional).
90+
- Performing a 1D FFT (or along the last axis for 2D data).
91+
- Returning absolute, shifted FFT spectra along with all intermediate steps.
92+
93+
Results include the processed frequency-domain spectra in MHz, with
94+
dimensionality preserved for field-dependent ESEEM maps.
95+
96+
If you have a processed ``EprData`` object from a ``ESEEM`` data set, the processed arrays can be accessed using ``EprData.data_dict``:
97+
98+
.. code-block:: python
99+
100+
- 'proc_param': Processing parameters used.
101+
- 'raw_data': Original raw data.
102+
- 'baseline_dim1': Baseline correction results.
103+
- 'bc_data': Baseline-corrected data.
104+
- 'window_dim1': Window function applied.
105+
- 'bc_w_data': Baseline-corrected and windowed data.
106+
- 'time_axis1': Time axis (extended if zero-filled).
107+
- 'bc_w_zf_data': Zero-filled, baseline-corrected, windowed data.
108+
- 'frequency_axis1': Frequency axis for FFT.
109+
- 'FFT_data': FFT of processed data.
110+
- 'FFT_shifted_data': FFT data after shift.
111+
- 'data': Absolute value of shifted FFT (final processed data).
112+
- 'dims': Frequency axis (and second axis if 2D data).
113+
- 'is_complex': Boolean indicating if data is complex (always False).
114+
- 'history': List of processing steps.
115+
116+
117+
.. _eprworkflowclass:
118+
119+
EprWorkflow Class
120+
--------------------
121+
122+
The ``EprWorkflow`` class encapsulates complete workflows for HYSCORE and ESEEM
123+
data processing. It manages parameters like zero-filling, polynomial order for
124+
baseline correction, and optional symmetrisation. All results, including raw
125+
data, corrected data, intermediate steps, and frequency-domain spectra, are
126+
stored in a dictionary that is updated step by step.
127+
128+
**Parameters**
129+
- **eprdata** (*EprData*) – Instance of the EprData class.
130+
- **zf** (*int, optional*) – Number of points to add via zero filling.
131+
- **poly_order** (*int, optional, default=3*) – Order of the polynomial for baseline correction in case of 3P ESEEM experiments
132+
- **x_max** (*float, optional*) – Upper bound of the Hamming window.
133+
- **pick_eseem_points** (*bool, default=False*) – Whether to pick points along the ESEEM decay curve for background calculation. If False, all data points are used.
134+
- **symmeterise** (*bool or str, default=False*) – If set, symmetrises HYSCORE FFT results across diagonal or anti-diagonal.
135+
136+
Both ``hyscore()`` and ``eseem()`` return a **dictionary** containing raw data,
137+
intermediate results, processing parameters, and final spectra.
138+
Some keys are primarily useful for debugging, while others are essential for
139+
end-users who wish to analyse or visualise results.
140+
141+

eprpy/loader.py

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77

88
# EPRpy
99
from eprpy.plotter import eprplot
10-
from eprpy.processor import _integrate,_scale_between,_baseline_correct
10+
from eprpy.processor import _integrate,_scale_between,_baseline_correct,_derivative
11+
from eprpy.workflows import EprWorkflow
1112

13+
warnings.simplefilter("always")
1214

1315
def load(filepath):
1416

@@ -38,7 +40,8 @@ def load(filepath):
3840

3941
out_dict = {'dims':None,
4042
'data':None,
41-
'acq_param':None}
43+
'acq_param':None,
44+
'workflow_type':None}
4245

4346
dta_filepath,dsc_filepath = check_filepaths(filepath)
4447
dsc_parameter_dict = read_DSC_file(dsc_filepath)
@@ -411,7 +414,17 @@ def __init__(self,out_dict):
411414
self.g = ((float(self.acq_param['MWFQ'])/1e+9)/(13.996*(x_g/10000)))
412415
else:
413416
self.g = None
414-
self.history[0].append(deepcopy(self))
417+
self.workflow_type = out_dict['workflow_type']
418+
self.history[-1].append(deepcopy(self))
419+
420+
try:
421+
self.pulse_program = self.acq_param["PlsSPELEXPSlct"]
422+
except Exception as e:
423+
print(
424+
"Error ocurred while reading pulse program parameter PlsSPELEXPSlct from DSC file : ",
425+
e,
426+
)
427+
self.pulse_program = "Unknown"
415428

416429

417430
def plot(self,g_scale=False,plot_type='stacked', slices='all', spacing=0.5,plot_imag=True,interactive=False):
@@ -429,24 +442,38 @@ def integral(self):
429442
eprdata_proc = _integrate(self)
430443
return eprdata_proc
431444

432-
def baseline_correct(self,interactive=False,
433-
npts=10,method='linear',spline_smooth=1e-5,
434-
order=2):
445+
def baseline_correct(self,interactive=False, npts=0, method="linear", spline_smooth=1e-5, order=2,init_vals=None,bounds = (-np.inf, np.inf),fit_eseem_max=False):
435446

436-
eprdata_proc = _baseline_correct(self,interactive,
437-
npts,method,spline_smooth,order)
447+
eprdata_proc = _baseline_correct(self,interactive, npts, method, spline_smooth, order,init_vals,bounds,fit_eseem_max)
438448
return eprdata_proc
439449

440450

441451
def select_region(self,region):
442452

443-
444453
assert type(region) in [range,list],'region keyword must be a range object or list.'
445454
out_dict = deepcopy(self.data_dict)
446455
out_dict['dims'][-1] = out_dict['dims'][-1][region]
447456
out_dict['data'] = out_dict['data'][...,region]
448457

449458
return EprData(out_dict)
459+
460+
def derivative(self,sigma=1,axis=-1):
461+
462+
epr_data_proc = _derivative(self,sigma,axis)
463+
return epr_data_proc
464+
465+
def workflow(self,zf=0,poly_order=3,x_max=None,pick_eseem_points=False,symmetrise=False,verbose=False):
450466

467+
if self.pulse_program == "HYSCORE":
468+
hyscore_out_dict = EprWorkflow(eprdata=self,zf=zf,poly_order=poly_order,x_max=x_max,pick_eseem_points=pick_eseem_points,symmetrise=symmetrise,verbose=verbose).hyscore()
469+
return EprData(hyscore_out_dict)
451470

471+
elif self.pulse_program in ["2P ESEEM", "3P ESEEM","2P ESEEM vs. B0","3P ESEEM vs. B0","3P ESEEM vs tau"]:
472+
eseem_out_dict = EprWorkflow(eprdata=self,zf=zf,poly_order=poly_order,x_max=x_max,pick_eseem_points=pick_eseem_points,verbose=verbose).eseem()
473+
return EprData(eseem_out_dict)
452474

475+
else:
476+
raise ValueError(f"No supported workflows found for the pulse program : {self.pulse_program}")
477+
478+
479+

0 commit comments

Comments
 (0)