suvtools 1.0.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.
suvtools/__init__.py ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Program: SUV tools
5
+ @author: Pranab Das (GitHub: @pranabdas)
6
+ """
7
+ from .load import load
8
+ from .fit_gauss import fit_gauss
9
+ from .fit_lorentz import fit_lorentz
10
+ from .save_csv import save_csv
11
+ from .norm_bg import norm_bg
12
+ from .lock_peak import lock_peak
13
+ from .calc_area import calc_area
suvtools/calc_area.py ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Program: Calculate area under the curve
5
+ @author: Pranab Das (GitHub: @pranabdas)
6
+ area = suv.calc_area(y, x, x_end=None, x_end=None)
7
+ """
8
+
9
+
10
+ def calc_area(y, x, x_start=None, x_end=None):
11
+ import numpy as np
12
+
13
+ if x_start:
14
+ start_index = np.argmin(abs(x - x_start))
15
+ else:
16
+ start_index = 0
17
+
18
+ if x_end:
19
+ end_index = np.argmin(abs(x - x_end))
20
+ else:
21
+ end_index = -1
22
+
23
+ return np.trapezoid(y[start_index:end_index], x[start_index:end_index])
suvtools/fit_gauss.py ADDED
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Program: Fit peaks with Gaussian distribution
5
+ @author: Pranab Das (GitHub: @pranabdas)
6
+ data = suv.fit_gauss(x, y, a=None, x0=None, sigma=None, xmin=None, xmax=None)
7
+ """
8
+
9
+
10
+ def fit_gauss(x, y, a=None, x0=None, sigma=None, xmin=None, xmax=None, num=1000):
11
+ import numpy as np
12
+ from scipy import optimize
13
+
14
+ def gauss(x, a, x0, sigma):
15
+ return a * np.exp(-((x - x0) ** 2) / (2 * sigma**2))
16
+
17
+ if xmin:
18
+ xmin = float(xmin)
19
+ xmin_id = np.argmin(abs(x - xmin))
20
+ x = x[xmin_id:]
21
+ y = y[xmin_id:]
22
+ else:
23
+ xmin = x[0]
24
+
25
+ if xmax:
26
+ xmax = float(xmax)
27
+ xmax_id = np.argmin(abs(x - xmax))
28
+ x = x[:xmax_id]
29
+ y = y[:xmax_id]
30
+ else:
31
+ xmax = x[-1]
32
+
33
+ if not a:
34
+ a = max(y)
35
+
36
+ if not x0:
37
+ x0 = x[np.argmax(y)]
38
+
39
+ if not sigma:
40
+ sigma = abs(x[-1] - x[0]) / 10
41
+
42
+ params, _ = optimize.curve_fit(gauss, x, y, p0=[a, x0, sigma])
43
+
44
+ print(
45
+ "\n#---- Gaussian fitting parameters ----#\na =",
46
+ params[0],
47
+ "\nx0 =",
48
+ params[1],
49
+ "\nsigma =",
50
+ params[2],
51
+ "\nFWHM =",
52
+ 2.355 * params[2],
53
+ "\n#----------------------------------------#",
54
+ )
55
+
56
+ fit_x = np.linspace(xmin, xmax, num)
57
+ fit_y = gauss(fit_x, params[0], params[1], params[2])
58
+
59
+ return fit_x, fit_y
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Program: Fit peaks with Lorentzian distribution
5
+ @author: Pranab Das (GitHub: @pranabdas)
6
+ data = suv.fit_lorentz(x, y, a=None, x0=None, gamma=None, xmin=None, xmax=None)
7
+ """
8
+
9
+
10
+ def fit_lorentz(x, y, a=None, x0=None, gamma=None, xmin=None, xmax=None, num=1000):
11
+ import numpy as np
12
+ from scipy import optimize
13
+
14
+ def lorentz(x, a, x0, gamma):
15
+ return a * gamma**2 / (np.pi * gamma * ((x - x0) ** 2 + gamma**2))
16
+
17
+ if xmin:
18
+ xmin = float(xmin)
19
+ xmin_id = np.argmin(abs(x - xmin))
20
+ x = x[xmin_id:]
21
+ y = y[xmin_id:]
22
+ else:
23
+ xmin = x[0]
24
+
25
+ if xmax:
26
+ xmax = float(xmax)
27
+ xmax_id = np.argmin(abs(x - xmax))
28
+ x = x[:xmax_id]
29
+ y = y[:xmax_id]
30
+ else:
31
+ xmax = x[-1]
32
+
33
+ if not a:
34
+ a = max(y)
35
+
36
+ if not x0:
37
+ x0 = x[np.argmax(y)]
38
+
39
+ if not gamma:
40
+ gamma = abs(x[-1] - x[0]) / 10
41
+
42
+ params, _ = optimize.curve_fit(lorentz, x, y, p0=[a, x0, gamma])
43
+
44
+ print(
45
+ "\n#---- Lorentzian fitting parameters ----#\na =",
46
+ params[0],
47
+ "\nx0 =",
48
+ params[1],
49
+ "\ngamma =",
50
+ params[2],
51
+ "\nFWHM =",
52
+ 2 * params[2],
53
+ "\n#---------------------------------------#",
54
+ )
55
+
56
+ fit_x = np.linspace(xmin, xmax, num)
57
+ fit_y = lorentz(fit_x, params[0], params[1], params[2])
58
+
59
+ return fit_x, fit_y
suvtools/load.py ADDED
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Program: Load SUV data
5
+ @author: Pranab Das (GitHub: @pranabdas)
6
+ data = suv.load("data.txt", 12)
7
+ """
8
+
9
+
10
+ def load(filename, scan=None):
11
+ import numpy as np
12
+ import urllib.request
13
+ import itertools
14
+
15
+ if (filename[:7] == "http://") or (filename[:8] == "https://"):
16
+ web = True
17
+ else:
18
+ web = False
19
+
20
+ if web:
21
+ try:
22
+ headers = {"User-Agent": "Mozilla/5.0"}
23
+ req = urllib.request.Request(url=filename, headers=headers)
24
+ contents = urllib.request.urlopen(req).read().decode()
25
+ contents = contents.splitlines()
26
+ except:
27
+ print("Could not read url.")
28
+
29
+ else:
30
+ fid = open(filename, "r")
31
+ if fid.mode == "r":
32
+ contents = fid.read()
33
+ fid.close()
34
+ contents = contents.splitlines()
35
+
36
+ # remove empty lines
37
+ # contents = list(filter(None, contents))
38
+
39
+ # find scan id and corresponding line numbers
40
+ scan_id = []
41
+ line_id = []
42
+
43
+ for line in range(len(contents)):
44
+ line_content = contents[line].split(" ")
45
+ if line_content[0] == "#S":
46
+ scan_id.append(int(line_content[1]))
47
+ line_id.append(line)
48
+
49
+ scan_id = np.array(scan_id)
50
+ # Determine the lines to read for specific scan
51
+ # Default scan set to the last scan
52
+ if not scan:
53
+ scan = scan_id[-1]
54
+
55
+ # In case given scan does not exist in the file
56
+ if scan not in scan_id:
57
+ print("Error: scan not found!")
58
+ return
59
+ else:
60
+ line_start = line_id[np.where(scan_id == scan)[0][0]]
61
+
62
+ if scan == scan_id[-1]:
63
+ line_end = len(contents)
64
+ else:
65
+ line_end = line_id[np.where(scan_id == scan)[0][0] + 1] - 1
66
+
67
+ # Extract the column names
68
+ # for line in range(line_end-line_start):
69
+ # line_content = contents[line+line_start].split(' ')
70
+ # if line_content[0]=='#L':
71
+ # colnames=line_content
72
+ # colnames.pop(0)
73
+ # colnames = list(filter(None, colnames))
74
+ # break
75
+
76
+ # print("Scan #", scan, "loaded. Data columns are: \n", colnames)
77
+
78
+ lines_to_read = itertools.islice(open(filename), line_start, line_end)
79
+ filtered_lines = itertools.dropwhile(lambda x: x.strip()[0] == "#", lines_to_read)
80
+
81
+ return np.loadtxt(filtered_lines)
suvtools/lock_peak.py ADDED
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Program: Lock the peaks at same energy location
5
+ @author: Pranab Das (GitHub: @pranabdas)
6
+ data = suv.lock_peak(data, refdata, x1=None, x2=None, E_col=0, I_col=9, I0_col=4)
7
+ """
8
+
9
+
10
+ def lock_peak(data, refdata, x1=None, x2=None, E_col=0, I_col=9, I0_col=4):
11
+ import numpy as np
12
+
13
+ if data.shape != refdata.shape:
14
+ print("Error! Data and reference data must have same dimensions!")
15
+ return
16
+
17
+ if not x1:
18
+ x1 = refdata[0, E_col]
19
+ if not x2:
20
+ x2 = refdata[-1, E_col]
21
+
22
+ x1_id = np.argmin(abs(refdata[:, E_col] - x1))
23
+ x2_id = np.argmin(abs(refdata[:, E_col] - x2))
24
+
25
+ ref_peak_id = np.argmax(refdata[x1_id:x2_id, I_col] / refdata[x1_id:x2_id, I0_col])
26
+
27
+ peak_id = np.argmax(data[x1_id:x2_id, I_col] / data[x1_id:x2_id, I0_col])
28
+
29
+ diff_id = peak_id - ref_peak_id
30
+
31
+ if diff_id > 0:
32
+ for _ in range(diff_id):
33
+ data = np.delete(data, 0, 0)
34
+ data = np.vstack((data, data[-1, :]))
35
+ elif diff_id < 0:
36
+ for _ in range(abs(diff_id)):
37
+ data = np.delete(data, -1, 0)
38
+ data = np.vstack((data[0, :], data))
39
+
40
+ data[:, E_col] = refdata[:, E_col]
41
+
42
+ return data
suvtools/norm_bg.py ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Program: Subtracts linear background and normalizes the data
5
+ @author: Pranab Das (GitHub: @pranabdas)
6
+ normalized_intensity = suv.norm_bg(energy, intensity, x1, x2, x_norm_loc=None)
7
+ """
8
+
9
+
10
+ def norm_bg(energy, intensity, x1, x2, x_norm_loc=None):
11
+ import numpy as np
12
+
13
+ y1 = intensity[np.argmin(abs(energy - x1))]
14
+ y2 = intensity[np.argmin(abs(energy - x2))]
15
+
16
+ if x_norm_loc:
17
+ x_norm_loc = np.argmin(abs(energy - x_norm_loc))
18
+ else:
19
+ x_norm_loc = -1
20
+
21
+ m = (y2 - y1) / (x2 - x1) # slope
22
+ c = y1 - (m * x1) # intersection
23
+
24
+ bg = (m * energy) + c
25
+ intensity -= bg
26
+ return intensity / intensity[x_norm_loc]
suvtools/save_csv.py ADDED
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Program: Export SUV data in CSV format
5
+ @author: Pranab Das (GitHub: @pranabdas)
6
+ data = suv.save_csv(filename, csvname=None, scan=None)
7
+ """
8
+
9
+
10
+ def save_csv(filename, csvname=None, scan=None):
11
+ import numpy as np
12
+ import pandas as pd
13
+ import os
14
+ import warnings
15
+
16
+ fid = open(filename, "r")
17
+ contents = fid.read()
18
+ fid.close()
19
+ contents = contents.splitlines()
20
+
21
+ # find scan id and corresponding line numbers
22
+ scan_id = []
23
+ line_id = []
24
+
25
+ for line in range(len(contents)):
26
+ line_content = contents[line].split(" ")
27
+ if line_content[0] == "#S":
28
+ scan_id.append(int(line_content[1]))
29
+ line_id.append(line)
30
+
31
+ # determine the lines to read for specific scan
32
+ # default scan set to the last scan
33
+ if not scan:
34
+ scan = scan_id[-1]
35
+
36
+ # if given scan does not exist in the file
37
+ if scan not in scan_id:
38
+ print("Error! Scan not found!")
39
+ return
40
+ else:
41
+ line_start = line_id[scan - 1]
42
+ if scan == scan_id[-1]:
43
+ line_end = len(contents)
44
+ else:
45
+ line_end = line_id[scan] - 1
46
+
47
+ if not csvname:
48
+ csvname = filename + "_scan_" + str(scan) + ".csv"
49
+
50
+ if os.path.isfile(csvname):
51
+ if csvname[-4:] == ".csv":
52
+ csvname = csvname[:-4]
53
+ csvname = csvname + "_copy.csv"
54
+
55
+ # Extract the column names
56
+ for line in range(line_end - line_start):
57
+ line_content = contents[line + line_start].split(" ")
58
+ if line_content[0] == "#L":
59
+ colnames = line_content
60
+ colnames.pop(0)
61
+ colnames = list(filter(None, colnames))
62
+
63
+ # suppress numpy warning related to max_rows behavior
64
+ with warnings.catch_warnings():
65
+ warnings.filterwarnings(action="ignore")
66
+ data = np.loadtxt(
67
+ filename,
68
+ comments="#",
69
+ skiprows=(line_start - 1),
70
+ max_rows=(line_end - line_start),
71
+ )
72
+
73
+ # convert to dataframe
74
+ data = pd.DataFrame(data, columns=colnames)
75
+ data.to_csv(csvname, index=False)
76
+ print("Saved data to file:", csvname)
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020-Present Pranab Das (GitHub: @pranabdas)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9
+ of the Software, and to permit persons to whom the Software is furnished to do
10
+ 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, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,110 @@
1
+ Metadata-Version: 2.1
2
+ Name: suvtools
3
+ Version: 1.0.1
4
+ Summary: Python library for analyzing and visualizing SSLS SUV Beamline data.
5
+ Home-page: https://pranabdas.github.io/suvtools/
6
+ License: MIT
7
+ Keywords: python,data-analysis,matplotlib,materials-science,surface-science,condensed-matter-physics,photoelectron-spectroscopy,arpes
8
+ Author: Pranab Das
9
+ Requires-Python: >=3.9,<4.0
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Requires-Dist: matplotlib (>=3.9.2,<4.0.0)
17
+ Requires-Dist: numpy (>=2.0.2,<3.0.0)
18
+ Requires-Dist: pandas (>=2.2.3,<3.0.0)
19
+ Requires-Dist: scipy (>=1.13.1,<2.0.0)
20
+ Project-URL: Documentation, https://pranabdas.github.io/suvtools/docs/
21
+ Project-URL: Repository, https://github.com/pranabdas/suvtools
22
+ Description-Content-Type: text/markdown
23
+
24
+ <h1 align="center">SUV Tools</h1>
25
+
26
+ <p align="center">
27
+ <a href="https://github.com/pranabdas/suvtools/actions/workflows/python-tests.yml"><img src="https://github.com/pranabdas/suvtools/actions/workflows/python-tests.yml/badge.svg" alt="Python tests status"></a>
28
+ <a href="https://github.com/pranabdas/suvtools/actions/workflows/deploy-gh-pages.yml"><img src="https://github.com/pranabdas/suvtools/actions/workflows/deploy-gh-pages.yml/badge.svg" alt="Deploy gh-pages status"></a>
29
+ <a href="https://github.com/pranabdas/suvtools/actions/workflows/publish.yml"><img src="https://github.com/pranabdas/suvtools/actions/workflows/publish.yml/badge.svg" alt="Deploy gh-pages status"></a>
30
+ <a href="https://github.com/pranabdas/suvtools/blob/master/LICENSE"><img src="https://img.shields.io/github/license/sourcerer-io/hall-of-fame.svg?colorB=A31F34"></a>
31
+ <a href="https://pypi.org/project/suvtools/"><img src="https://img.shields.io/github/v/release/pranabdas/suvtools.svg" alt="Latest Release"/></a>
32
+ </p>
33
+
34
+
35
+ Visit the project homepage <https://pranabdas.github.io/suvtools/>
36
+
37
+ ## Quick start
38
+
39
+ Install latest stable release:
40
+ ```console
41
+ pip3 install --upgrade suvtools
42
+ ```
43
+
44
+ Import suvtools into your project:
45
+ ```python
46
+ import suvtools as suv
47
+ ```
48
+
49
+ **Modules:**
50
+
51
+ - `suv.load("datafile.txt", scan=None)`: It will return a two dimensional array
52
+ with columns for various parameters. If the second argument, i.e., the scan
53
+ number is not specified, the code will read the last scan from the file.
54
+
55
+ - `suv.fit_gauss(x, y, a=None, x0=None, sigma=None, xmin=None, xmax=None, num=1000)`:
56
+ returns x, Gaussian fitted y values, and prints out relevant parameters. `xmin`
57
+ and `xmax` determines the range to fit. If `xmin` and `xmax` are not provided,
58
+ whole range is used. `num` determines the number of points returned in `x_fit`
59
+ and `y_fit`.
60
+
61
+ - `suv.fit_lorentz(x, y, a=None, x0=None, gamma=None, xmin=None, xmax=None, num=1000)`:
62
+ returns x, Lorentzian fitted y values, and prints out relevant parameters.
63
+ `xmin` and `xmax` determines the range to fit. If `xmin` and `xmax` are not
64
+ provided, whole range is used. `num` determines the number of points returned in
65
+ `x_fit` and `y_fit`.
66
+
67
+ - `suv.save_csv("datafile.txt", csvname=None, scan=None)`: saves scan to a csv
68
+ file. The file will be saved in the save directory as `datafile` with name
69
+ `datafile.csv` unless `csvname` is specified. Like the `load` module, if the
70
+ scan number is not specified, it will read the last scan from the file.
71
+
72
+ - `suv.norm_bg(energy, intensity, x1, x2, x_norm_loc=None)`:
73
+ Removes linear background, and normalizes the data. x1, x2 are energy values
74
+ that determines the slope of the background. By default the normalization done
75
+ at the tail point of the spectra. It can be changed to other point, enter the
76
+ corresponding energy value. The intention is to normalize at an energy value
77
+ away from the peaks/features of interest.
78
+
79
+ - `suv.lock_peak(data, refdata, x1=None, x2=None, E_col=0, I_col=9, I0_col=4)`:
80
+ Locks peak position with respect to the reference data. It locks the maximum of
81
+ intensity to the same energy; the range of peak search can be specified by input
82
+ `x1` and `x2`. If no bounds are given, it will find the maximum in the whole
83
+ data range.
84
+
85
+ - `suv.calc_area(y, x, x_start=None, x_end=None)`:
86
+ Calculates area under the curve for given `x` and `y` values. `x_start` and
87
+ `x_end` can be specified to set the limit of integration region, if not provided
88
+ whole range is integrated.
89
+
90
+ See the notebook and documentation for example usage.
91
+
92
+ ### Documentation development
93
+
94
+ ```console
95
+ # install npm packages
96
+ npm install
97
+
98
+ # serve locally
99
+ npm start
100
+
101
+ # build
102
+ npm run build
103
+ ```
104
+
105
+ ### Python tests
106
+
107
+ ```console
108
+ python3 -m unittest discover tests
109
+ ```
110
+
@@ -0,0 +1,12 @@
1
+ suvtools/__init__.py,sha256=xWPTY65VV7bfT42Ox-v4-yVlRG7usXrDhjZuPLeuQ-I,334
2
+ suvtools/calc_area.py,sha256=2BWAwcvHyNgMCjEv-kQ-gOIL_2MZ8noFrWQ69likDwI,531
3
+ suvtools/fit_gauss.py,sha256=StpdmKocKeb_SUflujTAg9X2y8rx5StTxuZLMugnZ28,1382
4
+ suvtools/fit_lorentz.py,sha256=hBOSz162IX_qNHd2Cxs3SsJnG4iYQqiMWi8ua3zsTjA,1405
5
+ suvtools/load.py,sha256=Yo-vnOGNEiPnl9xgh4MweTur-lReSj1TcBqHAELAfAA,2340
6
+ suvtools/lock_peak.py,sha256=UeeRJhAxUfUpt7oXySD1ebsaxQ0W5-CwxfA4n2YBlmY,1209
7
+ suvtools/norm_bg.py,sha256=Rq5gweCt3FjSBoPIHYWu-dwo6qHdZQHeIIMd8-qI0yw,696
8
+ suvtools/save_csv.py,sha256=IjR0AB_G0ejFsY9F9Mbvl30KfxzMQcLaR-5hNAbBE48,2149
9
+ suvtools-1.0.1.dist-info/LICENSE,sha256=nvsF0-R601iteeQ-U8lB30wwJ3Tvpv_SYT9Syx5ntsI,1096
10
+ suvtools-1.0.1.dist-info/METADATA,sha256=HCy5tQLiN4A38G00Yi6R6tLEDHAYK4Dvz4Dh7g3U_To,4721
11
+ suvtools-1.0.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
12
+ suvtools-1.0.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 1.9.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any