ChessAnalysisPipeline 0.0.4__py3-none-any.whl → 0.0.6__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.
Potentially problematic release.
This version of ChessAnalysisPipeline might be problematic. Click here for more details.
- CHAP/TaskManager.py +214 -0
- CHAP/common/models/__init__.py +0 -2
- CHAP/common/models/integration.py +392 -249
- CHAP/common/models/map.py +350 -198
- CHAP/common/processor.py +229 -191
- CHAP/common/reader.py +52 -39
- CHAP/common/utils/__init__.py +0 -37
- CHAP/common/utils/fit.py +1197 -991
- CHAP/common/utils/general.py +629 -372
- CHAP/common/utils/material.py +158 -121
- CHAP/common/utils/scanparsers.py +735 -339
- CHAP/common/writer.py +31 -25
- CHAP/edd/models.py +65 -51
- CHAP/edd/processor.py +136 -113
- CHAP/edd/reader.py +1 -1
- CHAP/edd/writer.py +1 -1
- CHAP/inference/processor.py +35 -28
- CHAP/inference/reader.py +1 -1
- CHAP/inference/writer.py +1 -1
- CHAP/pipeline.py +14 -28
- CHAP/processor.py +44 -75
- CHAP/reader.py +49 -40
- CHAP/runner.py +73 -32
- CHAP/saxswaxs/processor.py +1 -1
- CHAP/saxswaxs/reader.py +1 -1
- CHAP/saxswaxs/writer.py +1 -1
- CHAP/server.py +130 -0
- CHAP/sin2psi/processor.py +1 -1
- CHAP/sin2psi/reader.py +1 -1
- CHAP/sin2psi/writer.py +1 -1
- CHAP/tomo/__init__.py +1 -4
- CHAP/tomo/models.py +53 -31
- CHAP/tomo/processor.py +1326 -902
- CHAP/tomo/reader.py +4 -2
- CHAP/tomo/writer.py +4 -2
- CHAP/writer.py +47 -41
- {ChessAnalysisPipeline-0.0.4.dist-info → ChessAnalysisPipeline-0.0.6.dist-info}/METADATA +1 -1
- ChessAnalysisPipeline-0.0.6.dist-info/RECORD +52 -0
- ChessAnalysisPipeline-0.0.4.dist-info/RECORD +0 -50
- {ChessAnalysisPipeline-0.0.4.dist-info → ChessAnalysisPipeline-0.0.6.dist-info}/LICENSE +0 -0
- {ChessAnalysisPipeline-0.0.4.dist-info → ChessAnalysisPipeline-0.0.6.dist-info}/WHEEL +0 -0
- {ChessAnalysisPipeline-0.0.4.dist-info → ChessAnalysisPipeline-0.0.6.dist-info}/entry_points.txt +0 -0
- {ChessAnalysisPipeline-0.0.4.dist-info → ChessAnalysisPipeline-0.0.6.dist-info}/top_level.txt +0 -0
CHAP/common/utils/scanparsers.py
CHANGED
|
@@ -3,29 +3,40 @@
|
|
|
3
3
|
# -*- coding: utf-8 -*-
|
|
4
4
|
|
|
5
5
|
# system modules
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
from
|
|
9
|
-
import json
|
|
6
|
+
from csv import reader
|
|
7
|
+
from fnmatch import filter as fnmatch_filter
|
|
8
|
+
from json import load
|
|
10
9
|
import os
|
|
11
10
|
import re
|
|
12
11
|
|
|
13
|
-
#
|
|
12
|
+
# third party modules
|
|
14
13
|
import numpy as np
|
|
15
|
-
from pyspec.file.spec import FileSpec
|
|
14
|
+
from pyspec.file.spec import FileSpec
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ScanParser:
|
|
18
|
+
"""Partial implementation of a class representing a SPEC scan and
|
|
19
|
+
some of its metadata.
|
|
20
|
+
|
|
21
|
+
:param spec_file_name: path to a SPEC file on the CLASSE DAQ
|
|
22
|
+
:type spec_file_name: str
|
|
23
|
+
:param scan_number: the number of a scan in the SPEC file provided
|
|
24
|
+
with `spec_file_name`
|
|
25
|
+
:type scan_number: int
|
|
26
|
+
"""
|
|
16
27
|
|
|
17
|
-
class ScanParser(object):
|
|
18
28
|
def __init__(self,
|
|
19
29
|
spec_file_name:str,
|
|
20
30
|
scan_number:int):
|
|
31
|
+
"""Constructor method"""
|
|
21
32
|
|
|
22
33
|
self.spec_file_name = spec_file_name
|
|
23
34
|
self.scan_number = scan_number
|
|
24
|
-
|
|
35
|
+
|
|
25
36
|
self._scan_path = None
|
|
26
37
|
self._scan_name = None
|
|
27
38
|
self._scan_title = None
|
|
28
|
-
|
|
39
|
+
|
|
29
40
|
self._spec_scan = None
|
|
30
41
|
self._spec_command = None
|
|
31
42
|
self._spec_macro = None
|
|
@@ -33,93 +44,162 @@ class ScanParser(object):
|
|
|
33
44
|
self._spec_scan_npts = None
|
|
34
45
|
self._spec_scan_data = None
|
|
35
46
|
self._spec_positioner_values = None
|
|
36
|
-
|
|
47
|
+
|
|
37
48
|
self._detector_data_path = None
|
|
38
|
-
|
|
49
|
+
|
|
39
50
|
def __repr__(self):
|
|
40
|
-
return(f'{self.__class__.__name__}
|
|
41
|
-
|
|
51
|
+
return (f'{self.__class__.__name__}'
|
|
52
|
+
f'({self.spec_file_name}, {self.scan_number}) '
|
|
53
|
+
f'-- {self.spec_command}')
|
|
54
|
+
|
|
42
55
|
@property
|
|
43
56
|
def spec_file(self):
|
|
44
|
-
# NB This FileSpec instance is not stored as a private
|
|
45
|
-
# it cannot be pickled (and therefore could
|
|
46
|
-
# parallel code that uses ScanParsers).
|
|
47
|
-
return
|
|
57
|
+
# NB This FileSpec instance is not stored as a private
|
|
58
|
+
# attribute because it cannot be pickled (and therefore could
|
|
59
|
+
# cause problems for parallel code that uses ScanParsers).
|
|
60
|
+
return FileSpec(self.spec_file_name)
|
|
61
|
+
|
|
48
62
|
@property
|
|
49
63
|
def scan_path(self):
|
|
50
64
|
if self._scan_path is None:
|
|
51
65
|
self._scan_path = self.get_scan_path()
|
|
52
66
|
return self._scan_path
|
|
67
|
+
|
|
53
68
|
@property
|
|
54
69
|
def scan_name(self):
|
|
55
70
|
if self._scan_name is None:
|
|
56
71
|
self._scan_name = self.get_scan_name()
|
|
57
72
|
return self._scan_name
|
|
73
|
+
|
|
58
74
|
@property
|
|
59
75
|
def scan_title(self):
|
|
60
76
|
if self._scan_title is None:
|
|
61
77
|
self._scan_title = self.get_scan_title()
|
|
62
78
|
return self._scan_title
|
|
79
|
+
|
|
63
80
|
@property
|
|
64
81
|
def spec_scan(self):
|
|
65
82
|
if self._spec_scan is None:
|
|
66
83
|
self._spec_scan = self.get_spec_scan()
|
|
67
84
|
return self._spec_scan
|
|
85
|
+
|
|
68
86
|
@property
|
|
69
87
|
def spec_command(self):
|
|
70
88
|
if self._spec_command is None:
|
|
71
89
|
self._spec_command = self.get_spec_command()
|
|
72
90
|
return self._spec_command
|
|
91
|
+
|
|
73
92
|
@property
|
|
74
93
|
def spec_macro(self):
|
|
75
94
|
if self._spec_macro is None:
|
|
76
95
|
self._spec_macro = self.get_spec_macro()
|
|
77
96
|
return self._spec_macro
|
|
97
|
+
|
|
78
98
|
@property
|
|
79
99
|
def spec_args(self):
|
|
80
100
|
if self._spec_args is None:
|
|
81
101
|
self._spec_args = self.get_spec_args()
|
|
82
102
|
return self._spec_args
|
|
103
|
+
|
|
83
104
|
@property
|
|
84
105
|
def spec_scan_npts(self):
|
|
85
106
|
if self._spec_scan_npts is None:
|
|
86
107
|
self._spec_scan_npts = self.get_spec_scan_npts()
|
|
87
108
|
return self._spec_scan_npts
|
|
109
|
+
|
|
88
110
|
@property
|
|
89
111
|
def spec_scan_data(self):
|
|
90
112
|
if self._spec_scan_data is None:
|
|
91
113
|
self._spec_scan_data = self.get_spec_scan_data()
|
|
92
114
|
return self._spec_scan_data
|
|
115
|
+
|
|
93
116
|
@property
|
|
94
117
|
def spec_positioner_values(self):
|
|
95
118
|
if self._spec_positioner_values is None:
|
|
96
119
|
self._spec_positioner_values = self.get_spec_positioner_values()
|
|
97
120
|
return self._spec_positioner_values
|
|
121
|
+
|
|
98
122
|
@property
|
|
99
123
|
def detector_data_path(self):
|
|
100
124
|
if self._detector_data_path is None:
|
|
101
125
|
self._detector_data_path = self.get_detector_data_path()
|
|
102
126
|
return self._detector_data_path
|
|
103
|
-
|
|
127
|
+
|
|
104
128
|
def get_scan_path(self):
|
|
105
|
-
|
|
129
|
+
"""Return the name of the directory containining the SPEC file
|
|
130
|
+
for this scan.
|
|
131
|
+
|
|
132
|
+
:rtype: str
|
|
133
|
+
"""
|
|
134
|
+
return os.path.dirname(self.spec_file_name)
|
|
135
|
+
|
|
106
136
|
def get_scan_name(self):
|
|
107
|
-
|
|
137
|
+
"""Return the name of this SPEC scan (not unique to scans
|
|
138
|
+
within a single spec file).
|
|
139
|
+
|
|
140
|
+
:rtype: str
|
|
141
|
+
"""
|
|
142
|
+
raise NotImplementedError
|
|
143
|
+
|
|
108
144
|
def get_scan_title(self):
|
|
109
|
-
|
|
145
|
+
"""Return the title of this spec scan (unique to each scan
|
|
146
|
+
within a spec file).
|
|
147
|
+
|
|
148
|
+
:rtype: str
|
|
149
|
+
"""
|
|
150
|
+
raise NotImplementedError
|
|
151
|
+
|
|
110
152
|
def get_spec_scan(self):
|
|
111
|
-
|
|
153
|
+
"""Return the `pyspec.file.spec.Scan` object parsed from the
|
|
154
|
+
spec file and scan number provided to the constructor.
|
|
155
|
+
|
|
156
|
+
:rtype: pyspec.file.spec.Scan
|
|
157
|
+
"""
|
|
158
|
+
return self.spec_file.getScanByNumber(self.scan_number)
|
|
159
|
+
|
|
112
160
|
def get_spec_command(self):
|
|
113
|
-
|
|
161
|
+
"""Return the string command of this SPEC scan.
|
|
162
|
+
|
|
163
|
+
:rtype: str
|
|
164
|
+
"""
|
|
165
|
+
return self.spec_scan.command
|
|
166
|
+
|
|
114
167
|
def get_spec_macro(self):
|
|
115
|
-
|
|
168
|
+
"""Return the macro used in this scan's SPEC command.
|
|
169
|
+
|
|
170
|
+
:rtype: str
|
|
171
|
+
"""
|
|
172
|
+
return self.spec_command.split()[0]
|
|
173
|
+
|
|
116
174
|
def get_spec_args(self):
|
|
117
|
-
|
|
175
|
+
"""Return a list of the arguments provided to the macro for
|
|
176
|
+
this SPEC scan.
|
|
177
|
+
|
|
178
|
+
:rtype: list[str]
|
|
179
|
+
"""
|
|
180
|
+
return self.spec_command.split()[1:]
|
|
181
|
+
|
|
118
182
|
def get_spec_scan_npts(self):
|
|
119
|
-
|
|
183
|
+
"""Return the number of points collected in this SPEC scan
|
|
184
|
+
|
|
185
|
+
:rtype: int
|
|
186
|
+
"""
|
|
187
|
+
raise NotImplementedError
|
|
188
|
+
|
|
120
189
|
def get_spec_scan_data(self):
|
|
121
|
-
|
|
190
|
+
"""Return a dictionary of all the counter data collected by
|
|
191
|
+
this SPEC scan.
|
|
192
|
+
|
|
193
|
+
:rtype: dict[str, numpy.ndarray]
|
|
194
|
+
"""
|
|
195
|
+
return dict(zip(self.spec_scan.labels, self.spec_scan.data.T))
|
|
196
|
+
|
|
122
197
|
def get_spec_positioner_values(self):
|
|
198
|
+
"""Return a dictionary of all the SPEC positioner values
|
|
199
|
+
recorded by SPEC just before the scan began.
|
|
200
|
+
|
|
201
|
+
:rtype: dict[str,str]
|
|
202
|
+
"""
|
|
123
203
|
positioner_values = dict(self.spec_scan.motor_positions)
|
|
124
204
|
names = list(positioner_values.keys())
|
|
125
205
|
mnemonics = self.spec_scan.motors
|
|
@@ -127,94 +207,145 @@ class ScanParser(object):
|
|
|
127
207
|
for name,mnemonic in zip(names,mnemonics):
|
|
128
208
|
if name != mnemonic:
|
|
129
209
|
positioner_values[mnemonic] = positioner_values[name]
|
|
130
|
-
return
|
|
210
|
+
return positioner_values
|
|
211
|
+
|
|
131
212
|
def get_detector_data_path(self):
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
def get_detector_data_file(self, detector_prefix, scan_step_index:int):
|
|
135
|
-
raise(NotImplementedError)
|
|
136
|
-
def get_detector_data(self, detector_prefix, scan_step_index:int):
|
|
137
|
-
'''
|
|
138
|
-
Return a np.ndarray of detector data.
|
|
213
|
+
"""Return the name of the directory containing detector data
|
|
214
|
+
collected by this scan.
|
|
139
215
|
|
|
140
|
-
:
|
|
141
|
-
|
|
142
|
-
|
|
216
|
+
:rtype: str
|
|
217
|
+
"""
|
|
218
|
+
raise NotImplementedError
|
|
219
|
+
|
|
220
|
+
def get_detector_data_file(self, detector_prefix, scan_step_index:int):
|
|
221
|
+
"""Return the name of the file containing detector data
|
|
222
|
+
collected at a certain step of this scan.
|
|
143
223
|
|
|
144
|
-
:param
|
|
145
|
-
|
|
224
|
+
:param detector_prefix: the prefix used in filenames for the
|
|
225
|
+
detector
|
|
226
|
+
:type detector_prefix: str
|
|
227
|
+
:param scan_step_index: the index of the point in this scan
|
|
228
|
+
whose detector file name should be returned.
|
|
146
229
|
:type scan_step_index: int
|
|
230
|
+
:rtype: str
|
|
231
|
+
"""
|
|
232
|
+
raise NotImplementedError
|
|
147
233
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
234
|
+
def get_detector_data(self, detector_prefix, scan_step_index:int):
|
|
235
|
+
"""Return the detector data collected at a certain step of
|
|
236
|
+
this scan.
|
|
237
|
+
|
|
238
|
+
:param detector_prefix: the prefix used in filenames for the
|
|
239
|
+
detector
|
|
240
|
+
:type detector_prefix: str
|
|
241
|
+
:param scan_step_index: the index of the point in this scan
|
|
242
|
+
whose detector data should be returned.
|
|
243
|
+
:type scan_step_index: int
|
|
244
|
+
:rtype: numpy.ndarray
|
|
245
|
+
"""
|
|
246
|
+
raise NotImplementedError
|
|
152
247
|
|
|
153
248
|
def get_spec_positioner_value(self, positioner_name):
|
|
249
|
+
"""Return the value of a spec positioner recorded before this
|
|
250
|
+
scan began.
|
|
251
|
+
|
|
252
|
+
:param positioner_name: the name or mnemonic of a SPEC motor
|
|
253
|
+
whose position should be returned.
|
|
254
|
+
:raises KeyError: if `positioner_name` is not the name or
|
|
255
|
+
mnemonic of a SPEC motor recorded for this scan.
|
|
256
|
+
:raises ValueError: if the recorded string value of the
|
|
257
|
+
positioner in the SPEC file cannot be converted to a
|
|
258
|
+
float.
|
|
259
|
+
:rtype: float
|
|
260
|
+
"""
|
|
154
261
|
try:
|
|
155
262
|
positioner_value = self.spec_positioner_values[positioner_name]
|
|
156
263
|
positioner_value = float(positioner_value)
|
|
157
|
-
return(positioner_value)
|
|
158
264
|
except KeyError:
|
|
159
|
-
raise
|
|
265
|
+
raise KeyError(f'{self.scan_title}: motor {positioner_name} '
|
|
266
|
+
'not found for this scan')
|
|
160
267
|
except ValueError:
|
|
161
|
-
raise
|
|
268
|
+
raise ValueError(f'{self.scan_title}: could not convert value of'
|
|
269
|
+
f' {positioner_name} to float: '
|
|
270
|
+
f'{positioner_value}')
|
|
271
|
+
return positioner_value
|
|
162
272
|
|
|
163
273
|
|
|
164
274
|
class FMBScanParser(ScanParser):
|
|
165
|
-
|
|
166
|
-
|
|
275
|
+
"""Partial implementation of a class representing a SPEC scan
|
|
276
|
+
collected at FMB.
|
|
277
|
+
"""
|
|
278
|
+
|
|
167
279
|
def get_scan_name(self):
|
|
168
|
-
return
|
|
169
|
-
def get_scan_title(self):
|
|
170
|
-
return(f'{self.scan_name}_{self.scan_number:03d}')
|
|
280
|
+
return os.path.basename(self.spec_file.abspath)
|
|
171
281
|
|
|
282
|
+
def get_scan_title(self):
|
|
283
|
+
return f'{self.scan_name}_{self.scan_number:03d}'
|
|
172
284
|
|
|
173
285
|
|
|
174
286
|
class SMBScanParser(ScanParser):
|
|
287
|
+
"""Partial implementation of a class representing a SPEC scan
|
|
288
|
+
collected at SMB or FAST.
|
|
289
|
+
"""
|
|
290
|
+
|
|
175
291
|
def __init__(self, spec_file_name, scan_number):
|
|
176
292
|
super().__init__(spec_file_name, scan_number)
|
|
177
|
-
|
|
178
|
-
self._pars = None
|
|
293
|
+
|
|
294
|
+
self._pars = None
|
|
179
295
|
self.par_file_pattern = f'*-*-{self.scan_name}'
|
|
180
|
-
|
|
296
|
+
|
|
181
297
|
def get_scan_name(self):
|
|
182
|
-
return
|
|
298
|
+
return os.path.basename(self.scan_path)
|
|
299
|
+
|
|
183
300
|
def get_scan_title(self):
|
|
184
|
-
return
|
|
185
|
-
|
|
301
|
+
return f'{self.scan_name}_{self.scan_number}'
|
|
302
|
+
|
|
186
303
|
@property
|
|
187
304
|
def pars(self):
|
|
188
305
|
if self._pars is None:
|
|
189
306
|
self._pars = self.get_pars()
|
|
190
|
-
return
|
|
191
|
-
|
|
307
|
+
return self._pars
|
|
308
|
+
|
|
192
309
|
def get_pars(self):
|
|
310
|
+
"""Return a dictionary of values recorded in the .par file
|
|
311
|
+
associated with this SPEC scan.
|
|
312
|
+
|
|
313
|
+
:rtype: dict[str,object]
|
|
314
|
+
"""
|
|
193
315
|
# JSON file holds titles for columns in the par file
|
|
194
|
-
json_files =
|
|
195
|
-
|
|
196
|
-
|
|
316
|
+
json_files = fnmatch_filter(
|
|
317
|
+
os.listdir(self.scan_path),
|
|
318
|
+
f'{self.par_file_pattern}.json')
|
|
319
|
+
if len(json_files) != 1:
|
|
320
|
+
raise RuntimeError(f'{self.scan_title}: cannot find the '
|
|
321
|
+
'.json file to decode the .par file')
|
|
197
322
|
with open(os.path.join(self.scan_path, json_files[0])) as json_file:
|
|
198
|
-
par_file_cols =
|
|
323
|
+
par_file_cols = load(json_file)
|
|
199
324
|
try:
|
|
200
325
|
par_col_names = list(par_file_cols.values())
|
|
201
326
|
scann_val_idx = par_col_names.index('SCAN_N')
|
|
202
327
|
scann_col_idx = int(list(par_file_cols.keys())[scann_val_idx])
|
|
203
328
|
except:
|
|
204
|
-
raise
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
329
|
+
raise RuntimeError(f'{self.scan_title}: cannot find scan pars '
|
|
330
|
+
'without a "SCAN_N" column in the par file')
|
|
331
|
+
|
|
332
|
+
par_files = fnmatch_filter(
|
|
333
|
+
os.listdir(self.scan_path),
|
|
334
|
+
f'{self.par_file_pattern}.par')
|
|
335
|
+
if len(par_files) != 1:
|
|
336
|
+
raise RuntimeError(f'{self.scan_title}: cannot find the .par '
|
|
337
|
+
'file for this scan directory')
|
|
338
|
+
par_dict = None
|
|
209
339
|
with open(os.path.join(self.scan_path, par_files[0])) as par_file:
|
|
210
|
-
par_reader =
|
|
340
|
+
par_reader = reader(par_file, delimiter=' ')
|
|
211
341
|
for row in par_reader:
|
|
212
342
|
if len(row) == len(par_col_names):
|
|
213
343
|
row_scann = int(row[scann_col_idx])
|
|
214
344
|
if row_scann == self.scan_number:
|
|
215
345
|
par_dict = {}
|
|
216
346
|
for par_col_idx,par_col_name in par_file_cols.items():
|
|
217
|
-
# Convert the string par value from the
|
|
347
|
+
# Convert the string par value from the
|
|
348
|
+
# file to an int or float, if possible.
|
|
218
349
|
par_value = row[int(par_col_idx)]
|
|
219
350
|
try:
|
|
220
351
|
par_value = int(par_value)
|
|
@@ -224,27 +355,49 @@ class SMBScanParser(ScanParser):
|
|
|
224
355
|
except:
|
|
225
356
|
pass
|
|
226
357
|
par_dict[par_col_name] = par_value
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
358
|
+
|
|
359
|
+
if par_dict is None:
|
|
360
|
+
raise RuntimeError(f'{self.scan_title}: could not find scan pars '
|
|
361
|
+
'for scan number {self.scan_number}')
|
|
362
|
+
return par_dict
|
|
363
|
+
|
|
230
364
|
def get_counter_gain(self, counter_name):
|
|
365
|
+
"""Return the gain of a counter as recorded in the comments of
|
|
366
|
+
a scan in a SPEC file converted to nA/V.
|
|
367
|
+
|
|
368
|
+
:param counter_name: the name of the counter
|
|
369
|
+
:type counter_name: str
|
|
370
|
+
:rtype: str
|
|
371
|
+
"""
|
|
372
|
+
counter_gain = None
|
|
231
373
|
for comment in self.spec_scan.comments:
|
|
232
|
-
match = re.search(
|
|
374
|
+
match = re.search(
|
|
375
|
+
f'{counter_name} gain: ' # start of counter gain comments
|
|
376
|
+
'(?P<gain_value>\d+) ' # gain numerical value
|
|
377
|
+
'(?P<unit_prefix>[m|u|n])A/V', # gain units
|
|
378
|
+
comment)
|
|
233
379
|
if match:
|
|
234
380
|
unit_prefix = match['unit_prefix']
|
|
235
|
-
gain_scalar = 1 if unit_prefix == 'n'
|
|
381
|
+
gain_scalar = 1 if unit_prefix == 'n' \
|
|
382
|
+
else 1e3 if unit_prefix == 'u' else 1e6
|
|
236
383
|
counter_gain = f'{float(match["gain_value"])*gain_scalar} nA/V'
|
|
237
|
-
|
|
238
|
-
|
|
384
|
+
|
|
385
|
+
if counter_gain is None:
|
|
386
|
+
raise RuntimeError(f'{self.scan_title}: could not get gain for '
|
|
387
|
+
f'counter {counter_name}')
|
|
388
|
+
return counter_gain
|
|
239
389
|
|
|
240
390
|
|
|
241
391
|
class LinearScanParser(ScanParser):
|
|
392
|
+
"""Partial implementation of a class representing a typical line
|
|
393
|
+
or mesh scan in SPEC.
|
|
394
|
+
"""
|
|
242
395
|
def __init__(self, spec_file_name, scan_number):
|
|
243
396
|
super().__init__(spec_file_name, scan_number)
|
|
244
|
-
|
|
397
|
+
|
|
245
398
|
self._spec_scan_motor_mnes = None
|
|
246
399
|
self._spec_scan_motor_vals = None
|
|
247
|
-
self._spec_scan_shape = None
|
|
400
|
+
self._spec_scan_shape = None
|
|
248
401
|
self._spec_scan_dwell = None
|
|
249
402
|
|
|
250
403
|
@property
|
|
@@ -252,38 +405,107 @@ class LinearScanParser(ScanParser):
|
|
|
252
405
|
if self._spec_scan_motor_mnes is None:
|
|
253
406
|
self._spec_scan_motor_mnes = self.get_spec_scan_motor_mnes()
|
|
254
407
|
return self._spec_scan_motor_mnes
|
|
408
|
+
|
|
255
409
|
@property
|
|
256
410
|
def spec_scan_motor_vals(self):
|
|
257
411
|
if self._spec_scan_motor_vals is None:
|
|
258
412
|
self._spec_scan_motor_vals = self.get_spec_scan_motor_vals()
|
|
259
413
|
return self._spec_scan_motor_vals
|
|
414
|
+
|
|
260
415
|
@property
|
|
261
416
|
def spec_scan_shape(self):
|
|
262
417
|
if self._spec_scan_shape is None:
|
|
263
418
|
self._spec_scan_shape = self.get_spec_scan_shape()
|
|
264
419
|
return self._spec_scan_shape
|
|
420
|
+
|
|
265
421
|
@property
|
|
266
422
|
def spec_scan_dwell(self):
|
|
267
423
|
if self._spec_scan_dwell is None:
|
|
268
424
|
self._spec_scan_dwell = self.get_spec_scan_dwell()
|
|
269
|
-
return
|
|
425
|
+
return self._spec_scan_dwell
|
|
426
|
+
|
|
427
|
+
def get_spec_scan_motor_mnes(self):
|
|
428
|
+
"""Return the mnemonics of the SPEC motor(s) provided to the
|
|
429
|
+
macro for this scan. If there is more than one motor scanned
|
|
430
|
+
(in a "flymesh" scan, for example), the order of motors in the
|
|
431
|
+
returned tuple will go from the fastest moving motor first to
|
|
432
|
+
the slowest moving motor last.
|
|
433
|
+
|
|
434
|
+
:rtype: tuple
|
|
435
|
+
"""
|
|
436
|
+
raise NotImplementedError
|
|
270
437
|
|
|
271
|
-
def get_spec_scan_motor_names(self):
|
|
272
|
-
raise(NotImplementedError)
|
|
273
438
|
def get_spec_scan_motor_vals(self):
|
|
274
|
-
|
|
439
|
+
"""Return the values visited by each of the scanned motors. If
|
|
440
|
+
there is more than one motor scanned (in a "flymesh" scan, for
|
|
441
|
+
example), the order of motor values in the returned tuple will
|
|
442
|
+
go from the fastest moving motor's values first to the slowest
|
|
443
|
+
moving motor's values last.
|
|
444
|
+
|
|
445
|
+
:rtype: tuple
|
|
446
|
+
"""
|
|
447
|
+
raise NotImplementedError
|
|
448
|
+
|
|
275
449
|
def get_spec_scan_shape(self):
|
|
276
|
-
|
|
450
|
+
"""Return the number of points visited by each of the scanned
|
|
451
|
+
motors. If there is more than one motor scanned (in a
|
|
452
|
+
"flymesh" scan, for example), the order of number of motor
|
|
453
|
+
values in the returned tuple will go from the number of points
|
|
454
|
+
visited by the fastest moving motor first to the the number of
|
|
455
|
+
points visited by the slowest moving motor last.
|
|
456
|
+
|
|
457
|
+
:rtype: tuple
|
|
458
|
+
"""
|
|
459
|
+
raise NotImplementedError
|
|
460
|
+
|
|
461
|
+
def get_spec_scan_dwell(self):
|
|
462
|
+
"""Return the dwell time for each point in the scan as it
|
|
463
|
+
appears in the command string.
|
|
464
|
+
|
|
465
|
+
:rtype: float
|
|
466
|
+
"""
|
|
467
|
+
raise NotImplementedError
|
|
468
|
+
|
|
277
469
|
def get_spec_scan_npts(self):
|
|
278
|
-
|
|
470
|
+
"""Return the number of points collected in this SPEC scan.
|
|
471
|
+
|
|
472
|
+
:rtype: int
|
|
473
|
+
"""
|
|
474
|
+
return np.prod(self.spec_scan_shape)
|
|
475
|
+
|
|
279
476
|
def get_scan_step(self, scan_step_index:int):
|
|
477
|
+
"""Return the index of each motor coordinate corresponding to
|
|
478
|
+
the index of a single point in the scan. If there is more than
|
|
479
|
+
one motor scanned (in a "flymesh" scan, for example), the
|
|
480
|
+
order of indices in the returned tuple will go from the index
|
|
481
|
+
of the value of the fastest moving motor first to the index of
|
|
482
|
+
the value of the slowest moving motor last.
|
|
483
|
+
|
|
484
|
+
:param scan_step_index: the index of a single point in the
|
|
485
|
+
scan.
|
|
486
|
+
:type scan_step_index: int
|
|
487
|
+
:rtype: tuple
|
|
488
|
+
"""
|
|
280
489
|
scan_steps = np.ndindex(self.spec_scan_shape[::-1])
|
|
281
490
|
i = 0
|
|
282
491
|
while i <= scan_step_index:
|
|
283
492
|
scan_step = next(scan_steps)
|
|
284
493
|
i += 1
|
|
285
|
-
return
|
|
494
|
+
return scan_step
|
|
495
|
+
|
|
286
496
|
def get_scan_step_index(self, scan_step:tuple):
|
|
497
|
+
"""Return the index of a single scan point corresponding to a
|
|
498
|
+
tuple of indices for each scanned motor coordinate.
|
|
499
|
+
|
|
500
|
+
:param scan_step: a tuple of the indices of each scanned motor
|
|
501
|
+
coordinate. If there is more than one motor scanned (in a
|
|
502
|
+
"flymesh" scan, for example), the order of indices should
|
|
503
|
+
go from the index of the value of the fastest moving motor
|
|
504
|
+
first to the index of the value of the slowest moving
|
|
505
|
+
motor last.
|
|
506
|
+
:type scan_step: tuple
|
|
507
|
+
:trype: int
|
|
508
|
+
"""
|
|
287
509
|
scan_steps = np.ndindex(self.spec_scan_shape[::-1])
|
|
288
510
|
scan_step_found = False
|
|
289
511
|
scan_step_index = -1
|
|
@@ -293,166 +515,223 @@ class LinearScanParser(ScanParser):
|
|
|
293
515
|
if next_scan_step == scan_step:
|
|
294
516
|
scan_step_found = True
|
|
295
517
|
break
|
|
296
|
-
return
|
|
518
|
+
return scan_step_index
|
|
297
519
|
|
|
298
520
|
|
|
299
521
|
class FMBLinearScanParser(LinearScanParser, FMBScanParser):
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
522
|
+
"""Partial implementation of a class representing a typical line
|
|
523
|
+
or mesh scan in SPEC collected at FMB.
|
|
524
|
+
"""
|
|
525
|
+
|
|
303
526
|
def get_spec_scan_motor_mnes(self):
|
|
304
527
|
if self.spec_macro == 'flymesh':
|
|
305
|
-
return(
|
|
306
|
-
|
|
307
|
-
return(
|
|
308
|
-
|
|
309
|
-
return(
|
|
310
|
-
|
|
311
|
-
|
|
528
|
+
return (self.spec_args[0], self.spec_args[5])
|
|
529
|
+
if self.spec_macro == 'flyscan':
|
|
530
|
+
return (self.spec_args[0],)
|
|
531
|
+
if self.spec_macro in ('tseries', 'loopscan'):
|
|
532
|
+
return ('Time',)
|
|
533
|
+
raise RuntimeError(f'{self.scan_title}: cannot determine scan motors '
|
|
534
|
+
f'for scans of type {self.spec_macro}')
|
|
535
|
+
|
|
312
536
|
def get_spec_scan_motor_vals(self):
|
|
313
537
|
if self.spec_macro == 'flymesh':
|
|
314
|
-
fast_mot_vals = np.linspace(float(self.spec_args[1]),
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
538
|
+
fast_mot_vals = np.linspace(float(self.spec_args[1]),
|
|
539
|
+
float(self.spec_args[2]),
|
|
540
|
+
int(self.spec_args[3])+1)
|
|
541
|
+
slow_mot_vals = np.linspace(float(self.spec_args[6]),
|
|
542
|
+
float(self.spec_args[7]),
|
|
543
|
+
int(self.spec_args[8])+1)
|
|
544
|
+
return (fast_mot_vals, slow_mot_vals)
|
|
545
|
+
if self.spec_macro == 'flyscan':
|
|
546
|
+
mot_vals = np.linspace(float(self.spec_args[1]),
|
|
547
|
+
float(self.spec_args[2]),
|
|
548
|
+
int(self.spec_args[3])+1)
|
|
549
|
+
return (mot_vals,)
|
|
550
|
+
if self.spec_macro in ('tseries', 'loopscan'):
|
|
551
|
+
return self.spec_scan.data[:,0]
|
|
552
|
+
raise RuntimeError(f'{self.scan_title}: cannot determine scan motors '
|
|
553
|
+
f'for scans of type {self.spec_macro}')
|
|
554
|
+
|
|
324
555
|
def get_spec_scan_shape(self):
|
|
325
556
|
if self.spec_macro == 'flymesh':
|
|
326
557
|
fast_mot_npts = int(self.spec_args[3])+1
|
|
327
558
|
slow_mot_npts = int(self.spec_args[8])+1
|
|
328
|
-
return(
|
|
329
|
-
|
|
559
|
+
return (fast_mot_npts, slow_mot_npts)
|
|
560
|
+
if self.spec_macro == 'flyscan':
|
|
330
561
|
mot_npts = int(self.spec_args[3])+1
|
|
331
|
-
return(
|
|
332
|
-
|
|
333
|
-
return
|
|
334
|
-
|
|
335
|
-
|
|
562
|
+
return (mot_npts,)
|
|
563
|
+
if self.spec_macro in ('tseries', 'loopscan'):
|
|
564
|
+
return len(np.array(self.spec_scan.data[:,0]))
|
|
565
|
+
raise RuntimeError(f'{self.scan_title}: cannot determine scan shape '
|
|
566
|
+
f'for scans of type {self.spec_macro}')
|
|
567
|
+
|
|
336
568
|
def get_spec_scan_dwell(self):
|
|
337
569
|
if self.spec_macro in ('flymesh', 'flyscan'):
|
|
338
|
-
return
|
|
339
|
-
|
|
340
|
-
return
|
|
341
|
-
|
|
342
|
-
|
|
570
|
+
return float(self.spec_args[4])
|
|
571
|
+
if self.spec_macro in ('tseries', 'loopscan'):
|
|
572
|
+
return float(self.spec_args[1])
|
|
573
|
+
raise RuntimeError(f'{self.scan_title}: cannot determine dwell for '
|
|
574
|
+
f'scans of type {self.spec_macro}')
|
|
575
|
+
|
|
343
576
|
def get_detector_data_path(self):
|
|
344
|
-
return
|
|
577
|
+
return os.path.join(self.scan_path, self.scan_title)
|
|
345
578
|
|
|
346
579
|
|
|
347
580
|
class FMBSAXSWAXSScanParser(FMBLinearScanParser):
|
|
348
|
-
|
|
349
|
-
|
|
581
|
+
"""Concrete implementation of a class representing a scan taken
|
|
582
|
+
with the typical SAXS/WAXS setup at FMB.
|
|
583
|
+
"""
|
|
350
584
|
|
|
351
585
|
def get_scan_title(self):
|
|
352
|
-
return
|
|
586
|
+
return f'{self.scan_name}_{self.scan_number:03d}'
|
|
587
|
+
|
|
353
588
|
def get_detector_data_file(self, detector_prefix, scan_step_index:int):
|
|
354
589
|
scan_step = self.get_scan_step(scan_step_index)
|
|
355
|
-
file_indices = [f'{scan_step[i]:03d}'
|
|
356
|
-
|
|
590
|
+
file_indices = [f'{scan_step[i]:03d}'
|
|
591
|
+
for i in range(len(self.spec_scan_shape))
|
|
592
|
+
if self.spec_scan_shape[i] != 1]
|
|
593
|
+
file_name = f'{self.scan_name}_{detector_prefix}_' \
|
|
594
|
+
f'{self.scan_number:03d}_{"_".join(file_indices)}.tiff'
|
|
357
595
|
file_name_full = os.path.join(self.detector_data_path, file_name)
|
|
358
596
|
if os.path.isfile(file_name_full):
|
|
359
|
-
return
|
|
360
|
-
|
|
361
|
-
|
|
597
|
+
return file_name_full
|
|
598
|
+
raise RuntimeError(f'{self.scan_title}: could not find detector image '
|
|
599
|
+
f'file for detector {detector_prefix} scan step '
|
|
600
|
+
f'({scan_step})')
|
|
601
|
+
|
|
362
602
|
def get_detector_data(self, detector_prefix, scan_step_index:int):
|
|
603
|
+
# third party modules
|
|
363
604
|
from pyspec.file.tiff import TiffFile
|
|
364
|
-
|
|
605
|
+
|
|
606
|
+
image_file = self.get_detector_data_file(detector_prefix,
|
|
607
|
+
scan_step_index)
|
|
365
608
|
with TiffFile(image_file) as tiff_file:
|
|
366
609
|
image_data = tiff_file.asarray()
|
|
367
|
-
return
|
|
610
|
+
return image_data
|
|
368
611
|
|
|
369
612
|
|
|
370
613
|
class FMBXRFScanParser(FMBLinearScanParser):
|
|
371
|
-
|
|
372
|
-
|
|
614
|
+
"""Concrete implementation of a class representing a scan taken
|
|
615
|
+
with the typical XRF setup at FMB.
|
|
616
|
+
"""
|
|
617
|
+
|
|
373
618
|
def get_scan_title(self):
|
|
374
|
-
return
|
|
619
|
+
return f'{self.scan_name}_scan{self.scan_number}'
|
|
620
|
+
|
|
375
621
|
def get_detector_data_file(self, detector_prefix, scan_step_index:int):
|
|
376
622
|
scan_step = self.get_scan_step(scan_step_index)
|
|
377
623
|
file_name = f'scan{self.scan_number}_{scan_step[1]:03d}.hdf5'
|
|
378
624
|
file_name_full = os.path.join(self.detector_data_path, file_name)
|
|
379
625
|
if os.path.isfile(file_name_full):
|
|
380
|
-
return
|
|
381
|
-
|
|
382
|
-
|
|
626
|
+
return file_name_full
|
|
627
|
+
raise RuntimeError(f'{self.scan_title}: could not find detector image '
|
|
628
|
+
f'file for detector {detector_prefix} scan step '
|
|
629
|
+
f'({scan_step_index})')
|
|
630
|
+
|
|
383
631
|
def get_detector_data(self, detector_prefix, scan_step_index:int):
|
|
384
|
-
|
|
385
|
-
|
|
632
|
+
# third party modules
|
|
633
|
+
from h5py import File
|
|
634
|
+
|
|
635
|
+
detector_file = self.get_detector_data_file(
|
|
636
|
+
detector_prefix, scan_step_index)
|
|
386
637
|
scan_step = self.get_scan_step(scan_step_index)
|
|
387
|
-
with
|
|
388
|
-
detector_data =
|
|
389
|
-
|
|
638
|
+
with File(detector_file) as h5_file:
|
|
639
|
+
detector_data = \
|
|
640
|
+
h5_file['/entry/instrument/detector/data'][scan_step[0]]
|
|
641
|
+
return detector_data
|
|
390
642
|
|
|
391
643
|
|
|
392
644
|
class SMBLinearScanParser(LinearScanParser, SMBScanParser):
|
|
393
|
-
|
|
394
|
-
|
|
645
|
+
"""Concrete implementation of a class representing a scan taken
|
|
646
|
+
with the typical powder diffraction setup at SMB.
|
|
647
|
+
"""
|
|
648
|
+
|
|
395
649
|
def get_spec_scan_motor_mnes(self):
|
|
396
650
|
if self.spec_macro == 'flymesh':
|
|
397
|
-
return(
|
|
398
|
-
|
|
399
|
-
return(
|
|
400
|
-
|
|
401
|
-
return(
|
|
402
|
-
|
|
403
|
-
|
|
651
|
+
return (self.spec_args[0], self.spec_args[5])
|
|
652
|
+
if self.spec_macro == 'flyscan':
|
|
653
|
+
return (self.spec_args[0],)
|
|
654
|
+
if self.spec_macro in ('tseries', 'loopscan'):
|
|
655
|
+
return ('Time',)
|
|
656
|
+
raise RuntimeError(f'{self.scan_title}: cannot determine scan motors '
|
|
657
|
+
f'for scans of type {self.spec_macro}')
|
|
658
|
+
|
|
404
659
|
def get_spec_scan_motor_vals(self):
|
|
405
660
|
if self.spec_macro == 'flymesh':
|
|
406
|
-
fast_mot_vals = np.linspace(float(self.spec_args[1]),
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
661
|
+
fast_mot_vals = np.linspace(float(self.spec_args[1]),
|
|
662
|
+
float(self.spec_args[2]),
|
|
663
|
+
int(self.spec_args[3])+1)
|
|
664
|
+
slow_mot_vals = np.linspace(float(self.spec_args[6]),
|
|
665
|
+
float(self.spec_args[7]),
|
|
666
|
+
int(self.spec_args[8])+1)
|
|
667
|
+
return (fast_mot_vals, slow_mot_vals)
|
|
668
|
+
if self.spec_macro == 'flyscan':
|
|
669
|
+
mot_vals = np.linspace(float(self.spec_args[1]),
|
|
670
|
+
float(self.spec_args[2]),
|
|
671
|
+
int(self.spec_args[3])+1)
|
|
672
|
+
return (mot_vals,)
|
|
673
|
+
if self.spec_macro in ('tseries', 'loopscan'):
|
|
674
|
+
return self.spec_scan.data[:,0]
|
|
675
|
+
raise RuntimeError(f'{self.scan_title}: cannot determine scan motors '
|
|
676
|
+
f'for scans of type {self.spec_macro}')
|
|
677
|
+
|
|
416
678
|
def get_spec_scan_shape(self):
|
|
417
679
|
if self.spec_macro == 'flymesh':
|
|
418
680
|
fast_mot_npts = int(self.spec_args[3])+1
|
|
419
681
|
slow_mot_npts = int(self.spec_args[8])+1
|
|
420
|
-
return(
|
|
421
|
-
|
|
682
|
+
return (fast_mot_npts, slow_mot_npts)
|
|
683
|
+
if self.spec_macro == 'flyscan':
|
|
422
684
|
mot_npts = int(self.spec_args[3])+1
|
|
423
|
-
return(
|
|
424
|
-
|
|
425
|
-
return
|
|
426
|
-
|
|
427
|
-
|
|
685
|
+
return (mot_npts,)
|
|
686
|
+
if self.spec_macro in ('tseries', 'loopscan'):
|
|
687
|
+
return len(np.array(self.spec_scan.data[:,0]))
|
|
688
|
+
raise RuntimeError(f'{self.scan_title}: cannot determine scan shape '
|
|
689
|
+
f'for scans of type {self.spec_macro}')
|
|
690
|
+
|
|
428
691
|
def get_spec_scan_dwell(self):
|
|
429
692
|
if self.spec_macro == 'flymesh':
|
|
430
|
-
return
|
|
431
|
-
|
|
432
|
-
return
|
|
433
|
-
|
|
434
|
-
|
|
693
|
+
return float(self.spec_args[4])
|
|
694
|
+
if self.spec_macro == 'flyscan':
|
|
695
|
+
return float(self.spec_args[-1])
|
|
696
|
+
raise RuntimeError(f'{self.scan_title}: cannot determine dwell time '
|
|
697
|
+
f'for scans of type {self.spec_macro}')
|
|
698
|
+
|
|
435
699
|
def get_detector_data_path(self):
|
|
436
|
-
return
|
|
700
|
+
return os.path.join(self.scan_path, str(self.scan_number))
|
|
701
|
+
|
|
437
702
|
def get_detector_data_file(self, detector_prefix, scan_step_index:int):
|
|
438
703
|
scan_step = self.get_scan_step(scan_step_index)
|
|
439
704
|
if len(scan_step) == 1:
|
|
440
705
|
scan_step = (0, *scan_step)
|
|
441
|
-
file_name_pattern = f'{detector_prefix}_
|
|
442
|
-
|
|
706
|
+
file_name_pattern = (f'{detector_prefix}_'
|
|
707
|
+
f'{self.scan_name}_*_'
|
|
708
|
+
f'{scan_step[0]}_data_'
|
|
709
|
+
f'{(scan_step[1]+1):06d}.h5')
|
|
710
|
+
file_name_matches = fnmatch_filter(
|
|
711
|
+
os.listdir(self.detector_data_path),
|
|
712
|
+
file_name_pattern)
|
|
443
713
|
if len(file_name_matches) == 1:
|
|
444
|
-
return
|
|
445
|
-
|
|
446
|
-
|
|
714
|
+
return os.path.join(self.detector_data_path, file_name_matches[0])
|
|
715
|
+
raise RuntimeError(f'{self.scan_title}: could not find detector image '
|
|
716
|
+
f'file for detector {detector_prefix} scan step '
|
|
717
|
+
f'({scan_step_index})')
|
|
718
|
+
|
|
447
719
|
def get_detector_data(self, detector_prefix, scan_step_index:int):
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
720
|
+
# third party modules
|
|
721
|
+
from h5py import File
|
|
722
|
+
|
|
723
|
+
image_file = self.get_detector_data_file(
|
|
724
|
+
detector_prefix, scan_step_index)
|
|
725
|
+
with File(image_file) as h5_file:
|
|
451
726
|
image_data = h5_file['/entry/data/data'][0]
|
|
452
|
-
return
|
|
727
|
+
return image_data
|
|
453
728
|
|
|
454
729
|
|
|
455
730
|
class RotationScanParser(ScanParser):
|
|
731
|
+
"""Partial implementation of a class representing a rotation
|
|
732
|
+
scan.
|
|
733
|
+
"""
|
|
734
|
+
|
|
456
735
|
def __init__(self, spec_file_name, scan_number):
|
|
457
736
|
super().__init__(spec_file_name, scan_number)
|
|
458
737
|
|
|
@@ -467,291 +746,407 @@ class RotationScanParser(ScanParser):
|
|
|
467
746
|
def scan_type(self):
|
|
468
747
|
if self._scan_type is None:
|
|
469
748
|
self._scan_type = self.get_scan_type()
|
|
470
|
-
return
|
|
749
|
+
return self._scan_type
|
|
750
|
+
|
|
471
751
|
@property
|
|
472
752
|
def theta_vals(self):
|
|
473
753
|
if self._theta_vals is None:
|
|
474
754
|
self._theta_vals = self.get_theta_vals()
|
|
475
|
-
return
|
|
755
|
+
return self._theta_vals
|
|
756
|
+
|
|
476
757
|
@property
|
|
477
758
|
def horizontal_shift(self):
|
|
478
759
|
if self._horizontal_shift is None:
|
|
479
760
|
self._horizontal_shift = self.get_horizontal_shift()
|
|
480
|
-
return
|
|
761
|
+
return self._horizontal_shift
|
|
762
|
+
|
|
481
763
|
@property
|
|
482
764
|
def vertical_shift(self):
|
|
483
765
|
if self._vertical_shift is None:
|
|
484
766
|
self._vertical_shift = self.get_vertical_shift()
|
|
485
|
-
return
|
|
767
|
+
return self._vertical_shift
|
|
768
|
+
|
|
486
769
|
@property
|
|
487
770
|
def starting_image_index(self):
|
|
488
771
|
if self._starting_image_index is None:
|
|
489
772
|
self._starting_image_index = self.get_starting_image_index()
|
|
490
|
-
return
|
|
773
|
+
return self._starting_image_index
|
|
774
|
+
|
|
491
775
|
@property
|
|
492
776
|
def starting_image_offset(self):
|
|
493
777
|
if self._starting_image_offset is None:
|
|
494
778
|
self._starting_image_offset = self.get_starting_image_offset()
|
|
495
|
-
return
|
|
496
|
-
|
|
779
|
+
return self._starting_image_offset
|
|
780
|
+
|
|
497
781
|
def get_scan_type(self):
|
|
498
|
-
|
|
782
|
+
"""Return a string identifier for the type of tomography data
|
|
783
|
+
being collected by this scan: df1 (dark field), bf1 (bright
|
|
784
|
+
field), or tf1 (sample tomography data).
|
|
785
|
+
|
|
786
|
+
:rtype: typing.Literal['df1', 'bf1', 'tf1']
|
|
787
|
+
"""
|
|
788
|
+
return None
|
|
789
|
+
|
|
499
790
|
def get_theta_vals(self):
|
|
500
|
-
|
|
791
|
+
"""Return a dictionary of information about the angular values
|
|
792
|
+
visited by the rotating motor at each point in the scan. The
|
|
793
|
+
dictionary may contain a single key, "num", or three keys:
|
|
794
|
+
"num", "start", and "end"
|
|
795
|
+
|
|
796
|
+
:rtype: dict[str, float]"""
|
|
797
|
+
raise NotImplementedError
|
|
798
|
+
|
|
501
799
|
def get_horizontal_shift(self):
|
|
502
|
-
|
|
800
|
+
"""Return the value of the motor that shifts the sample in the
|
|
801
|
+
+x direction (hutch frame). Useful when tomography scans are
|
|
802
|
+
taken in a series of stacks when the sample is wider than the
|
|
803
|
+
width of the beam.
|
|
804
|
+
|
|
805
|
+
:rtype: float
|
|
806
|
+
"""
|
|
807
|
+
raise NotImplementedError
|
|
808
|
+
|
|
503
809
|
def get_vertical_shift(self):
|
|
504
|
-
|
|
810
|
+
"""Return the value of the motor that shifts the sample in the
|
|
811
|
+
+z direction (hutch frame). Useful when tomography scans are
|
|
812
|
+
taken in a series of stacks when the sample is taller than the
|
|
813
|
+
height of the beam.
|
|
814
|
+
|
|
815
|
+
:rtype: float
|
|
816
|
+
"""
|
|
817
|
+
raise NotImplementedError
|
|
818
|
+
|
|
505
819
|
def get_starting_image_index(self):
|
|
506
|
-
|
|
820
|
+
"""Return the index of the first frame of detector data
|
|
821
|
+
collected by this scan.
|
|
822
|
+
|
|
823
|
+
:rtype: int
|
|
824
|
+
"""
|
|
825
|
+
raise NotImplementedError
|
|
826
|
+
|
|
507
827
|
def get_starting_image_offset(self):
|
|
508
|
-
|
|
828
|
+
"""Return the offet of the index of the first "good" frame of
|
|
829
|
+
detector data collected by this scan from the index of the
|
|
830
|
+
first frame of detector data collected by this scan.
|
|
831
|
+
|
|
832
|
+
:rtype: int
|
|
833
|
+
"""
|
|
834
|
+
raise NotImplementedError
|
|
835
|
+
|
|
509
836
|
def get_num_image(self, detector_prefix):
|
|
510
|
-
|
|
837
|
+
"""Return the total number of "good" frames of detector data
|
|
838
|
+
collected by this scan
|
|
839
|
+
|
|
840
|
+
:rtype: int
|
|
841
|
+
"""
|
|
842
|
+
raise NotImplementedError
|
|
511
843
|
|
|
512
844
|
|
|
513
845
|
class FMBRotationScanParser(RotationScanParser, FMBScanParser):
|
|
514
|
-
|
|
515
|
-
|
|
846
|
+
"""Concrete implementation of a class representing a scan taken
|
|
847
|
+
with the typical tomography setup at FMB.
|
|
848
|
+
"""
|
|
849
|
+
|
|
516
850
|
def get_spec_scan_npts(self):
|
|
517
851
|
if self.spec_macro == 'flyscan':
|
|
518
852
|
if len(self.spec_args) == 2:
|
|
519
853
|
# Flat field (dark or bright)
|
|
520
|
-
return
|
|
521
|
-
|
|
522
|
-
return
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
854
|
+
return int(self.spec_args[0])+1
|
|
855
|
+
if len(self.spec_args) == 5:
|
|
856
|
+
return int(self.spec_args[3])+1
|
|
857
|
+
raise RuntimeError(f'{self.scan_title}: cannot obtain number of '
|
|
858
|
+
f'points from {self.spec_macro} with '
|
|
859
|
+
f'arguments {self.spec_args}')
|
|
860
|
+
raise RuntimeError(f'{self.scan_title}: cannot determine number of '
|
|
861
|
+
f'points for scans of type {self.spec_macro}')
|
|
862
|
+
|
|
529
863
|
def get_theta_vals(self):
|
|
530
864
|
if self.spec_macro == 'flyscan':
|
|
531
865
|
if len(self.spec_args) == 2:
|
|
532
866
|
# Flat field (dark or bright)
|
|
533
|
-
return
|
|
534
|
-
|
|
535
|
-
return
|
|
536
|
-
'
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
867
|
+
return {'num': int(self.spec_args[0])}
|
|
868
|
+
if len(self.spec_args) == 5:
|
|
869
|
+
return {'start': float(self.spec_args[1]),
|
|
870
|
+
'end': float(self.spec_args[2]),
|
|
871
|
+
'num': int(self.spec_args[3])+1}
|
|
872
|
+
raise RuntimeError(f'{self.scan_title}: cannot obtain theta values'
|
|
873
|
+
f' from {self.spec_macro} with arguments '
|
|
874
|
+
f'{self.spec_args}')
|
|
875
|
+
raise RuntimeError(f'{self.scan_title}: cannot determine theta values '
|
|
876
|
+
f'for scans of type {self.spec_macro}')
|
|
877
|
+
|
|
543
878
|
def get_horizontal_shift(self):
|
|
544
|
-
return
|
|
879
|
+
return 0.0
|
|
880
|
+
|
|
545
881
|
def get_vertical_shift(self):
|
|
546
|
-
return
|
|
882
|
+
return float(self.get_spec_positioner_value('4C_samz'))
|
|
883
|
+
|
|
547
884
|
def get_starting_image_index(self):
|
|
548
|
-
return
|
|
885
|
+
return 0
|
|
886
|
+
|
|
549
887
|
def get_starting_image_offset(self):
|
|
550
|
-
return
|
|
888
|
+
return 1
|
|
889
|
+
|
|
551
890
|
def get_num_image(self, detector_prefix):
|
|
552
|
-
|
|
891
|
+
# third party modules
|
|
892
|
+
from h5py import File
|
|
893
|
+
|
|
553
894
|
detector_file = self.get_detector_data_file(detector_prefix)
|
|
554
|
-
with
|
|
895
|
+
with File(detector_file) as h5_file:
|
|
555
896
|
num_image = h5_file['/entry/instrument/detector/data'].shape[0]
|
|
556
|
-
return
|
|
897
|
+
return num_image-self.starting_image_offset
|
|
898
|
+
|
|
557
899
|
def get_detector_data_path(self):
|
|
558
|
-
return
|
|
900
|
+
return self.scan_path
|
|
901
|
+
|
|
559
902
|
def get_detector_data_file(self, detector_prefix):
|
|
560
903
|
prefix = detector_prefix.upper()
|
|
561
904
|
file_name = f'{self.scan_name}_{prefix}_{self.scan_number:03d}.h5'
|
|
562
905
|
file_name_full = os.path.join(self.detector_data_path, file_name)
|
|
563
906
|
if os.path.isfile(file_name_full):
|
|
564
|
-
return
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
907
|
+
return file_name_full
|
|
908
|
+
raise RuntimeError(f'{self.scan_title}: could not find detector image '
|
|
909
|
+
f'file for detector {detector_prefix}')
|
|
910
|
+
|
|
911
|
+
def get_all_detector_data_in_file(
|
|
912
|
+
self, detector_prefix, scan_step_index=None):
|
|
913
|
+
# third party modules
|
|
914
|
+
from h5py import File
|
|
915
|
+
|
|
571
916
|
detector_file = self.get_detector_data_file(detector_prefix)
|
|
572
|
-
with
|
|
917
|
+
with File(detector_file) as h5_file:
|
|
573
918
|
if scan_step_index is None:
|
|
574
919
|
detector_data = h5_file['/entry/instrument/detector/data'][
|
|
575
|
-
|
|
920
|
+
self.starting_image_index:]
|
|
576
921
|
elif isinstance(scan_step_index, int):
|
|
577
922
|
detector_data = h5_file['/entry/instrument/detector/data'][
|
|
578
|
-
|
|
579
|
-
elif isinstance(scan_step_index, (list, tuple))
|
|
923
|
+
self.starting_image_index+scan_step_index]
|
|
924
|
+
elif (isinstance(scan_step_index, (list, tuple))
|
|
925
|
+
and len(scan_step_index) == 2):
|
|
580
926
|
detector_data = h5_file['/entry/instrument/detector/data'][
|
|
581
|
-
|
|
582
|
-
|
|
927
|
+
self.starting_image_index+scan_step_index[0]:
|
|
928
|
+
self.starting_image_index+scan_step_index[1]]
|
|
583
929
|
else:
|
|
584
|
-
raise
|
|
585
|
-
|
|
930
|
+
raise ValueError('Invalid parameter scan_step_index '
|
|
931
|
+
f'({scan_step_index})')
|
|
932
|
+
return detector_data
|
|
933
|
+
|
|
586
934
|
def get_detector_data(self, detector_prefix, scan_step_index=None):
|
|
587
|
-
return
|
|
935
|
+
return self.get_all_detector_data_in_file(
|
|
936
|
+
detector_prefix, scan_step_index)
|
|
588
937
|
|
|
589
938
|
|
|
590
939
|
class SMBRotationScanParser(RotationScanParser, SMBScanParser):
|
|
940
|
+
"""Concrete implementation of a class representing a scan taken
|
|
941
|
+
with the typical tomography setup at SMB.
|
|
942
|
+
"""
|
|
943
|
+
|
|
591
944
|
def __init__(self, spec_file_name, scan_number):
|
|
592
945
|
super().__init__(spec_file_name, scan_number)
|
|
946
|
+
|
|
593
947
|
self.par_file_pattern = f'id*-*tomo*-{self.scan_name}'
|
|
948
|
+
|
|
594
949
|
def get_spec_scan_npts(self):
|
|
595
|
-
if self.spec_macro
|
|
596
|
-
return
|
|
597
|
-
|
|
598
|
-
|
|
950
|
+
if self.spec_macro in ('slew_ome','rams4_slew_ome'):
|
|
951
|
+
return int(self.pars['nframes_real'])
|
|
952
|
+
raise RuntimeError(f'{self.scan_title}: cannot determine number of '
|
|
953
|
+
f'points for scans of type {self.spec_macro}')
|
|
954
|
+
|
|
599
955
|
def get_scan_type(self):
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
956
|
+
scan_type = self.pars.get('tomo_type',
|
|
957
|
+
self.pars.get('tomotype', None))
|
|
958
|
+
if scan_type is None:
|
|
959
|
+
raise RuntimeError(f'{self.scan_title}: cannot determine '
|
|
960
|
+
'the scan_type')
|
|
961
|
+
return scan_type
|
|
962
|
+
|
|
607
963
|
def get_theta_vals(self):
|
|
608
|
-
return
|
|
609
|
-
'end': float(self.pars['ome_end_real']),
|
|
964
|
+
return {'start': float(self.pars['ome_start_real']),
|
|
965
|
+
'end': float(self.pars['ome_end_real']),
|
|
966
|
+
'num': int(self.pars['nframes_real'])}
|
|
967
|
+
|
|
610
968
|
def get_horizontal_shift(self):
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
969
|
+
horizontal_shift = self.pars.get(
|
|
970
|
+
'rams4x', self.pars.get('ramsx', None))
|
|
971
|
+
if horizontal_shift is None:
|
|
972
|
+
raise RuntimeError(
|
|
973
|
+
f'{self.scan_title}: cannot determine the horizontal shift')
|
|
974
|
+
return horizontal_shift
|
|
975
|
+
|
|
618
976
|
def get_vertical_shift(self):
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
977
|
+
vertical_shift = self.pars.get(
|
|
978
|
+
'rams4z', self.pars.get('ramsz', None))
|
|
979
|
+
if vertical_shift is None:
|
|
980
|
+
raise RuntimeError(
|
|
981
|
+
f'{self.scan_title}: cannot determine the vertical shift')
|
|
982
|
+
return vertical_shift
|
|
983
|
+
|
|
626
984
|
def get_starting_image_index(self):
|
|
627
985
|
try:
|
|
628
|
-
return
|
|
986
|
+
return int(self.pars['junkstart'])
|
|
629
987
|
except:
|
|
630
|
-
raise
|
|
988
|
+
raise RuntimeError(f'{self.scan_title}: cannot determine first '
|
|
989
|
+
'detector image index')
|
|
990
|
+
|
|
631
991
|
def get_starting_image_offset(self):
|
|
632
992
|
try:
|
|
633
|
-
return(int(self.pars['goodstart'])
|
|
993
|
+
return (int(self.pars['goodstart'])
|
|
994
|
+
- self.get_starting_image_index())
|
|
634
995
|
except:
|
|
635
|
-
raise
|
|
636
|
-
|
|
996
|
+
raise RuntimeError(f'{self.scan_title}: cannot determine index '
|
|
997
|
+
'offset of first good detector image')
|
|
998
|
+
|
|
637
999
|
def get_num_image(self, detector_prefix=None):
|
|
638
1000
|
try:
|
|
639
|
-
return
|
|
640
|
-
#
|
|
1001
|
+
return int(self.pars['nframes_real'])
|
|
1002
|
+
# index_regex = re.compile(r'\d+')
|
|
641
1003
|
# # At this point only tiffs
|
|
642
1004
|
# path = self.get_detector_data_path()
|
|
643
|
-
# files = sorted([f for f in os.listdir(path)
|
|
644
|
-
#
|
|
645
|
-
#
|
|
1005
|
+
# files = sorted([f for f in os.listdir(path) \
|
|
1006
|
+
# if os.path.isfile(os.path.join(path, f)) \
|
|
1007
|
+
# and f.endswith('.tif') \
|
|
1008
|
+
# and index_regex.search(f)])
|
|
1009
|
+
# return len(files)-self.starting_image_offset
|
|
646
1010
|
except:
|
|
647
|
-
raise
|
|
648
|
-
|
|
1011
|
+
raise RuntimeError(f'{self.scan_title}: cannot determine the '
|
|
1012
|
+
'number of good detector images')
|
|
1013
|
+
|
|
649
1014
|
def get_detector_data_path(self):
|
|
650
|
-
return
|
|
1015
|
+
return os.path.join(self.scan_path, str(self.scan_number), 'nf')
|
|
1016
|
+
|
|
651
1017
|
def get_detector_data_file(self, scan_step_index:int):
|
|
652
1018
|
file_name = f'nf_{self.starting_image_index+scan_step_index:06d}.tif'
|
|
653
1019
|
file_name_full = os.path.join(self.detector_data_path, file_name)
|
|
654
1020
|
if os.path.isfile(file_name_full):
|
|
655
|
-
return
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
1021
|
+
return file_name_full
|
|
1022
|
+
raise RuntimeError(f'{self.scan_title}: could not find detector image '
|
|
1023
|
+
f'file for scan step ({scan_step_index})')
|
|
1024
|
+
|
|
659
1025
|
def get_detector_data(self, detector_prefix, scan_step_index=None):
|
|
660
1026
|
if scan_step_index is None:
|
|
661
1027
|
detector_data = []
|
|
662
1028
|
for index in range(len(self.get_num_image(detector_prefix))):
|
|
663
|
-
detector_data.append(
|
|
1029
|
+
detector_data.append(
|
|
1030
|
+
self.get_detector_data(detector_prefix, index))
|
|
664
1031
|
detector_data = np.asarray(detector_data)
|
|
665
1032
|
elif isinstance(scan_step_index, int):
|
|
666
|
-
|
|
1033
|
+
# third party modules
|
|
667
1034
|
from pyspec.file.tiff import TiffFile
|
|
1035
|
+
|
|
1036
|
+
image_file = self.get_detector_data_file(scan_step_index)
|
|
668
1037
|
with TiffFile(image_file) as tiff_file:
|
|
669
1038
|
detector_data = tiff_file.asarray()
|
|
670
|
-
elif isinstance(scan_step_index, (list, tuple))
|
|
1039
|
+
elif (isinstance(scan_step_index, (list, tuple))
|
|
1040
|
+
and len(scan_step_index) == 2):
|
|
671
1041
|
detector_data = []
|
|
672
1042
|
for index in range(scan_step_index[0], scan_step_index[1]):
|
|
673
|
-
detector_data.append(
|
|
1043
|
+
detector_data.append(
|
|
1044
|
+
self.get_detector_data(detector_prefix, index))
|
|
674
1045
|
detector_data = np.asarray(detector_data)
|
|
675
1046
|
else:
|
|
676
|
-
raise
|
|
677
|
-
|
|
1047
|
+
raise ValueError('Invalid parameter scan_step_index '
|
|
1048
|
+
f'({scan_step_index})')
|
|
1049
|
+
return detector_data
|
|
678
1050
|
|
|
679
1051
|
|
|
680
1052
|
class MCAScanParser(ScanParser):
|
|
1053
|
+
"""Partial implementation of a class representing a scan taken
|
|
1054
|
+
while collecting SPEC MCA data.
|
|
1055
|
+
"""
|
|
1056
|
+
|
|
681
1057
|
def __init__(self, spec_file_name, scan_number):
|
|
682
1058
|
super().__init__(spec_file_name, scan_number)
|
|
683
|
-
|
|
1059
|
+
|
|
684
1060
|
self._dwell_time = None
|
|
685
1061
|
self._detector_num_bins = None
|
|
686
|
-
|
|
1062
|
+
|
|
687
1063
|
@property
|
|
688
1064
|
def dwell_time(self):
|
|
689
1065
|
if self._dwell_time is None:
|
|
690
1066
|
self._dwell_time = self.get_dwell_time()
|
|
691
|
-
return
|
|
692
|
-
|
|
1067
|
+
return self._dwell_time
|
|
1068
|
+
|
|
693
1069
|
def get_dwell_time(self):
|
|
694
|
-
|
|
695
|
-
|
|
1070
|
+
"""Return the dwell time for each scan point as it appears in
|
|
1071
|
+
the SPEC command.
|
|
1072
|
+
|
|
1073
|
+
:rtype: float
|
|
1074
|
+
"""
|
|
1075
|
+
raise NotImplementedError
|
|
1076
|
+
|
|
696
1077
|
def get_detector_num_bins(self, detector_prefix):
|
|
697
|
-
|
|
1078
|
+
"""Return the number of bins for the detector with the given
|
|
1079
|
+
prefix.
|
|
1080
|
+
|
|
1081
|
+
:param detector_prefix: the detector prefix as used in SPEC
|
|
1082
|
+
MCA data files
|
|
1083
|
+
:type detector_prefix: str
|
|
1084
|
+
:rtype: int
|
|
1085
|
+
"""
|
|
1086
|
+
raise NotImplementedError
|
|
1087
|
+
|
|
698
1088
|
|
|
699
1089
|
class SMBMCAScanParser(MCAScanParser, SMBScanParser):
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
1090
|
+
"""Concrete implementation of a class representing a scan taken
|
|
1091
|
+
with the typical EDD setup at SMB or FAST.
|
|
1092
|
+
"""
|
|
1093
|
+
|
|
703
1094
|
def get_spec_scan_npts(self):
|
|
704
1095
|
if self.spec_macro == 'tseries':
|
|
705
|
-
return
|
|
706
|
-
|
|
707
|
-
return
|
|
708
|
-
|
|
709
|
-
return
|
|
710
|
-
|
|
711
|
-
|
|
1096
|
+
return 1
|
|
1097
|
+
if self.spec_macro == 'ascan':
|
|
1098
|
+
return int(self.spec_args[3])
|
|
1099
|
+
if self.spec_scan == 'wbslew_scan':
|
|
1100
|
+
return 1
|
|
1101
|
+
raise RuntimeError(f'{self.scan_title}: cannot determine number of '
|
|
1102
|
+
f'points for scans of type {self.spec_macro}')
|
|
712
1103
|
|
|
713
1104
|
def get_dwell_time(self):
|
|
714
1105
|
if self.spec_macro == 'tseries':
|
|
715
|
-
return
|
|
716
|
-
|
|
717
|
-
return
|
|
718
|
-
|
|
719
|
-
return
|
|
720
|
-
|
|
721
|
-
|
|
1106
|
+
return float(self.spec_args[1])
|
|
1107
|
+
if self.spec_macro == 'ascan':
|
|
1108
|
+
return float(self.spec_args[4])
|
|
1109
|
+
if self.spec_macro == 'wbslew_scan':
|
|
1110
|
+
return float(self.spec_args[3])
|
|
1111
|
+
raise RuntimeError(f'{self.scan_title}: cannot determine dwell time '
|
|
1112
|
+
f'for scans of type {self.spec_macro}')
|
|
722
1113
|
|
|
723
1114
|
def get_detector_num_bins(self, detector_prefix):
|
|
724
|
-
with open(self.
|
|
1115
|
+
with open(self.get_detector_data_file(detector_prefix)) \
|
|
1116
|
+
as detector_file:
|
|
725
1117
|
lines = detector_file.readlines()
|
|
726
1118
|
for line in lines:
|
|
727
1119
|
if line.startswith('#@CHANN'):
|
|
728
1120
|
try:
|
|
729
|
-
line_prefix, number_saved, first_saved, last_saved,
|
|
730
|
-
|
|
1121
|
+
line_prefix, number_saved, first_saved, last_saved, \
|
|
1122
|
+
reduction_coef = line.split()
|
|
1123
|
+
return int(number_saved)
|
|
731
1124
|
except:
|
|
732
1125
|
continue
|
|
733
|
-
raise
|
|
734
|
-
|
|
1126
|
+
raise RuntimeError(f'{self.scan_title}: could not find num_bins for '
|
|
1127
|
+
f'detector {detector_prefix}')
|
|
1128
|
+
|
|
735
1129
|
def get_detector_data_path(self):
|
|
736
|
-
return
|
|
1130
|
+
return self.scan_path
|
|
737
1131
|
|
|
738
|
-
def
|
|
1132
|
+
def get_detector_data_file(self, detector_prefix, scan_step_index=0):
|
|
739
1133
|
file_name = f'spec.log.scan{self.scan_number}.mca1.mca'
|
|
740
1134
|
file_name_full = os.path.join(self.detector_data_path, file_name)
|
|
741
1135
|
if os.path.isfile(file_name_full):
|
|
742
|
-
return
|
|
743
|
-
|
|
744
|
-
|
|
1136
|
+
return file_name_full
|
|
1137
|
+
raise RuntimeError(
|
|
1138
|
+
f'{self.scan_title}: could not find detector image file')
|
|
745
1139
|
|
|
746
|
-
@cache
|
|
747
1140
|
def get_all_detector_data(self, detector_prefix):
|
|
748
|
-
# This should be easy with pyspec, but there are bugs in
|
|
749
|
-
# or is the 'bug' from a nonstandard
|
|
750
|
-
#
|
|
1141
|
+
# This should be easy with pyspec, but there are bugs in
|
|
1142
|
+
# pyspec for MCA data..... or is the 'bug' from a nonstandard
|
|
1143
|
+
# implementation of some macro on our end? According to spec
|
|
1144
|
+
# manual and pyspec code, mca data should always begin w/ '@A'
|
|
751
1145
|
# In example scans, it begins with '@mca1' instead
|
|
752
1146
|
data = []
|
|
753
|
-
|
|
754
|
-
with open(self.
|
|
1147
|
+
|
|
1148
|
+
with open(self.get_detector_data_file(detector_prefix)) \
|
|
1149
|
+
as detector_file:
|
|
755
1150
|
lines = [line.strip("\\\n") for line in detector_file.readlines()]
|
|
756
1151
|
|
|
757
1152
|
num_bins = self.get_detector_num_bins(detector_prefix)
|
|
@@ -766,20 +1161,21 @@ class SMBMCAScanParser(MCAScanParser, SMBScanParser):
|
|
|
766
1161
|
spectrum = np.zeros(num_bins)
|
|
767
1162
|
if counter == 1:
|
|
768
1163
|
b = np.array(a[1:]).astype('uint16')
|
|
769
|
-
spectrum[(counter-1)*25:((counter-1)*25+25)] = b
|
|
1164
|
+
spectrum[(counter-1) * 25:((counter-1) * 25 + 25)] = b
|
|
770
1165
|
counter = counter + 1
|
|
771
|
-
elif counter > 1 and counter <= (np.floor(num_bins/25.)):
|
|
1166
|
+
elif counter > 1 and counter <= (np.floor(num_bins / 25.)):
|
|
772
1167
|
b = np.array(a).astype('uint16')
|
|
773
|
-
spectrum[(counter-1)*25:((counter-1)*25+25)] = b
|
|
774
|
-
counter = counter+1
|
|
1168
|
+
spectrum[(counter-1) * 25:((counter-1) * 25 + 25)] = b
|
|
1169
|
+
counter = counter + 1
|
|
775
1170
|
elif counter == (np.ceil(num_bins/25.)):
|
|
776
1171
|
b = np.array(a).astype('uint16')
|
|
777
|
-
spectrum[(counter-1)*25:
|
|
1172
|
+
spectrum[(counter-1) * 25:
|
|
1173
|
+
((counter-1) * 25 + (np.mod(num_bins, 25)))] = b
|
|
778
1174
|
data.append(spectrum)
|
|
779
1175
|
counter = 0
|
|
780
1176
|
|
|
781
|
-
return
|
|
1177
|
+
return data
|
|
782
1178
|
|
|
783
1179
|
def get_detector_data(self, detector_prefix, scan_step_index:int):
|
|
784
1180
|
detector_data = self.get_all_detector_data(detector_prefix)
|
|
785
|
-
return
|
|
1181
|
+
return detector_data[scan_step_index]
|