pychemstation 0.4.7.dev1__py3-none-any.whl → 0.4.7.dev2__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.
Files changed (104) hide show
  1. pychemstation/control/__init__.py +3 -2
  2. hein-analytical-control/devices/Agilent/hplc.py → pychemstation/control/comm.py +21 -181
  3. pychemstation/control/method.py +232 -0
  4. pychemstation/control/sequence.py +140 -0
  5. pychemstation/control/table_controller.py +75 -0
  6. pychemstation/utils/__init__.py +0 -2
  7. {ag_hplc_macro/control → pychemstation/utils}/chromatogram.py +2 -1
  8. pychemstation/utils/constants.py +1 -1
  9. hein_analytical_control/devices/Agilent/hplc_param_types.py → pychemstation/utils/macro.py +5 -69
  10. pychemstation/utils/method_types.py +44 -0
  11. pychemstation/utils/sequence_types.py +33 -0
  12. pychemstation/utils/table_types.py +60 -0
  13. {pychemstation-0.4.7.dev1.dist-info → pychemstation-0.4.7.dev2.dist-info}/METADATA +13 -12
  14. pychemstation-0.4.7.dev2.dist-info/RECORD +30 -0
  15. ag_hplc_macro/__init__.py +0 -3
  16. ag_hplc_macro/analysis/__init__.py +0 -1
  17. ag_hplc_macro/analysis/base_spectrum.py +0 -509
  18. ag_hplc_macro/analysis/spec_utils.py +0 -304
  19. ag_hplc_macro/analysis/utils.py +0 -63
  20. ag_hplc_macro/control/__init__.py +0 -5
  21. ag_hplc_macro/control/hplc.py +0 -673
  22. ag_hplc_macro/generated/__init__.py +0 -56
  23. ag_hplc_macro/generated/dad_method.py +0 -367
  24. ag_hplc_macro/generated/pump_method.py +0 -519
  25. ag_hplc_macro/utils/__init__.py +0 -2
  26. ag_hplc_macro/utils/constants.py +0 -15
  27. ag_hplc_macro/utils/hplc_param_types.py +0 -185
  28. hein-analytical-control/__init__.py +0 -3
  29. hein-analytical-control/analysis/__init__.py +0 -1
  30. hein-analytical-control/analysis/base_spectrum.py +0 -509
  31. hein-analytical-control/analysis/spec_utils.py +0 -304
  32. hein-analytical-control/analysis/utils.py +0 -63
  33. hein-analytical-control/devices/Agilent/__init__.py +0 -3
  34. hein-analytical-control/devices/Agilent/chemstation.py +0 -290
  35. hein-analytical-control/devices/Agilent/chromatogram.py +0 -129
  36. hein-analytical-control/devices/Agilent/hplc_param_types.py +0 -141
  37. hein-analytical-control/devices/Magritek/Spinsolve/__init__.py +0 -0
  38. hein-analytical-control/devices/Magritek/Spinsolve/commands.py +0 -495
  39. hein-analytical-control/devices/Magritek/Spinsolve/spectrum.py +0 -822
  40. hein-analytical-control/devices/Magritek/Spinsolve/spinsolve.py +0 -425
  41. hein-analytical-control/devices/Magritek/Spinsolve/utils/__init__.py +0 -5
  42. hein-analytical-control/devices/Magritek/Spinsolve/utils/connection.py +0 -168
  43. hein-analytical-control/devices/Magritek/Spinsolve/utils/constants.py +0 -8
  44. hein-analytical-control/devices/Magritek/Spinsolve/utils/exceptions.py +0 -25
  45. hein-analytical-control/devices/Magritek/Spinsolve/utils/parser.py +0 -340
  46. hein-analytical-control/devices/Magritek/Spinsolve/utils/shimming.py +0 -55
  47. hein-analytical-control/devices/Magritek/Spinsolve/utils/spinsolve_logging.py +0 -43
  48. hein-analytical-control/devices/Magritek/__init__.py +0 -0
  49. hein-analytical-control/devices/OceanOptics/IR/NIRQuest512.py +0 -90
  50. hein-analytical-control/devices/OceanOptics/IR/__init__.py +0 -0
  51. hein-analytical-control/devices/OceanOptics/IR/ir_spectrum.py +0 -191
  52. hein-analytical-control/devices/OceanOptics/Raman/__init__.py +0 -0
  53. hein-analytical-control/devices/OceanOptics/Raman/raman_control.py +0 -46
  54. hein-analytical-control/devices/OceanOptics/Raman/raman_spectrum.py +0 -148
  55. hein-analytical-control/devices/OceanOptics/UV/QEPro2192.py +0 -90
  56. hein-analytical-control/devices/OceanOptics/UV/__init__.py +0 -0
  57. hein-analytical-control/devices/OceanOptics/UV/uv_spectrum.py +0 -227
  58. hein-analytical-control/devices/OceanOptics/__init__.py +0 -0
  59. hein-analytical-control/devices/OceanOptics/oceanoptics.py +0 -115
  60. hein-analytical-control/devices/__init__.py +0 -15
  61. hein-analytical-control/generated/__init__.py +0 -56
  62. hein-analytical-control/generated/dad_method.py +0 -367
  63. hein-analytical-control/generated/pump_method.py +0 -519
  64. hein_analytical_control/__init__.py +0 -3
  65. hein_analytical_control/analysis/__init__.py +0 -1
  66. hein_analytical_control/analysis/base_spectrum.py +0 -509
  67. hein_analytical_control/analysis/spec_utils.py +0 -304
  68. hein_analytical_control/analysis/utils.py +0 -63
  69. hein_analytical_control/devices/Agilent/__init__.py +0 -3
  70. hein_analytical_control/devices/Agilent/chemstation.py +0 -290
  71. hein_analytical_control/devices/Agilent/chromatogram.py +0 -129
  72. hein_analytical_control/devices/Agilent/hplc.py +0 -436
  73. hein_analytical_control/devices/Magritek/Spinsolve/__init__.py +0 -0
  74. hein_analytical_control/devices/Magritek/Spinsolve/commands.py +0 -495
  75. hein_analytical_control/devices/Magritek/Spinsolve/spectrum.py +0 -822
  76. hein_analytical_control/devices/Magritek/Spinsolve/spinsolve.py +0 -425
  77. hein_analytical_control/devices/Magritek/Spinsolve/utils/__init__.py +0 -5
  78. hein_analytical_control/devices/Magritek/Spinsolve/utils/connection.py +0 -168
  79. hein_analytical_control/devices/Magritek/Spinsolve/utils/constants.py +0 -8
  80. hein_analytical_control/devices/Magritek/Spinsolve/utils/exceptions.py +0 -25
  81. hein_analytical_control/devices/Magritek/Spinsolve/utils/parser.py +0 -340
  82. hein_analytical_control/devices/Magritek/Spinsolve/utils/shimming.py +0 -55
  83. hein_analytical_control/devices/Magritek/Spinsolve/utils/spinsolve_logging.py +0 -43
  84. hein_analytical_control/devices/Magritek/__init__.py +0 -0
  85. hein_analytical_control/devices/OceanOptics/IR/NIRQuest512.py +0 -90
  86. hein_analytical_control/devices/OceanOptics/IR/__init__.py +0 -0
  87. hein_analytical_control/devices/OceanOptics/IR/ir_spectrum.py +0 -191
  88. hein_analytical_control/devices/OceanOptics/Raman/__init__.py +0 -0
  89. hein_analytical_control/devices/OceanOptics/Raman/raman_control.py +0 -46
  90. hein_analytical_control/devices/OceanOptics/Raman/raman_spectrum.py +0 -148
  91. hein_analytical_control/devices/OceanOptics/UV/QEPro2192.py +0 -90
  92. hein_analytical_control/devices/OceanOptics/UV/__init__.py +0 -0
  93. hein_analytical_control/devices/OceanOptics/UV/uv_spectrum.py +0 -227
  94. hein_analytical_control/devices/OceanOptics/__init__.py +0 -0
  95. hein_analytical_control/devices/OceanOptics/oceanoptics.py +0 -115
  96. hein_analytical_control/devices/__init__.py +0 -15
  97. hein_analytical_control/generated/__init__.py +0 -56
  98. hein_analytical_control/generated/dad_method.py +0 -367
  99. hein_analytical_control/generated/pump_method.py +0 -519
  100. pychemstation-0.4.7.dev1.dist-info/RECORD +0 -109
  101. /ag_hplc_macro/utils/chemstation.py → /pychemstation/utils/parsing.py +0 -0
  102. {pychemstation-0.4.7.dev1.dist-info → pychemstation-0.4.7.dev2.dist-info}/LICENSE +0 -0
  103. {pychemstation-0.4.7.dev1.dist-info → pychemstation-0.4.7.dev2.dist-info}/WHEEL +0 -0
  104. {pychemstation-0.4.7.dev1.dist-info → pychemstation-0.4.7.dev2.dist-info}/top_level.txt +0 -0
