scispectrum 0.3.0__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.
- scispectrum-0.3.0/LICENSE +21 -0
- scispectrum-0.3.0/PKG-INFO +220 -0
- scispectrum-0.3.0/README.md +193 -0
- scispectrum-0.3.0/pyproject.toml +36 -0
- scispectrum-0.3.0/scispectrum/__init__.py +4 -0
- scispectrum-0.3.0/scispectrum/background/__init__.py +4 -0
- scispectrum-0.3.0/scispectrum/background/als.py +81 -0
- scispectrum-0.3.0/scispectrum/background/base.py +31 -0
- scispectrum-0.3.0/scispectrum/background/minimum_in_envelope.py +125 -0
- scispectrum-0.3.0/scispectrum/background/polynomial.py +165 -0
- scispectrum-0.3.0/scispectrum/background/snip.py +65 -0
- scispectrum-0.3.0/scispectrum/background/snip_utils.py +10 -0
- scispectrum-0.3.0/scispectrum/calibration/__init__.py +7 -0
- scispectrum-0.3.0/scispectrum/calibration/axis.py +60 -0
- scispectrum-0.3.0/scispectrum/calibration/detector_calibration.py +158 -0
- scispectrum-0.3.0/scispectrum/calibration/models/__init__.py +3 -0
- scispectrum-0.3.0/scispectrum/calibration/models/base.py +31 -0
- scispectrum-0.3.0/scispectrum/calibration/models/energy_poly.py +25 -0
- scispectrum-0.3.0/scispectrum/calibration/models/hpge_fwhm_model.py +10 -0
- scispectrum-0.3.0/scispectrum/calibration/resolution.py +21 -0
- scispectrum-0.3.0/scispectrum/core/__init__.py +2 -0
- scispectrum-0.3.0/scispectrum/core/domain.py +198 -0
- scispectrum-0.3.0/scispectrum/core/spectrum.py +282 -0
- scispectrum-0.3.0/scispectrum/domain_analysis/__init__.py +0 -0
- scispectrum-0.3.0/scispectrum/domain_analysis/background.py +63 -0
- scispectrum-0.3.0/scispectrum/domain_analysis/find_peaks.py +208 -0
- scispectrum-0.3.0/scispectrum/domain_analysis/moment.py +74 -0
- scispectrum-0.3.0/scispectrum/domain_analysis/morphology.py +107 -0
- scispectrum-0.3.0/scispectrum/domain_analysis/single_peak.py +140 -0
- scispectrum-0.3.0/scispectrum/domain_fitting/__init__.py +3 -0
- scispectrum-0.3.0/scispectrum/domain_fitting/abstract_fitting_class.py +20 -0
- scispectrum-0.3.0/scispectrum/domain_fitting/multi_gaussian_fitter.py +410 -0
- scispectrum-0.3.0/scispectrum/identification/__init__.py +3 -0
- scispectrum-0.3.0/scispectrum/identification/base.py +18 -0
- scispectrum-0.3.0/scispectrum/identification/convolution.py +84 -0
- scispectrum-0.3.0/scispectrum/identification/kernels/__init__.py +1 -0
- scispectrum-0.3.0/scispectrum/identification/kernels/base.py +42 -0
- scispectrum-0.3.0/scispectrum/identification/kernels/mexican_hat.py +24 -0
- scispectrum-0.3.0/scispectrum/identification/kernels/utils.py +1 -0
- scispectrum-0.3.0/scispectrum/identification/snr.py +192 -0
- scispectrum-0.3.0/scispectrum/io/__init__.py +4 -0
- scispectrum-0.3.0/scispectrum/io/time_channel.py +175 -0
- scispectrum-0.3.0/scispectrum/utils/__init__.py +0 -0
- scispectrum-0.3.0/scispectrum/utils/gaussian.py +12 -0
- scispectrum-0.3.0/scispectrum/utils/smoothing.py +88 -0
- scispectrum-0.3.0/scispectrum.egg-info/PKG-INFO +220 -0
- scispectrum-0.3.0/scispectrum.egg-info/SOURCES.txt +50 -0
- scispectrum-0.3.0/scispectrum.egg-info/dependency_links.txt +1 -0
- scispectrum-0.3.0/scispectrum.egg-info/requires.txt +5 -0
- scispectrum-0.3.0/scispectrum.egg-info/top_level.txt +1 -0
- scispectrum-0.3.0/setup.cfg +4 -0
- scispectrum-0.3.0/tests/test_calibration.py +37 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Achiya Yosef Amrusi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: scispectrum
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: 1D detector spectrum analysis — energy calibration, background estimation, peak detection and fitting
|
|
5
|
+
Author-email: Achiya Yosef Amrusi <ahia.amrosi@mail.huji.ac.il>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/achiyaAmrusi/scispectrum
|
|
8
|
+
Project-URL: Repository, https://github.com/achiyaAmrusi/scispectrum
|
|
9
|
+
Project-URL: Issues, https://github.com/achiyaAmrusi/scispectrum/issues
|
|
10
|
+
Keywords: gamma-ray spectroscopy,peak fitting,background estimation,detector calibration,nuclear physics
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: numpy<3.0,>=2.0.0
|
|
22
|
+
Requires-Dist: pandas<4.0,>=2.3
|
|
23
|
+
Requires-Dist: scipy>=1.14.0
|
|
24
|
+
Requires-Dist: xarray>=2024.6.0
|
|
25
|
+
Requires-Dist: uncertainties>=3.1
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# scispectrum
|
|
29
|
+
|
|
30
|
+
A Python package for 1D spectrum analysis, designed for physicists and scientists working with detector data such as gamma-ray, X-ray, or particle spectra.
|
|
31
|
+
|
|
32
|
+
scispectrum provides a clean, extensible framework for the full spectrum analysis pipeline — from raw detector output to calibrated, fitted, background-subtracted results — with proper uncertainty propagation throughout.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Features
|
|
37
|
+
|
|
38
|
+
- **Spectrum object** with axis calibration, resolution calibration, and error propagation using the uncertainties package
|
|
39
|
+
- **Domain slicing** by physical axis values (e.g. energy in keV) or channels
|
|
40
|
+
- **List-mode parser** for time-channel detector data, with chunked reading for large files
|
|
41
|
+
- **Domain fitting and analysis** — multiple fitting and analysis methods for a domain including sum of Gaussians fitting, find peaks, centers and fwhm
|
|
42
|
+
- **Background estimation** — multiple global background extimation methods including Asymmetric Least Squares (ALS)
|
|
43
|
+
- **Extensible base classes** — build your own fitting, background, or analysis procedures with a consistent interface
|
|
44
|
+
- **xarray throughout** — labeled, sliceable data with named coordinates
|
|
45
|
+
- **Uncertainty propagation** via the `uncertainties` library
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Installation
|
|
50
|
+
|
|
51
|
+
scispectrum is not yet available on PyPI. Install directly from GitHub:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
git clone https://github.com/achiyaAmrusi/pySpectrum
|
|
55
|
+
cd pySpectrum
|
|
56
|
+
pip install -e .
|
|
57
|
+
```
|
|
58
|
+
This installs the package in editable mode, so any changes you make to the source are reflected immediately.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Quick Start
|
|
63
|
+
|
|
64
|
+
### Load a spectrum from a DataFrame
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
import pandas as pd
|
|
68
|
+
from scispectrum.core import Spectrum
|
|
69
|
+
|
|
70
|
+
df = pd.read_csv("my_spectrum.csv")
|
|
71
|
+
spectrum = Spectrum.from_dataframe(df, channel_col="channel", counts_col="counts")
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Apply an energy calibration
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from scispectrum.calibration import AxisCalibration
|
|
78
|
+
|
|
79
|
+
# Linear calibration: energy = 0.5 * channel + 1.2
|
|
80
|
+
calib = AxisCalibration(lambda ch: 0.5 * ch + 1.2, name="energy_keV")
|
|
81
|
+
spectrum.set_axis_calibration(calib)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Slice a domain by axis values
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
# Select the region between 1460 and 1480 keV
|
|
88
|
+
domain = spectrum.domain(1460, 1480)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Fit peaks
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from scispectrum.domain_fitting import SumOfGaussians
|
|
95
|
+
|
|
96
|
+
result = SumOfGaussians.fit(domain)
|
|
97
|
+
|
|
98
|
+
print(result["center"].values) # peak centers in keV
|
|
99
|
+
print(result["fwhm"].values) # peak widths
|
|
100
|
+
print(result["amplitude"].values) # peak amplitudes
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Estimate and subtract background
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
from scispectrum.background import ALSBackground
|
|
107
|
+
|
|
108
|
+
bg_estimator = ALSBackground(lam=1e5, p=0.001, max_iter=50)
|
|
109
|
+
als_bg = bg_estimator.estimate(spectrum.axis, spectrum.counts)
|
|
110
|
+
domain_subtracted = domains.subtract_background(als_bg[domain.indices])
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Parse raw list-mode data
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
from scispectrum.parsers import TimeChannelParser
|
|
117
|
+
|
|
118
|
+
# From a large file — processed in chunks to save memory
|
|
119
|
+
spectrum = TimeChannelParser.from_file(
|
|
120
|
+
"detector_run.csv",
|
|
121
|
+
axis_calib=calib,
|
|
122
|
+
num_of_channels=2**14
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# From an in-memory DataFrame
|
|
126
|
+
spectrum = TimeChannelParser.from_dataframe(df, axis_calib=calib)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Uncertainty Propagation
|
|
132
|
+
|
|
133
|
+
scispectrum propagates measurement uncertainties through arithmetic operations using the `uncertainties` library. Poisson errors are assigned automatically when parsing list-mode data.
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
# Arithmetic preserves errors
|
|
137
|
+
subtraction = spectrum_a - spectrum_b
|
|
138
|
+
subtraction.counts_err # propagated uncertainties
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Extending scispectrum
|
|
144
|
+
|
|
145
|
+
scispectrum is designed to be extended. Each analysis category has an abstract base class that defines the interface:
|
|
146
|
+
|
|
147
|
+
### Custom fitting method
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
from scispectrum.domain_fitting.abstract_fitting_class import PeakFit
|
|
151
|
+
|
|
152
|
+
class MyFitter(PeakFit):
|
|
153
|
+
@classmethod
|
|
154
|
+
def fit(cls, domain, **kwargs):
|
|
155
|
+
# your fitting logic here
|
|
156
|
+
...
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Custom background estimator
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
from scispectrum.background.base import BackgroundEstimator
|
|
163
|
+
|
|
164
|
+
class MyBackground(BackgroundEstimator):
|
|
165
|
+
def estimate(self, x, y):
|
|
166
|
+
# your background logic here
|
|
167
|
+
...
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
All custom classes integrate seamlessly with `Domain`, `Spectrum`, and the rest of the pipeline.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Examples
|
|
175
|
+
|
|
176
|
+
Full worked examples are available in the [examples directory](https://github.com/achiyaAmrusi/pySpectrum/tree/main/examples):
|
|
177
|
+
|
|
178
|
+
**Core**
|
|
179
|
+
- [Loading a spectrum](./examples/Core/loading_spectrum.ipynb) — reading and constructing a `Spectrum` from data
|
|
180
|
+
- [Calibration](./examples/Core/calibration.ipynb) — applying axis and resolution calibrations
|
|
181
|
+
- [Domain slicing](./examples/Core/domain_example.ipynb) — creating and working with domains
|
|
182
|
+
|
|
183
|
+
**Background Estimation**
|
|
184
|
+
- [Background subtraction](./examples/Background/background_substraction.ipynb) — estimating and subtracting background from a domain
|
|
185
|
+
|
|
186
|
+
**Domain Analysis and Fitting**
|
|
187
|
+
- [Domain fitting](./examples/Domain_Analysis_and_Fitting/domain_fitting.ipynb) — fitting peaks in a single domain
|
|
188
|
+
- [Full spectrum fitting](./examples/Domain_Analysis_and_Fitting/full_sepctrum_fitting.ipynb) — fitting peaks across an entire spectrum
|
|
189
|
+
|
|
190
|
+
**SNR Identification**
|
|
191
|
+
- [Peak domain identification](./examples/SNR%20Identification/peak_domin_identification.ipynb) — identifying signal regions automatically
|
|
192
|
+
- [Complex spectrum domains](./examples/SNR%20Identification/complex_spectrum_domains.ipynb) — handling overlapping and complex peak structures
|
|
193
|
+
|
|
194
|
+
**Library**
|
|
195
|
+
Sample data files for running the examples are provided in the [Library directory](./examples/Library).
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Requirements
|
|
200
|
+
|
|
201
|
+
- numpy>=2.0.0,<3.0
|
|
202
|
+
- pandas>=2.3,<4.0
|
|
203
|
+
- scipy>=1.14.0
|
|
204
|
+
- xarray>=2024.6.0
|
|
205
|
+
- uncertainties>=3.1
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## License
|
|
210
|
+
|
|
211
|
+
MIT License. See [LICENSE](LICENSE) for details.
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Author
|
|
216
|
+
|
|
217
|
+
Achiya Yosef Amrusi — [GitHub](https://github.com/achiyaAmrusi/pySpectrum)
|
|
218
|
+
|
|
219
|
+
Contributions and feedback are welcome.
|
|
220
|
+
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# scispectrum
|
|
2
|
+
|
|
3
|
+
A Python package for 1D spectrum analysis, designed for physicists and scientists working with detector data such as gamma-ray, X-ray, or particle spectra.
|
|
4
|
+
|
|
5
|
+
scispectrum provides a clean, extensible framework for the full spectrum analysis pipeline — from raw detector output to calibrated, fitted, background-subtracted results — with proper uncertainty propagation throughout.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Spectrum object** with axis calibration, resolution calibration, and error propagation using the uncertainties package
|
|
12
|
+
- **Domain slicing** by physical axis values (e.g. energy in keV) or channels
|
|
13
|
+
- **List-mode parser** for time-channel detector data, with chunked reading for large files
|
|
14
|
+
- **Domain fitting and analysis** — multiple fitting and analysis methods for a domain including sum of Gaussians fitting, find peaks, centers and fwhm
|
|
15
|
+
- **Background estimation** — multiple global background extimation methods including Asymmetric Least Squares (ALS)
|
|
16
|
+
- **Extensible base classes** — build your own fitting, background, or analysis procedures with a consistent interface
|
|
17
|
+
- **xarray throughout** — labeled, sliceable data with named coordinates
|
|
18
|
+
- **Uncertainty propagation** via the `uncertainties` library
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
scispectrum is not yet available on PyPI. Install directly from GitHub:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
git clone https://github.com/achiyaAmrusi/pySpectrum
|
|
28
|
+
cd pySpectrum
|
|
29
|
+
pip install -e .
|
|
30
|
+
```
|
|
31
|
+
This installs the package in editable mode, so any changes you make to the source are reflected immediately.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
### Load a spectrum from a DataFrame
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
import pandas as pd
|
|
41
|
+
from scispectrum.core import Spectrum
|
|
42
|
+
|
|
43
|
+
df = pd.read_csv("my_spectrum.csv")
|
|
44
|
+
spectrum = Spectrum.from_dataframe(df, channel_col="channel", counts_col="counts")
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Apply an energy calibration
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from scispectrum.calibration import AxisCalibration
|
|
51
|
+
|
|
52
|
+
# Linear calibration: energy = 0.5 * channel + 1.2
|
|
53
|
+
calib = AxisCalibration(lambda ch: 0.5 * ch + 1.2, name="energy_keV")
|
|
54
|
+
spectrum.set_axis_calibration(calib)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Slice a domain by axis values
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
# Select the region between 1460 and 1480 keV
|
|
61
|
+
domain = spectrum.domain(1460, 1480)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Fit peaks
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from scispectrum.domain_fitting import SumOfGaussians
|
|
68
|
+
|
|
69
|
+
result = SumOfGaussians.fit(domain)
|
|
70
|
+
|
|
71
|
+
print(result["center"].values) # peak centers in keV
|
|
72
|
+
print(result["fwhm"].values) # peak widths
|
|
73
|
+
print(result["amplitude"].values) # peak amplitudes
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Estimate and subtract background
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from scispectrum.background import ALSBackground
|
|
80
|
+
|
|
81
|
+
bg_estimator = ALSBackground(lam=1e5, p=0.001, max_iter=50)
|
|
82
|
+
als_bg = bg_estimator.estimate(spectrum.axis, spectrum.counts)
|
|
83
|
+
domain_subtracted = domains.subtract_background(als_bg[domain.indices])
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Parse raw list-mode data
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
from scispectrum.parsers import TimeChannelParser
|
|
90
|
+
|
|
91
|
+
# From a large file — processed in chunks to save memory
|
|
92
|
+
spectrum = TimeChannelParser.from_file(
|
|
93
|
+
"detector_run.csv",
|
|
94
|
+
axis_calib=calib,
|
|
95
|
+
num_of_channels=2**14
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# From an in-memory DataFrame
|
|
99
|
+
spectrum = TimeChannelParser.from_dataframe(df, axis_calib=calib)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Uncertainty Propagation
|
|
105
|
+
|
|
106
|
+
scispectrum propagates measurement uncertainties through arithmetic operations using the `uncertainties` library. Poisson errors are assigned automatically when parsing list-mode data.
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
# Arithmetic preserves errors
|
|
110
|
+
subtraction = spectrum_a - spectrum_b
|
|
111
|
+
subtraction.counts_err # propagated uncertainties
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Extending scispectrum
|
|
117
|
+
|
|
118
|
+
scispectrum is designed to be extended. Each analysis category has an abstract base class that defines the interface:
|
|
119
|
+
|
|
120
|
+
### Custom fitting method
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
from scispectrum.domain_fitting.abstract_fitting_class import PeakFit
|
|
124
|
+
|
|
125
|
+
class MyFitter(PeakFit):
|
|
126
|
+
@classmethod
|
|
127
|
+
def fit(cls, domain, **kwargs):
|
|
128
|
+
# your fitting logic here
|
|
129
|
+
...
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Custom background estimator
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
from scispectrum.background.base import BackgroundEstimator
|
|
136
|
+
|
|
137
|
+
class MyBackground(BackgroundEstimator):
|
|
138
|
+
def estimate(self, x, y):
|
|
139
|
+
# your background logic here
|
|
140
|
+
...
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
All custom classes integrate seamlessly with `Domain`, `Spectrum`, and the rest of the pipeline.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Examples
|
|
148
|
+
|
|
149
|
+
Full worked examples are available in the [examples directory](https://github.com/achiyaAmrusi/pySpectrum/tree/main/examples):
|
|
150
|
+
|
|
151
|
+
**Core**
|
|
152
|
+
- [Loading a spectrum](./examples/Core/loading_spectrum.ipynb) — reading and constructing a `Spectrum` from data
|
|
153
|
+
- [Calibration](./examples/Core/calibration.ipynb) — applying axis and resolution calibrations
|
|
154
|
+
- [Domain slicing](./examples/Core/domain_example.ipynb) — creating and working with domains
|
|
155
|
+
|
|
156
|
+
**Background Estimation**
|
|
157
|
+
- [Background subtraction](./examples/Background/background_substraction.ipynb) — estimating and subtracting background from a domain
|
|
158
|
+
|
|
159
|
+
**Domain Analysis and Fitting**
|
|
160
|
+
- [Domain fitting](./examples/Domain_Analysis_and_Fitting/domain_fitting.ipynb) — fitting peaks in a single domain
|
|
161
|
+
- [Full spectrum fitting](./examples/Domain_Analysis_and_Fitting/full_sepctrum_fitting.ipynb) — fitting peaks across an entire spectrum
|
|
162
|
+
|
|
163
|
+
**SNR Identification**
|
|
164
|
+
- [Peak domain identification](./examples/SNR%20Identification/peak_domin_identification.ipynb) — identifying signal regions automatically
|
|
165
|
+
- [Complex spectrum domains](./examples/SNR%20Identification/complex_spectrum_domains.ipynb) — handling overlapping and complex peak structures
|
|
166
|
+
|
|
167
|
+
**Library**
|
|
168
|
+
Sample data files for running the examples are provided in the [Library directory](./examples/Library).
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Requirements
|
|
173
|
+
|
|
174
|
+
- numpy>=2.0.0,<3.0
|
|
175
|
+
- pandas>=2.3,<4.0
|
|
176
|
+
- scipy>=1.14.0
|
|
177
|
+
- xarray>=2024.6.0
|
|
178
|
+
- uncertainties>=3.1
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## License
|
|
183
|
+
|
|
184
|
+
MIT License. See [LICENSE](LICENSE) for details.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Author
|
|
189
|
+
|
|
190
|
+
Achiya Yosef Amrusi — [GitHub](https://github.com/achiyaAmrusi/pySpectrum)
|
|
191
|
+
|
|
192
|
+
Contributions and feedback are welcome.
|
|
193
|
+
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=74.1.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "scispectrum"
|
|
7
|
+
version = "0.3.0"
|
|
8
|
+
description = "1D detector spectrum analysis — energy calibration, background estimation, peak detection and fitting"
|
|
9
|
+
authors = [
|
|
10
|
+
{ name = "Achiya Yosef Amrusi", email = "ahia.amrosi@mail.huji.ac.il" }
|
|
11
|
+
]
|
|
12
|
+
license = { text = "MIT" }
|
|
13
|
+
readme = "README.md"
|
|
14
|
+
requires-python = ">=3.11"
|
|
15
|
+
keywords = ["gamma-ray spectroscopy", "peak fitting", "background estimation", "detector calibration", "nuclear physics"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Intended Audience :: Science/Research",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Topic :: Scientific/Engineering :: Physics",
|
|
24
|
+
]
|
|
25
|
+
dependencies = [
|
|
26
|
+
"numpy>=2.0.0,<3.0",
|
|
27
|
+
"pandas>=2.3,<4.0",
|
|
28
|
+
"scipy>=1.14.0",
|
|
29
|
+
"xarray>=2024.6.0",
|
|
30
|
+
"uncertainties>=3.1",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Homepage = "https://github.com/achiyaAmrusi/scispectrum"
|
|
35
|
+
Repository = "https://github.com/achiyaAmrusi/scispectrum"
|
|
36
|
+
Issues = "https://github.com/achiyaAmrusi/scispectrum/issues"
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from scipy import sparse
|
|
3
|
+
from scipy.sparse.linalg import spsolve
|
|
4
|
+
from scispectrum.background.base import BackgroundEstimator
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ALSBackground(BackgroundEstimator):
|
|
8
|
+
"""
|
|
9
|
+
Asymmetric Least Squares (ALS) background estimator.
|
|
10
|
+
|
|
11
|
+
Estimates a smooth background by iteratively fitting a weighted
|
|
12
|
+
smoothing spline, penalizing points above the current estimate
|
|
13
|
+
more lightly than points below it. This asymmetry drives the
|
|
14
|
+
baseline to sit beneath the signal peaks.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
lam : float
|
|
19
|
+
Smoothness penalty. Larger values produce a smoother background.
|
|
20
|
+
Typical range: 1e3 to 1e7. Default is 1e5.
|
|
21
|
+
p : float
|
|
22
|
+
Asymmetry parameter. Controls the weight given to points above
|
|
23
|
+
the current estimate. Should be small (e.g. 0.001 to 0.1) so
|
|
24
|
+
the baseline stays below peaks. Default is 0.01.
|
|
25
|
+
max_iter : int
|
|
26
|
+
Number of reweighting iterations. More iterations refine the
|
|
27
|
+
baseline but increase compute time. Default is 20.
|
|
28
|
+
|
|
29
|
+
References
|
|
30
|
+
----------
|
|
31
|
+
Eilers, P.H.C. and Boelens, H.F.M. (2005).
|
|
32
|
+
"Baseline correction with asymmetric least squares smoothing."
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(self, lam=1e5, p=0.01, max_iter=20):
|
|
36
|
+
self.lam = lam
|
|
37
|
+
self.p = p
|
|
38
|
+
self.max_iter = max_iter
|
|
39
|
+
|
|
40
|
+
def estimate(self, axis: np.ndarray, counts: np.ndarray) -> np.ndarray:
|
|
41
|
+
"""
|
|
42
|
+
Estimate the background of a spectrum using ALS.
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
axis : np.ndarray
|
|
47
|
+
Axis values (not used by ALS, required by the interface).
|
|
48
|
+
counts : np.ndarray
|
|
49
|
+
Spectrum counts.
|
|
50
|
+
|
|
51
|
+
Returns
|
|
52
|
+
-------
|
|
53
|
+
np.ndarray
|
|
54
|
+
Estimated background, same shape as counts.
|
|
55
|
+
"""
|
|
56
|
+
y = counts
|
|
57
|
+
n = len(y)
|
|
58
|
+
|
|
59
|
+
# Second-order difference matrix (n-2 x n) — penalizes curvature
|
|
60
|
+
# in the background estimate. D.T @ D appears in the smoothness term.
|
|
61
|
+
D = sparse.diags([1, -2, 1], [0, 1, 2], shape=(n - 2, n), dtype=float)
|
|
62
|
+
DTD = D.T @ D
|
|
63
|
+
|
|
64
|
+
# Initialize weights uniformly — all points treated equally
|
|
65
|
+
w = np.ones(n)
|
|
66
|
+
|
|
67
|
+
for _ in range(self.max_iter):
|
|
68
|
+
# Diagonal weight matrix for the current iteration
|
|
69
|
+
W = sparse.diags(w, 0)
|
|
70
|
+
|
|
71
|
+
# Solve the weighted penalized least squares system:
|
|
72
|
+
# (W + lam * D'D) z = W y
|
|
73
|
+
Z = W + self.lam * DTD
|
|
74
|
+
z = spsolve(Z.tocsc(), w * y)
|
|
75
|
+
|
|
76
|
+
# Asymmetric reweighting: points above the baseline get weight p,
|
|
77
|
+
# points below get weight 1-p. This pulls the baseline downward
|
|
78
|
+
# toward the true background, away from peaks.
|
|
79
|
+
w = np.where(y > z, self.p, 1 - self.p)
|
|
80
|
+
|
|
81
|
+
return z
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BackgroundEstimator(ABC):
|
|
7
|
+
"""
|
|
8
|
+
Abstract base class for background estimation algorithms.
|
|
9
|
+
|
|
10
|
+
All auxiliary inputs (resolution calibration, convolution objects, etc.)
|
|
11
|
+
must be passed at construction time, not to estimate().
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
@abstractmethod
|
|
15
|
+
def estimate(self, axis: np.ndarray, counts: np.ndarray) -> np.ndarray:
|
|
16
|
+
"""
|
|
17
|
+
Estimate background for a 1D spectrum.
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
axis : np.ndarray
|
|
22
|
+
Axis values (e.g. energy in keV).
|
|
23
|
+
counts : np.ndarray
|
|
24
|
+
Spectrum counts.
|
|
25
|
+
|
|
26
|
+
Returns
|
|
27
|
+
-------
|
|
28
|
+
np.ndarray
|
|
29
|
+
Estimated background, same shape as counts.
|
|
30
|
+
"""
|
|
31
|
+
pass
|