@@ -1,191 +0,0 @@
1
- """
2
- .. module:: ir_spectrum
3
- :synopsis: Module for representing an IR spectrum
4
- :platforms: Unix, Windows
5
-
6
- .. moduleauthor:: Graham Keenan (Cronin Lab 2020)
7
-
8
- .. note:: Edit as needed, this is a skeletal implementation that can be improved
9
- """
10
-
11
- import json
12
- import time
13
- import numpy as np
14
- import matplotlib.pyplot as plt
15
-
16
-
17
- def _write_json(data: dict, filename: str):
18
- """Write data out to a JSON file
19
-
20
- Args:
21
- data (dict): Data to write
22
- filename (str): Path to save JSON file
23
- """
24
-
25
- with open(filename, "w") as f_d:
26
- json.dump(data, f_d, indent=4)
27
-
28
-
29
- def _load_json(filename: str) -> dict:
30
- """Loads a JSON file from disk
31
-
32
- Args:
33
- filename (str): Name of the file to load
34
-
35
- Returns:
36
- dict: JSON data
37
- """
38
-
39
- with open(filename) as f_d:
40
- return json.load(f_d)
41
-
42
-
43
- def _ensure_serializable(data: dict) -> dict:
44
- """Ensures the output data is serializable
45
-
46
- Args:
47
- data (Dict): Data to check
48
-
49
- Returns:
50
- Dict: Serializable data
51
- """
52
-
53
- for k, v in data.items():
54
- if isinstance(v, np.ndarray):
55
- data[k] = v.tolist()
56
- elif isinstance(v, dict):
57
- data[k] = _ensure_serializable(v)
58
- return data
59
-
60
-
61
- def trim_data(self, data: list, lower_range: int, upper_range: int) -> np.ndarray:
62
- """Trims data within a certain range
63
- Returns valid indexes that can be used to trim X and Y data
64
-
65
- Args:
66
- data (list): Data to trim within the range
67
- lower_range (int): Lower end of the range (Minimum value)
68
- upper_range (int): Upper end of the range (Maximum value)
69
-
70
- Returns:
71
- np.ndarray: Indexes to use for trimming data
72
- """
73
-
74
- above_ind = np.array(data) > lower_range
75
- below_ind = np.array(data) < upper_range
76
-
77
- return np.logical_and(above_ind, below_ind)
78
-
79
-
80
- class IRSpectrum:
81
- """Class representing an IR spectrum.
82
- Spectrum can be a reference spectrum or a measured spectrum
83
-
84
- Args:
85
- wavelengths (list): Wavelengths of the spectrum
86
- intensities (list): Intensities of the spectrum
87
- ref (dict, optional): Reference spectrum if available
88
- """
89
-
90
- def __init__(self, wavelengths: list, intensities: list, ref: dict = {}):
91
- self.wavelengths = wavelengths
92
- self.intensities = intensities
93
- self.reference = ref
94
- self.wavenumbers = []
95
- self.transmittance = []
96
-
97
- if self.reference:
98
- self.convert_intensities_to_transmittance()
99
- self.convert_wavelength_to_wavenumber()
100
-
101
- @classmethod
102
- def load_spectrum(cls, filepath: str):
103
- """Class method for loading spectrum data from a file
104
-
105
- Args:
106
- filepath (str): Path to spectrum data
107
-
108
- Returns:
109
- IRSpectrum: IRSpectrum with spectral data from file
110
- """
111
-
112
- data = _load_json(filepath)
113
- return cls(data["wavelength"], data["intensities"], ref=data["reference"])
114
-
115
- def convert_intensities_to_transmittance(self):
116
- """Converts intensities to transmittance"""
117
-
118
- self.transmittance = list(
119
- reversed(
120
- [
121
- (s / r) * 100
122
- for (s, r) in zip(self.intensities, self.reference["intensities"])
123
- ]
124
- )
125
- )
126
-
127
- def convert_wavelength_to_wavenumber(self):
128
- """Converts Wavelengths to Wavenumbers"""
129
-
130
- self.wavenumbers = list(reversed([(10**7) / nm for nm in self.wavelengths]))
131
-
132
- def plot_spectrum(
133
- self, display: bool = False, savepath: str = "", limits: tuple = ()
134
- ):
135
- """Plots the spectral data
136
-
137
- Args:
138
- display (bool, optional): Display the spectrum on screen. Defaults to False.
139
- savepath (str, optional): Path to save the spectrum graph to disk. Defaults to "".
140
- """
141
-
142
- # Clear any previous plots/data
143
- plt.clf()
144
- plt.cla()
145
-
146
- # TODO::GAK -- Implement limit checks
147
-
148
- # Already have a reference set, measured spectrum
149
- if self.reference:
150
- plt.xlabel("Wavenumber (cm-1)")
151
- plt.ylabel("Transmittance (%)")
152
- plt.title("IR Spectrum")
153
- plt.plot(self.wavenumbers, self.transmittance, color="black", linewidth=0.5)
154
-
155
- # No reference set, reference spectrum
156
- else:
157
- plt.xlabel("Wavelength (nm)")
158
- plt.ylabel("Intensities")
159
- plt.title("Reference IR Spectrum")
160
- plt.plot(self.wavelengths, self.intensities, color="black", linewidth=0.5)
161
-
162
- # Set limits after plotting
163
- plt.gca().set_ylim(bottom=0)
164
-
165
- # Save plot to disk
166
- if savepath:
167
- plt.savefig(savepath)
168
-
169
- # Display the plot on screen
170
- if display:
171
- plt.show()
172
-
173
- def dump_spectrum(self, filename: str):
174
- """Dump spectral data out to disk
175
-
176
- Args:
177
- filename (str): Path to save file (JSON)
178
- """
179
-
180
- out = {
181
- "wavenumbers": self.wavenumbers,
182
- "transmittance": self.transmittance,
183
- "wavelength": self.wavelengths,
184
- "intensities": self.intensities,
185
- "reference": self.reference,
186
- "timestamp": time.strftime("%d_%m_%Y_%H:%M:%S"),
187
- }
188
-
189
- out = _ensure_serializable(out)
190
-
191
- _write_json(out, filename)
@@ -1,46 +0,0 @@
1
- """Module for interfacing with OceanOptics Raman spectrometer
2
-
3
- .. moduleauthor:: Artem Leonov, Graham Keenan
4
- """
5
- import time
6
- import logging
7
-
8
- from ..oceanoptics import OceanOpticsSpectrometer
9
- from .raman_spectrum import RamanSpectrum
10
-
11
-
12
- class OceanOpticsRaman(OceanOpticsSpectrometer):
13
- """Operational class for interfacing with the OceanOptics Raman spectrometer
14
-
15
- Inherits:
16
- OceanOpticsSpectrometer: Base Spectrometer class
17
- """
18
-
19
- def __init__(self, name=None, path=None):
20
- super().__init__("RAMAN", name)
21
- self.last_spectrum = None
22
- self.spectrum = RamanSpectrum(path)
23
- self.start_time = time.time()
24
- self.logger = logging.getLogger("oceanoptics.spectrometer.raman")
25
-
26
- def get_spectrum(self):
27
- """Obtains spectrum and performs basic processing"""
28
-
29
- spec = self.scan()
30
- timestamp = round((time.time() - self.start_time), 2)
31
- self.logger.debug("Spectrum obtained at %s", timestamp)
32
- self.spectrum.load_spectrum(spec, timestamp, reference=False)
33
- # self.last_spectrum = self.spectrum.default_process()
34
- # return np.array(
35
- # [
36
- # self.last_spectrum['wavelengths'],
37
- # self.last_spectrum['intensities']
38
- # ]
39
- # )
40
-
41
- def obtain_reference_spectrum(self):
42
- """Obtains the reference spectrum for further use"""
43
-
44
- spec = self.scan()
45
- self.start_time = time.time()
46
- self.spectrum.load_spectrum(spec, 0, reference=True)
@@ -1,148 +0,0 @@
1
- """
2
- Module for handling Raman spectroscopic data
3
-
4
- .. moduleauthor:: Artem Leonov, Graham Keenan
5
- """
6
- import logging
7
- import os
8
-
9
- import numpy as np
10
- from scipy import signal
11
-
12
- from ....analysis import AbstractSpectrum
13
- from ....analysis.utils import interpolate_to_index
14
-
15
- LASER_POWER = 785
16
-
17
- MIN_X = 780
18
- MAX_X = 1006
19
-
20
-
21
- def _convert_wavelength_to_wavenumber(data):
22
- """Converts x from spectrometer to Raman shift in wavenumbers
23
-
24
- Arguments:
25
- data (iterable): Wavelength data to convert
26
-
27
- Returns:
28
- (:obj: np.array): Wavenumbers data
29
- """
30
-
31
- wavenumbers = [(10**7 / LASER_POWER) - (10**7 / wv) for wv in data]
32
-
33
- return np.array(wavenumbers)
34
-
35
-
36
- class RamanSpectrum(AbstractSpectrum):
37
- """Defines methods for Raman spectroscopic data handling
38
-
39
- Args:
40
- path(str, optional): Valid path to save the spectral data.
41
- If not provided, uses .//raman_data
42
- """
43
-
44
- AXIS_MAPPING = {
45
- "x": "wavelength",
46
- "y": "intensities",
47
- }
48
-
49
- INTERNAL_PROPERTIES = {
50
- "reference",
51
- "original",
52
- "baseline",
53
- }
54
-
55
- def __init__(self, path=None, autosaving=True):
56
-
57
- if path is not None:
58
- path = os.path.join(".", "raman_data")
59
-
60
- self.logger = logging.getLogger("oceanoptics.spectrometer.raman.spectrum")
61
-
62
- super().__init__(path, autosaving)
63
-
64
- def find_peaks_iteratively(self, limit=10, steps=100):
65
- """Finds all peaks iteratively moving the threshold
66
-
67
- Args:
68
- limit (int): Max number of peaks found at each iteration to stop
69
- after.
70
- steps (int): Max number of iterations.
71
- """
72
-
73
- gradient = np.linspace(self.y.max(), self.y.min(), steps)
74
- pl = [
75
- 0,
76
- ] # peaks length
77
-
78
- # Looking for peaks and decreasing height
79
- for i, n in enumerate(gradient):
80
- peaks, _ = signal.find_peaks(self.y, height=n)
81
- pl.append(len(peaks))
82
- diff = pl[-1] - pl[-2]
83
- if diff > limit:
84
- self.logger.debug(
85
- "Stopped at iteration %s, with height %s, \
86
- diff - %s",
87
- i + 1,
88
- n,
89
- diff,
90
- )
91
- break
92
-
93
- # Final peaks at previous iteration
94
- peaks, _ = signal.find_peaks(self.y, height=gradient[i - 1])
95
-
96
- # Updating widths
97
- pw = signal.peak_widths(self.y, peaks, rel_height=0.95)
98
- peak_xs = self.x.copy()[peaks][:, np.newaxis]
99
- peak_ys = self.y.copy()[peaks][:, np.newaxis]
100
- peaks_ids = np.around(peak_xs)
101
- peaks_left_ids = interpolate_to_index(self.x, pw[2])[:, np.newaxis]
102
- peaks_right_ids = interpolate_to_index(self.x, pw[3])[:, np.newaxis]
103
-
104
- # Packing for peak array
105
- self.peaks = np.hstack(
106
- (
107
- peaks_ids,
108
- peak_xs,
109
- peak_ys,
110
- peaks_left_ids,
111
- peaks_right_ids,
112
- )
113
- )
114
-
115
- return peaks_ids
116
-
117
- def load_spectrum(self, spectrum, timestamp, reference=False):
118
- """Loads the spectral data
119
-
120
- Args:
121
- spectrum (tuple): Tuple containing spectrum x and y as numpy arrays.
122
- Example: (array(x), array(y))
123
- timestamp (float): time.time() for the measured spectra
124
- reference (bool, optional): True if the supplied spectra should be
125
- stored as a reference (background)
126
- """
127
-
128
- super().load_spectrum(spectrum[0], spectrum[1], timestamp)
129
- self.original = spectrum[1]
130
- if reference:
131
- self.reference = spectrum[1]
132
-
133
- def subtract_reference(self):
134
- """Subtracts reference spectrum and updates the current one"""
135
-
136
- if self.reference is None:
137
- raise ValueError("Please upload the reference first")
138
-
139
- self.y -= self.reference
140
-
141
- def default_processing(self):
142
- """Dummy method for quick processing. Returns spectral data!"""
143
-
144
- self.correct_baseline()
145
- self.smooth_spectrum()
146
- self.find_peaks_iteratively()
147
-
148
- return self.x, self.y, self.timestamp
@@ -1,90 +0,0 @@
1
- """
2
- .. module:: QEPro2192
3
- :synopsis: Module representing the UV QEPro2192 spectrometer
4
- :platforms: Unix, Windows
5
-
6
- .. moduleauthor:: Graham Keenan (Cronin Lab 2020)
7
-
8
- .. note:: Feel free to edit. This is a skeleton for minimal functionality.
9
-
10
- """
11
-
12
- import json
13
- from pathlib import Path
14
- from .uv_spectrum import UVSpectrum
15
- from typing import Optional, Union, Dict
16
- from ..oceanoptics import OceanOpticsSpectrometer
17
-
18
-
19
- class NoReferenceException(Exception):
20
- """Exception for calling spectrum without a reference"""
21
-
22
-
23
- class QEPro2192(OceanOpticsSpectrometer):
24
- """Class representing the QEPro2192 spectrometer
25
-
26
- Inherits:
27
- OceanOpticsSpectrometer
28
- """
29
-
30
- def __init__(self):
31
- super().__init__("UV", name="QEPro2192 UV Spectrometer")
32
- self.reference = {}
33
- self.__ref_called = False
34
-
35
- def load_reference(self, ref: Union[str, Dict]):
36
- """Loads a pre-existing reference from disk or from dict.
37
-
38
- Args:
39
- ref (Union[str, Dict]): Reference as either a dictionary
40
- or JSON filepath
41
- """
42
-
43
- # Filepath, load and set
44
- if isinstance(ref, str) or isinstance(ref, Path):
45
- with open(ref) as fd:
46
- self.reference = json.load(fd)
47
- self.__ref_called = True
48
-
49
- # Dict, set
50
- elif isinstance(ref, dict):
51
- self.reference = ref
52
- self.__ref_called = True
53
-
54
- # Not supported
55
- else:
56
- self.logger.warning(f"Reference {ref} is unsupported. Not loading")
57
-
58
- def obtain_reference_spectrum(self) -> UVSpectrum:
59
- """Obtain a reference spectrum.
60
-
61
- Returns:
62
- UVSpectrum: Reference UV spectrum
63
- """
64
-
65
- wavelengths, intensities = self.scan()
66
- self.reference["wavelength"] = wavelengths
67
- self.reference["intensities"] = intensities
68
- self.__ref_called = True
69
-
70
- return UVSpectrum(wavelengths, intensities)
71
-
72
- def obtain_spectrum(self) -> UVSpectrum:
73
- """Obtain a UV spectrum of a sample.
74
-
75
- Raises:
76
- NoReferenceException: Attempting to measure a sample without
77
- a reference
78
-
79
- Returns:
80
- UVSpectrum: Sample UV spectrum.
81
- """
82
-
83
- if not self.__ref_called:
84
- raise NoReferenceException(
85
- "Attempting to call a spectrum without a valid reference\
86
- spectrum"
87
- )
88
-
89
- wavelengths, intensities = self.scan()
90
- return UVSpectrum(wavelengths, intensities, ref=self.reference)
@@ -1,227 +0,0 @@
1
- """
2
- .. module:: uv_spectrum
3
- :synopsis: Module representing a UV spectrum
4
- :platforms: Unix, Windows
5
-
6
- .. moduleauthor:: Graham Keenan (Cronin Lab 2020)
7
-
8
- .. note:: Feel free to edit. This is a skeletal implementation.
9
-
10
- """
11
-
12
- import time
13
- import json
14
- import numpy as np
15
- import matplotlib.pyplot as plt
16
-
17
-
18
- def _write_json(data: dict, filename: str):
19
- """Writes out data to a JSON file
20
-
21
- Args:
22
- data (dict): Data to write
23
- filename (str): Path to save to
24
- """
25
-
26
- with open(filename, "w") as f_d:
27
- json.dump(data, f_d, indent=4)
28
-
29
-
30
- def _load_json(filename: str) -> dict:
31
- """Load JSON file from disk
32
-
33
- Args:
34
- filename (str): Path to JSON file
35
-
36
- Returns:
37
- dict: JSON data
38
- """
39
-
40
- with open(filename) as f_d:
41
- return json.load(f_d)
42
-
43
-
44
- def _ensure_serializable(data: dict) -> dict:
45
- """Ensures the output data is serializable
46
-
47
- Args:
48
- data (Dict): Data to check
49
-
50
- Returns:
51
- Dict: Serializable data
52
- """
53
-
54
- for k, v in data.items():
55
- if isinstance(v, np.ndarray):
56
- data[k] = v.tolist()
57
- elif isinstance(v, dict):
58
- data[k] = _ensure_serializable(v)
59
- return data
60
-
61
-
62
- def trim_data(data: list, lower_range: int, upper_range: int) -> np.ndarray:
63
- """Trims data within a certain range
64
- Returns valid indexes that can be used to trim X and Y data
65
-
66
- Args:
67
- data (list): Data to trim within the range
68
- lower_range (int): Lower end of the range (Minimum value)
69
- upper_range (int): Upper end of the range (Maximum value)
70
-
71
- Returns:
72
- np.ndarray: Indexes to use for trimming data
73
- """
74
-
75
- above_ind = np.array(data) > lower_range
76
- below_ind = np.array(data) < upper_range
77
-
78
- return np.logical_and(above_ind, below_ind)
79
-
80
-
81
- class UVSpectrum:
82
- """Class representing a UV spectrum.
83
- Spectrum can be a reference spectrum or measured spectrum.
84
-
85
- Args:
86
- wavelengths (list): Wavelengths of the spectrum
87
- intensities (list): Intensities of the spectrum
88
- ref (dict): Reference spectrum data (Defaults to {})
89
-
90
- """
91
-
92
- def __init__(self, wavelengths: list, intensities: list, ref: dict = {}):
93
- self.wavelengths = np.array(wavelengths)
94
- self.intensities = np.array(intensities)
95
- self.reference = ref
96
- self.absorbances = []
97
- self.max_peak = 0
98
-
99
- # If we have a reference, generate absorbances using Beer-Lambert relation
100
- if self.reference:
101
- self.beer_lambert()
102
-
103
- @classmethod
104
- def load_spectrum(cls, filepath: str):
105
- """Class method for loading spectrum data from file
106
-
107
- Args:
108
- filepath (str): Path to spectrum JSON file
109
-
110
- Returns:
111
- UVSpectrum: UVSpectrum instance
112
- """
113
-
114
- data = _load_json(filepath)
115
- return cls(data["wavelength"], data["intensities"], ref=data["reference"])
116
-
117
- def beer_lambert(self):
118
- """Beer-Lambert relation.
119
- Converts intensities to absorbances for a UV spectrum
120
- """
121
-
122
- for ref, measured in zip(self.reference["intensities"], self.intensities):
123
- try:
124
- if ref == 0 or measured == 0:
125
- continue
126
- self.absorbances.append(np.log10(ref / measured))
127
- except Exception:
128
- break
129
-
130
- # Set absorbances
131
- self.absorbances = np.array(self.absorbances)
132
-
133
- # Find the maximum peak
134
- self.max_peak = self.wavelengths[np.argmax(self.absorbances)]
135
-
136
- def plot_spectrum(
137
- self,
138
- display: bool = False,
139
- legend: bool = False,
140
- savepath: str = "",
141
- limits: tuple = (),
142
- ):
143
- """Plot spectrum.
144
-
145
- Args:
146
- display (bool, optional): Display the spectra on screen. Defaults to False.
147
- legend (bool, optional): Display a legend or not for maximum peak. Defaults to False.
148
- savepath (str, optional): Path to save the image to. Defaults to "".
149
- limits (tuple, optional): Trims data within a certain range. Defaults to ().
150
- """
151
-
152
- # Clear any previous plots/data
153
- plt.clf()
154
- plt.cla()
155
-
156
- # Set X label
157
- plt.xlabel("Wavelength (nm)")
158
-
159
- # Trim data within a certain range
160
- if limits:
161
- trimmed = trim_data(self.wavelengths, *limits)
162
- self.wavelengths = self.wavelengths[trimmed]
163
- self.intensities = self.intensities[trimmed]
164
-
165
- # Trim absorbances if they're present
166
- if len(self.absorbances) > 0:
167
- self.absorbances = self.absorbances[trimmed]
168
-
169
- # Display different labels for Measured spectrum
170
- # We have a reference spectrum already, means this is a measured sample
171
- if self.reference:
172
- plt.ylabel("Absorbances")
173
- plt.title("UV/Vis Reference Spectrum")
174
- plt.plot(self.wavelengths, self.absorbances, color="black", linewidth=0.5)
175
-
176
- # Display different labels for Reference
177
- # No reference means this should be a reference
178
- else:
179
- plt.ylabel("Intensities")
180
- plt.title("UV/Vis Spectrum")
181
- plt.plot(self.wavelengths, self.intensities, color="black", linewidth=0.5)
182
-
183
- # Display legend
184
- if legend:
185
- leg_label = f"Maximum Peak: {self.max_peak:.2f}nm"
186
- plt.vlines(
187
- x=self.max_peak,
188
- ymin=0,
189
- ymax=max(self.absorbances),
190
- color="g",
191
- zorder=3,
192
- label=leg_label,
193
- )
194
- leg = plt.legend()
195
- for legobj in leg.legendHandles:
196
- legobj.set_linewidth(5.0)
197
-
198
- # Set limits after plotting
199
- plt.gca().set_ylim(bottom=0)
200
-
201
- # Save if savepath is defined
202
- if savepath:
203
- plt.savefig(savepath)
204
-
205
- # SHow the spectrum on screen
206
- if display:
207
- plt.show()
208
-
209
- def dump_spectrum(self, filename: str):
210
- """Dump spectrum data to JSON file
211
-
212
- Args:
213
- filename (str): Path to save JSON file
214
- """
215
-
216
- out = {
217
- "wavelength": self.wavelengths,
218
- "absorbances": self.absorbances,
219
- "intensities": self.intensities,
220
- "reference": self.reference,
221
- "max_peak": self.max_peak,
222
- "timestamp": time.strftime("%d_%m_%Y_%H:%M:%S"),
223
- }
224
-
225
- out = _ensure_serializable(out)
226
-
227
- _write_json(out, filename)
File without changes