ChessAnalysisPipeline 0.0.13__py3-none-any.whl → 0.0.15__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/__init__.py +1 -1
- CHAP/common/__init__.py +10 -0
- CHAP/common/models/map.py +389 -124
- CHAP/common/processor.py +1494 -59
- CHAP/common/reader.py +180 -8
- CHAP/common/writer.py +192 -15
- CHAP/edd/__init__.py +12 -3
- CHAP/edd/models.py +868 -451
- CHAP/edd/processor.py +2383 -462
- CHAP/edd/reader.py +672 -0
- CHAP/edd/utils.py +906 -172
- CHAP/foxden/__init__.py +6 -0
- CHAP/foxden/processor.py +42 -0
- CHAP/foxden/writer.py +65 -0
- CHAP/pipeline.py +35 -3
- CHAP/runner.py +43 -16
- CHAP/tomo/models.py +15 -5
- CHAP/tomo/processor.py +871 -761
- CHAP/utils/__init__.py +1 -0
- CHAP/utils/fit.py +1339 -1309
- CHAP/utils/general.py +568 -105
- CHAP/utils/models.py +567 -0
- CHAP/utils/scanparsers.py +460 -77
- ChessAnalysisPipeline-0.0.15.dist-info/LICENSE +60 -0
- {ChessAnalysisPipeline-0.0.13.dist-info → ChessAnalysisPipeline-0.0.15.dist-info}/METADATA +1 -1
- {ChessAnalysisPipeline-0.0.13.dist-info → ChessAnalysisPipeline-0.0.15.dist-info}/RECORD +29 -25
- {ChessAnalysisPipeline-0.0.13.dist-info → ChessAnalysisPipeline-0.0.15.dist-info}/WHEEL +1 -1
- ChessAnalysisPipeline-0.0.13.dist-info/LICENSE +0 -21
- {ChessAnalysisPipeline-0.0.13.dist-info → ChessAnalysisPipeline-0.0.15.dist-info}/entry_points.txt +0 -0
- {ChessAnalysisPipeline-0.0.13.dist-info → ChessAnalysisPipeline-0.0.15.dist-info}/top_level.txt +0 -0
CHAP/common/models/map.py
CHANGED
|
@@ -32,7 +32,7 @@ class Sample(BaseModel):
|
|
|
32
32
|
:ivar name: The name of the sample.
|
|
33
33
|
:type name: str
|
|
34
34
|
:ivar description: A description of the sample.
|
|
35
|
-
:type description:
|
|
35
|
+
:type description: str, optional
|
|
36
36
|
"""
|
|
37
37
|
name: constr(min_length=1)
|
|
38
38
|
description: Optional[str]
|
|
@@ -45,9 +45,12 @@ class SpecScans(BaseModel):
|
|
|
45
45
|
:type spec_file: str
|
|
46
46
|
:ivar scan_numbers: List of scan numbers to use.
|
|
47
47
|
:type scan_numbers: list[int]
|
|
48
|
+
:ivar par_file: Path to a non-default SMB par file.
|
|
49
|
+
:type par_file: str, optional
|
|
48
50
|
"""
|
|
49
51
|
spec_file: FilePath
|
|
50
52
|
scan_numbers: conlist(item_type=conint(gt=0), min_items=1)
|
|
53
|
+
par_file: Optional[FilePath]
|
|
51
54
|
|
|
52
55
|
@validator('spec_file', allow_reuse=True)
|
|
53
56
|
def validate_spec_file(cls, spec_file, values):
|
|
@@ -55,6 +58,8 @@ class SpecScans(BaseModel):
|
|
|
55
58
|
|
|
56
59
|
:param spec_file: Path to the SPEC file.
|
|
57
60
|
:type spec_file: str
|
|
61
|
+
:param values: Dictionary of validated class field values.
|
|
62
|
+
:type values: dict
|
|
58
63
|
:raises ValueError: If the SPEC file is invalid.
|
|
59
64
|
:return: Absolute path to the SPEC file, if it is valid.
|
|
60
65
|
:rtype: str
|
|
@@ -72,7 +77,7 @@ class SpecScans(BaseModel):
|
|
|
72
77
|
|
|
73
78
|
:param scan_numbers: List of scan numbers.
|
|
74
79
|
:type scan_numbers: list of int
|
|
75
|
-
:param values: Dictionary of
|
|
80
|
+
:param values: Dictionary of validated class field values.
|
|
76
81
|
:type values: dict
|
|
77
82
|
:raises ValueError: If a specified scan number is not found in
|
|
78
83
|
the SPEC file.
|
|
@@ -95,6 +100,25 @@ class SpecScans(BaseModel):
|
|
|
95
100
|
f'No scan number {scan_number} in {spec_file}')
|
|
96
101
|
return scan_numbers
|
|
97
102
|
|
|
103
|
+
@validator('par_file', allow_reuse=True)
|
|
104
|
+
def validate_par_file(cls, par_file, values):
|
|
105
|
+
"""Validate the specified SMB par file.
|
|
106
|
+
|
|
107
|
+
:param par_file: Path to a non-default SMB par file.
|
|
108
|
+
:type par_file: str
|
|
109
|
+
:param values: Dictionary of validated class field values.
|
|
110
|
+
:type values: dict
|
|
111
|
+
:raises ValueError: If the SMB par file is invalid.
|
|
112
|
+
:return: Absolute path to the SMB par file, if it is valid.
|
|
113
|
+
:rtype: str
|
|
114
|
+
"""
|
|
115
|
+
if par_file is None or not par_file:
|
|
116
|
+
return ''
|
|
117
|
+
par_file = os.path.abspath(par_file)
|
|
118
|
+
if not os.path.isfile(par_file):
|
|
119
|
+
raise ValueError(f'Invalid SMB par file {par_file}')
|
|
120
|
+
return par_file
|
|
121
|
+
|
|
98
122
|
@property
|
|
99
123
|
def scanparsers(self):
|
|
100
124
|
"""A list of `ScanParser`s for each of the scans specified by
|
|
@@ -107,25 +131,29 @@ class SpecScans(BaseModel):
|
|
|
107
131
|
"""This method returns a `ScanParser` for the specified scan
|
|
108
132
|
number in the specified SPEC file.
|
|
109
133
|
|
|
110
|
-
:param scan_number: Scan number to get a `ScanParser` for
|
|
134
|
+
:param scan_number: Scan number to get a `ScanParser` for.
|
|
111
135
|
:type scan_number: int
|
|
112
|
-
:return: `ScanParser` for the specified scan number
|
|
136
|
+
:return: `ScanParser` for the specified scan number.
|
|
113
137
|
:rtype: ScanParser
|
|
114
138
|
"""
|
|
115
|
-
|
|
139
|
+
if self.par_file:
|
|
140
|
+
return get_scanparser(
|
|
141
|
+
self.spec_file, scan_number, par_file=self.par_file)
|
|
142
|
+
else:
|
|
143
|
+
return get_scanparser(self.spec_file, scan_number)
|
|
116
144
|
|
|
117
145
|
def get_index(self, scan_number:int, scan_step_index:int, map_config):
|
|
118
146
|
"""This method returns a tuple representing the index of a
|
|
119
147
|
specific step in a specific SPEC scan within a map.
|
|
120
148
|
|
|
121
|
-
:param scan_number: Scan number to get index for
|
|
149
|
+
:param scan_number: Scan number to get index for.
|
|
122
150
|
:type scan_number: int
|
|
123
|
-
:param scan_step_index: Scan step index to get index for
|
|
151
|
+
:param scan_step_index: Scan step index to get index for.
|
|
124
152
|
:type scan_step_index: int
|
|
125
|
-
:param map_config: Map configuration to get index for
|
|
153
|
+
:param map_config: Map configuration to get index for.
|
|
126
154
|
:type map_config: MapConfig
|
|
127
155
|
:return: Index for the specified scan number and scan step
|
|
128
|
-
index within the specified map configuration
|
|
156
|
+
index within the specified map configuration.
|
|
129
157
|
:rtype: tuple
|
|
130
158
|
"""
|
|
131
159
|
index = ()
|
|
@@ -133,7 +161,8 @@ class SpecScans(BaseModel):
|
|
|
133
161
|
coordinate_index = list(
|
|
134
162
|
map_config.coords[independent_dimension.label]).index(
|
|
135
163
|
independent_dimension.get_value(
|
|
136
|
-
self, scan_number, scan_step_index
|
|
164
|
+
self, scan_number, scan_step_index,
|
|
165
|
+
map_config.scalar_data))
|
|
137
166
|
index = (coordinate_index, *index)
|
|
138
167
|
return index
|
|
139
168
|
|
|
@@ -144,14 +173,15 @@ class SpecScans(BaseModel):
|
|
|
144
173
|
"""Return the raw data from the specified detectors at the
|
|
145
174
|
specified scan number and scan step index.
|
|
146
175
|
|
|
147
|
-
:param detectors: List of detector prefixes to get raw data
|
|
176
|
+
:param detectors: List of detector prefixes to get raw data
|
|
177
|
+
for.
|
|
148
178
|
:type detectors: list[str]
|
|
149
|
-
:param scan_number: Scan number to get data for
|
|
179
|
+
:param scan_number: Scan number to get data for.
|
|
150
180
|
:type scan_number: int
|
|
151
|
-
:param scan_step_index: Scan step index to get data for
|
|
181
|
+
:param scan_step_index: Scan step index to get data for.
|
|
152
182
|
:type scan_step_index: int
|
|
153
183
|
:return: Data from the specified detectors for the specified
|
|
154
|
-
scan number and scan step index
|
|
184
|
+
scan number and scan step index.
|
|
155
185
|
:rtype: list[np.ndarray]
|
|
156
186
|
"""
|
|
157
187
|
return get_detector_data(
|
|
@@ -169,10 +199,13 @@ def get_available_scan_numbers(spec_file:str):
|
|
|
169
199
|
|
|
170
200
|
|
|
171
201
|
@cache
|
|
172
|
-
def get_scanparser(spec_file:str, scan_number:int):
|
|
202
|
+
def get_scanparser(spec_file:str, scan_number:int, par_file=None):
|
|
173
203
|
if scan_number not in get_available_scan_numbers(spec_file):
|
|
174
204
|
return None
|
|
175
|
-
|
|
205
|
+
if par_file is None:
|
|
206
|
+
return ScanParser(spec_file, scan_number)
|
|
207
|
+
else:
|
|
208
|
+
return ScanParser(spec_file, scan_number, par_file=par_file)
|
|
176
209
|
|
|
177
210
|
|
|
178
211
|
@lru_cache(maxsize=10)
|
|
@@ -207,7 +240,8 @@ class PointByPointScanData(BaseModel):
|
|
|
207
240
|
"""
|
|
208
241
|
label: constr(min_length=1)
|
|
209
242
|
units: constr(strip_whitespace=True, min_length=1)
|
|
210
|
-
data_type: Literal['spec_motor', '
|
|
243
|
+
data_type: Literal['spec_motor', 'spec_motor_absolute', 'scan_column',
|
|
244
|
+
'smb_par', 'expression']
|
|
211
245
|
name: constr(strip_whitespace=True, min_length=1)
|
|
212
246
|
|
|
213
247
|
@validator('label')
|
|
@@ -216,7 +250,7 @@ class PointByPointScanData(BaseModel):
|
|
|
216
250
|
any of the values for `label` reserved for certain data needed
|
|
217
251
|
to perform corrections.
|
|
218
252
|
|
|
219
|
-
:param label: The value of `label` to validate
|
|
253
|
+
:param label: The value of `label` to validate.
|
|
220
254
|
:type label: str
|
|
221
255
|
:raises ValueError: If `label` is one of the reserved values.
|
|
222
256
|
:return: The original supplied value `label`, if it is
|
|
@@ -239,8 +273,6 @@ class PointByPointScanData(BaseModel):
|
|
|
239
273
|
:raises TypeError: If the station is not compatible with the
|
|
240
274
|
value of the `data_type` attribute for this instance of
|
|
241
275
|
PointByPointScanData.
|
|
242
|
-
:return: None
|
|
243
|
-
:rtype: None
|
|
244
276
|
"""
|
|
245
277
|
if (station.lower() not in ('id1a3', 'id3a')
|
|
246
278
|
and self.data_type == 'smb_par'):
|
|
@@ -256,15 +288,13 @@ class PointByPointScanData(BaseModel):
|
|
|
256
288
|
|
|
257
289
|
:param spec_scans: A list of `SpecScans` whose raw data will
|
|
258
290
|
be checked for the presence of the data represented by
|
|
259
|
-
this instance of `PointByPointScanData
|
|
291
|
+
this instance of `PointByPointScanData`.
|
|
260
292
|
:type spec_scans: list[SpecScans]
|
|
261
293
|
:param scan_step_index: A specific scan step index to validate,
|
|
262
294
|
defaults to `'all'`.
|
|
263
295
|
:type scan_step_index: Union[Literal['all'],int], optional
|
|
264
296
|
:raises RuntimeError: If the data represented by this instance of
|
|
265
297
|
`PointByPointScanData` is missing for the specified scan steps.
|
|
266
|
-
:return: None
|
|
267
|
-
:rtype: None
|
|
268
298
|
"""
|
|
269
299
|
for scans in spec_scans:
|
|
270
300
|
for scan_number in scans.scan_numbers:
|
|
@@ -285,8 +315,48 @@ class PointByPointScanData(BaseModel):
|
|
|
285
315
|
f'for index {index} '
|
|
286
316
|
f'in spec file {scans.spec_file}')
|
|
287
317
|
|
|
288
|
-
def
|
|
289
|
-
|
|
318
|
+
def validate_for_scalar_data(self, scalar_data):
|
|
319
|
+
"""Used for `PointByPointScanData` objects with a `data_type`
|
|
320
|
+
of `'expression'`. Validate that the `scalar_data` field of a
|
|
321
|
+
`MapConfig` object contains all the items necessary for
|
|
322
|
+
evaluating the expression.
|
|
323
|
+
|
|
324
|
+
:param scalar_data: the `scalar_data` field of a `MapConfig`
|
|
325
|
+
that this `PointByPointScanData` object will be validated
|
|
326
|
+
against
|
|
327
|
+
:type scalar_data: list[PointByPointScanData]
|
|
328
|
+
:raises ValueError: if `scalar_data` does not contain items
|
|
329
|
+
needed for evaluating the expression.
|
|
330
|
+
:return: None
|
|
331
|
+
"""
|
|
332
|
+
from ast import parse
|
|
333
|
+
from asteval import get_ast_names
|
|
334
|
+
|
|
335
|
+
labels = get_ast_names(parse(self.name))
|
|
336
|
+
for label in ('round', 'np', 'numpy'):
|
|
337
|
+
try:
|
|
338
|
+
labels.remove(label)
|
|
339
|
+
except:
|
|
340
|
+
pass
|
|
341
|
+
for l in labels:
|
|
342
|
+
if l == 'round':
|
|
343
|
+
symtable[l] = round
|
|
344
|
+
continue
|
|
345
|
+
if l in ('np', 'numpy'):
|
|
346
|
+
symtable[l] = np
|
|
347
|
+
continue
|
|
348
|
+
label_found = False
|
|
349
|
+
for s_d in scalar_data:
|
|
350
|
+
if s_d.label == l:
|
|
351
|
+
label_found = True
|
|
352
|
+
break
|
|
353
|
+
if not label_found:
|
|
354
|
+
raise ValueError(
|
|
355
|
+
f'{l} is not the label of an item in scalar_data')
|
|
356
|
+
|
|
357
|
+
def get_value(
|
|
358
|
+
self, spec_scans:SpecScans, scan_number:int, scan_step_index:int=0,
|
|
359
|
+
scalar_data=[], relative=True, ndigits=None):
|
|
290
360
|
"""Return the value recorded for this instance of
|
|
291
361
|
`PointByPointScanData` at a specific scan step.
|
|
292
362
|
|
|
@@ -296,18 +366,32 @@ class PointByPointScanData(BaseModel):
|
|
|
296
366
|
:param scan_number: The number of the scan in which the
|
|
297
367
|
requested scan step occurs.
|
|
298
368
|
:type scan_number: int
|
|
299
|
-
:param scan_step_index: The index of the requested scan step
|
|
300
|
-
|
|
369
|
+
:param scan_step_index: The index of the requested scan step,
|
|
370
|
+
defaults to `0`.
|
|
371
|
+
:type scan_step_index: int, optional
|
|
372
|
+
:param scalar_data: list of scalar data configurations used to
|
|
373
|
+
get values for `PointByPointScanData` objects with
|
|
374
|
+
`data_type == 'expression'`, defaults to `[]`.
|
|
375
|
+
:type scalar_data: list[PointByPointScanData], optional
|
|
376
|
+
:param relative: Whether to return a relative value or not,
|
|
377
|
+
defaults to `True` (only applies to SPEC motor values).
|
|
378
|
+
:type relative: bool, optional
|
|
379
|
+
:params ndigits: Round SPEC motor values to the specified
|
|
380
|
+
number of decimals if set, defaults to `None`.
|
|
381
|
+
:type ndigits: int, optional
|
|
301
382
|
:return: The value recorded of the data represented by this
|
|
302
383
|
instance of `PointByPointScanData` at the scan step
|
|
303
|
-
requested
|
|
384
|
+
requested.
|
|
304
385
|
:rtype: float
|
|
305
386
|
"""
|
|
306
|
-
if self.data_type
|
|
387
|
+
if 'spec_motor' in self.data_type:
|
|
388
|
+
if 'absolute' in self.data_type:
|
|
389
|
+
relative = False
|
|
307
390
|
return get_spec_motor_value(spec_scans.spec_file,
|
|
308
391
|
scan_number,
|
|
309
392
|
scan_step_index,
|
|
310
|
-
self.name
|
|
393
|
+
self.name,
|
|
394
|
+
relative, ndigits)
|
|
311
395
|
if self.data_type == 'scan_column':
|
|
312
396
|
return get_spec_counter_value(spec_scans.spec_file,
|
|
313
397
|
scan_number,
|
|
@@ -317,12 +401,19 @@ class PointByPointScanData(BaseModel):
|
|
|
317
401
|
return get_smb_par_value(spec_scans.spec_file,
|
|
318
402
|
scan_number,
|
|
319
403
|
self.name)
|
|
404
|
+
elif self.data_type == 'expression':
|
|
405
|
+
return get_expression_value(spec_scans,
|
|
406
|
+
scan_number,
|
|
407
|
+
scan_step_index,
|
|
408
|
+
self.name,
|
|
409
|
+
scalar_data)
|
|
320
410
|
return None
|
|
321
411
|
|
|
322
412
|
|
|
323
413
|
@cache
|
|
324
414
|
def get_spec_motor_value(spec_file:str, scan_number:int,
|
|
325
|
-
scan_step_index:int, spec_mnemonic:str
|
|
415
|
+
scan_step_index:int, spec_mnemonic:str,
|
|
416
|
+
relative=True, ndigits=None):
|
|
326
417
|
"""Return the value recorded for a SPEC motor at a specific scan
|
|
327
418
|
step.
|
|
328
419
|
|
|
@@ -336,7 +427,13 @@ def get_spec_motor_value(spec_file:str, scan_number:int,
|
|
|
336
427
|
:type scan_step_index: int
|
|
337
428
|
:param spec_mnemonic: The menmonic of a SPEC motor.
|
|
338
429
|
:type spec_mnemonic: str
|
|
339
|
-
:
|
|
430
|
+
:param relative: Whether to return a relative value or not,
|
|
431
|
+
defaults to `True`.
|
|
432
|
+
:type relative: bool, optional
|
|
433
|
+
:params ndigits: Round SPEC motor values to the specified
|
|
434
|
+
number of decimals if set, defaults to `None`.
|
|
435
|
+
:type ndigits: int, optional
|
|
436
|
+
:return: The value of the motor at the scan step requested.
|
|
340
437
|
:rtype: float
|
|
341
438
|
"""
|
|
342
439
|
scanparser = get_scanparser(spec_file, scan_number)
|
|
@@ -349,11 +446,15 @@ def get_spec_motor_value(spec_file:str, scan_number:int,
|
|
|
349
446
|
scanparser.spec_scan_shape,
|
|
350
447
|
order='F')
|
|
351
448
|
motor_value = \
|
|
352
|
-
scanparser.
|
|
449
|
+
scanparser.get_spec_scan_motor_vals(
|
|
450
|
+
relative)[motor_i][scan_step[motor_i]]
|
|
353
451
|
else:
|
|
354
|
-
motor_value = scanparser.
|
|
452
|
+
motor_value = scanparser.get_spec_scan_motor_vals(
|
|
453
|
+
relative)[motor_i]
|
|
355
454
|
else:
|
|
356
455
|
motor_value = scanparser.get_spec_positioner_value(spec_mnemonic)
|
|
456
|
+
if ndigits is not None:
|
|
457
|
+
motor_value = round(motor_value, 3)
|
|
357
458
|
return motor_value
|
|
358
459
|
|
|
359
460
|
|
|
@@ -373,7 +474,7 @@ def get_spec_counter_value(spec_file:str, scan_number:int,
|
|
|
373
474
|
:type scan_step_index: int
|
|
374
475
|
:param spec_column_label: The label of a SPEC data column.
|
|
375
476
|
:type spec_column_label: str
|
|
376
|
-
:return: The value of the counter at the scan step requested
|
|
477
|
+
:return: The value of the counter at the scan step requested.
|
|
377
478
|
:rtype: float
|
|
378
479
|
"""
|
|
379
480
|
scanparser = get_scanparser(spec_file, scan_number)
|
|
@@ -393,7 +494,7 @@ def get_smb_par_value(spec_file:str, scan_number:int, par_name:str):
|
|
|
393
494
|
:param scan_number: The number of the scan in which the requested
|
|
394
495
|
scan step occurs.
|
|
395
496
|
:type scan_number: int
|
|
396
|
-
:param par_name: The name of the column in the .par file
|
|
497
|
+
:param par_name: The name of the column in the .par file.
|
|
397
498
|
:type par_name: str
|
|
398
499
|
:return: The value of the .par file value for the scan requested.
|
|
399
500
|
:rtype: float
|
|
@@ -402,24 +503,72 @@ def get_smb_par_value(spec_file:str, scan_number:int, par_name:str):
|
|
|
402
503
|
return scanparser.pars[par_name]
|
|
403
504
|
|
|
404
505
|
|
|
506
|
+
def get_expression_value(spec_scans:SpecScans, scan_number:int,
|
|
507
|
+
scan_step_index:int, expression:str,
|
|
508
|
+
scalar_data:list[PointByPointScanData]):
|
|
509
|
+
"""Return the value of an evaluated expression of other sources of
|
|
510
|
+
point-by-point scalar scan data for a single point.
|
|
511
|
+
|
|
512
|
+
:param spec_scans: An instance of `SpecScans` in which the
|
|
513
|
+
requested scan step occurs.
|
|
514
|
+
:type spec_scans: SpecScans
|
|
515
|
+
:param scan_number: The number of the scan in which the requested
|
|
516
|
+
scan step occurs.
|
|
517
|
+
:type scan_number: int
|
|
518
|
+
:param scan_step_index: The index of the requested scan step.
|
|
519
|
+
:type scan_step_index: int
|
|
520
|
+
:param expression: the string expression to evaluate
|
|
521
|
+
:type expression: str
|
|
522
|
+
:param scalar_data: the `scalar_data` field of a `MapConfig`
|
|
523
|
+
object (used to provide values for variables used in
|
|
524
|
+
`expression`)
|
|
525
|
+
:type scalar_data: list[PointByPointScanData]
|
|
526
|
+
:return: The value of the .par file value for the scan requested.
|
|
527
|
+
:rtype: float
|
|
528
|
+
"""
|
|
529
|
+
from ast import parse
|
|
530
|
+
from asteval import get_ast_names, Interpreter
|
|
531
|
+
labels = get_ast_names(parse(expression))
|
|
532
|
+
symtable = {}
|
|
533
|
+
for l in labels:
|
|
534
|
+
if l == 'round':
|
|
535
|
+
symtable[l] = round
|
|
536
|
+
for s_d in scalar_data:
|
|
537
|
+
if s_d.label == l:
|
|
538
|
+
symtable[l] = s_d.get_value(
|
|
539
|
+
spec_scans, scan_number, scan_step_index, scalar_data)
|
|
540
|
+
aeval = Interpreter(symtable=symtable)
|
|
541
|
+
return aeval(expression)
|
|
542
|
+
|
|
405
543
|
def validate_data_source_for_map_config(data_source, values):
|
|
406
544
|
"""Confirm that an instance of PointByPointScanData is valid for
|
|
407
545
|
the station and scans provided by a map configuration dictionary.
|
|
408
546
|
|
|
409
|
-
:param data_source:
|
|
547
|
+
:param data_source: The input object to validate.
|
|
410
548
|
:type data_source: PintByPointScanData
|
|
411
|
-
:param values:
|
|
549
|
+
:param values: The map configuration dictionary.
|
|
412
550
|
:type values: dict
|
|
413
|
-
:raises Exception:
|
|
551
|
+
:raises Exception: If `data_source` cannot be validated for
|
|
414
552
|
`values`.
|
|
415
|
-
:return: `data_source`,
|
|
553
|
+
:return: `data_source`, if it is valid.
|
|
416
554
|
:rtype: PointByPointScanData
|
|
417
555
|
"""
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
data_source
|
|
421
|
-
|
|
422
|
-
|
|
556
|
+
def _validate_data_source_for_map_config(
|
|
557
|
+
data_source, values, parent_list=None):
|
|
558
|
+
if isinstance(data_source, list):
|
|
559
|
+
return [_validate_data_source_for_map_config(
|
|
560
|
+
d_s, values, parent_list=data_source) for d_s in data_source]
|
|
561
|
+
if data_source is not None:
|
|
562
|
+
if data_source.data_type == 'expression':
|
|
563
|
+
data_source.validate_for_scalar_data(
|
|
564
|
+
values.get('scalar_data', parent_list))
|
|
565
|
+
else:
|
|
566
|
+
import_scanparser(
|
|
567
|
+
values.get('station'), values.get('experiment_type'))
|
|
568
|
+
data_source.validate_for_station(values.get('station'))
|
|
569
|
+
data_source.validate_for_spec_scans(values.get('spec_scans'))
|
|
570
|
+
return(data_source)
|
|
571
|
+
return _validate_data_source_for_map_config(data_source, values)
|
|
423
572
|
|
|
424
573
|
|
|
425
574
|
class IndependentDimension(PointByPointScanData):
|
|
@@ -437,15 +586,15 @@ class IndependentDimension(PointByPointScanData):
|
|
|
437
586
|
:ivar name: Represents the name with which these raw data were
|
|
438
587
|
recorded at time of data collection.
|
|
439
588
|
:type name: str
|
|
440
|
-
:
|
|
441
|
-
`MapConfig` along this axis, defaults to 0
|
|
589
|
+
:ivar start: Sarting index for slicing all datasets of a
|
|
590
|
+
`MapConfig` along this axis, defaults to `0`.
|
|
442
591
|
:type start: int, optional
|
|
443
|
-
:
|
|
592
|
+
:ivar end: Ending index for slicing all datasets of a `MapConfig`
|
|
444
593
|
along this axis, defaults to the total number of unique values
|
|
445
|
-
along this axis in the associated `MapConfig
|
|
594
|
+
along this axis in the associated `MapConfig`.
|
|
446
595
|
:type end: int, optional
|
|
447
|
-
:
|
|
448
|
-
this axis, defaults to 1
|
|
596
|
+
:ivar step: Step for slicing all datasets of a `MapConfig` along
|
|
597
|
+
this axis, defaults to `1`.
|
|
449
598
|
:type step: int, optional
|
|
450
599
|
"""
|
|
451
600
|
start: Optional[conint(ge=0)] = 0
|
|
@@ -489,20 +638,20 @@ class CorrectionsData(PointByPointScanData):
|
|
|
489
638
|
"""Return a list of all the labels reserved for
|
|
490
639
|
corrections-related scalar data.
|
|
491
640
|
|
|
492
|
-
:return: A list of reserved labels
|
|
641
|
+
:return: A list of reserved labels.
|
|
493
642
|
:rtype: list[str]
|
|
494
643
|
"""
|
|
495
|
-
return list(cls.__fields__['label'].type_.__args__)
|
|
644
|
+
return list((*cls.__fields__['label'].type_.__args__, 'round'))
|
|
496
645
|
|
|
497
646
|
|
|
498
647
|
class PresampleIntensity(CorrectionsData):
|
|
499
648
|
"""Class representing a source of raw data for the intensity of
|
|
500
649
|
the beam that is incident on the sample.
|
|
501
650
|
|
|
502
|
-
:ivar label: Must be `
|
|
503
|
-
:type label: Literal[
|
|
504
|
-
:ivar units: Must be `
|
|
505
|
-
:type units: Literal[
|
|
651
|
+
:ivar label: Must be `'presample_intensity"`.
|
|
652
|
+
:type label: Literal['presample_intensity']
|
|
653
|
+
:ivar units: Must be `'counts'`.
|
|
654
|
+
:type units: Literal['counts']
|
|
506
655
|
:ivar data_type: Represents how these data were recorded at time
|
|
507
656
|
of data collection.
|
|
508
657
|
:type data_type: Literal['scan_column', 'smb_par']
|
|
@@ -518,10 +667,10 @@ class PostsampleIntensity(CorrectionsData):
|
|
|
518
667
|
"""Class representing a source of raw data for the intensity of
|
|
519
668
|
the beam that has passed through the sample.
|
|
520
669
|
|
|
521
|
-
:ivar label: Must be `
|
|
522
|
-
:type label: Literal[
|
|
523
|
-
:ivar units: Must be `
|
|
524
|
-
:type units: Literal[
|
|
670
|
+
:ivar label: Must be `'postsample_intensity'`.
|
|
671
|
+
:type label: Literal['postsample_intensity']
|
|
672
|
+
:ivar units: Must be `'counts'`.
|
|
673
|
+
:type units: Literal['counts']
|
|
525
674
|
:ivar data_type: Represents how these data were recorded at time
|
|
526
675
|
of data collection.
|
|
527
676
|
:type data_type: Literal['scan_column', 'smb_par']
|
|
@@ -539,10 +688,10 @@ class DwellTimeActual(CorrectionsData):
|
|
|
539
688
|
can vary slightly point-to-point from the dwell time specified in
|
|
540
689
|
the command).
|
|
541
690
|
|
|
542
|
-
:ivar label: Must be `
|
|
543
|
-
:type label: Literal[
|
|
544
|
-
:ivar units: Must be `
|
|
545
|
-
:type units: Literal[
|
|
691
|
+
:ivar label: Must be `'dwell_time_actual'`.
|
|
692
|
+
:type label: Literal['dwell_time_actual']
|
|
693
|
+
:ivar units: Must be `'counts'`.
|
|
694
|
+
:type units: Literal['counts']
|
|
546
695
|
:ivar data_type: Represents how these data were recorded at time
|
|
547
696
|
of data collection.
|
|
548
697
|
:type data_type: Literal['scan_column', 'smb_par']
|
|
@@ -559,11 +708,11 @@ class SpecConfig(BaseModel):
|
|
|
559
708
|
|
|
560
709
|
:ivar station: The name of the station at which the data was
|
|
561
710
|
collected.
|
|
562
|
-
:type station: Literal['id1a3','id3a','id3b']
|
|
711
|
+
:type station: Literal['id1a3', 'id3a', 'id3b']
|
|
563
712
|
:ivar spec_scans: A list of the SPEC scans that compose the set.
|
|
564
713
|
:type spec_scans: list[SpecScans]
|
|
565
714
|
"""
|
|
566
|
-
station: Literal['id1a3','id3a','id3b']
|
|
715
|
+
station: Literal['id1a3', 'id3a', 'id3b']
|
|
567
716
|
experiment_type: Literal['SAXSWAXS', 'EDD', 'XRF', 'TOMO']
|
|
568
717
|
spec_scans: conlist(item_type=SpecScans, min_items=1)
|
|
569
718
|
|
|
@@ -572,7 +721,7 @@ class SpecConfig(BaseModel):
|
|
|
572
721
|
"""Ensure that a valid configuration was provided and finalize
|
|
573
722
|
spec_file filepaths.
|
|
574
723
|
|
|
575
|
-
:param values: Dictionary of class field values.
|
|
724
|
+
:param values: Dictionary of validated class field values.
|
|
576
725
|
:type values: dict
|
|
577
726
|
:return: The validated list of `values`.
|
|
578
727
|
:rtype: dict
|
|
@@ -593,6 +742,14 @@ class SpecConfig(BaseModel):
|
|
|
593
742
|
def validate_experiment_type(cls, value, values):
|
|
594
743
|
"""Ensure values for the station and experiment_type fields are
|
|
595
744
|
compatible
|
|
745
|
+
|
|
746
|
+
:param value: Field value to validate (`experiment_type`).
|
|
747
|
+
:type value: str
|
|
748
|
+
:param values: Dictionary of validated class field values.
|
|
749
|
+
:type values: dict
|
|
750
|
+
:raises ValueError: Invalid experiment type.
|
|
751
|
+
:return: The validated field for `experiment_type`.
|
|
752
|
+
:rtype: str
|
|
596
753
|
"""
|
|
597
754
|
station = values.get('station')
|
|
598
755
|
if station == 'id1a3':
|
|
@@ -620,7 +777,7 @@ class MapConfig(BaseModel):
|
|
|
620
777
|
:type title: str
|
|
621
778
|
:ivar station: The name of the station at which the map was
|
|
622
779
|
collected.
|
|
623
|
-
:type station: Literal['id1a3','id3a','id3b']
|
|
780
|
+
:type station: Literal['id1a3', 'id3a', 'id3b']
|
|
624
781
|
:ivar spec_scans: A list of the SPEC scans that compose the map.
|
|
625
782
|
:type spec_scans: list[SpecScans]
|
|
626
783
|
:ivar independent_dimensions: A list of the sources of data
|
|
@@ -630,27 +787,27 @@ class MapConfig(BaseModel):
|
|
|
630
787
|
:ivar presample_intensity: A source of point-by-point presample
|
|
631
788
|
beam intensity data. Required when applying a CorrectionConfig
|
|
632
789
|
tool.
|
|
633
|
-
:type presample_intensity:
|
|
790
|
+
:type presample_intensity: PresampleIntensity, optional
|
|
634
791
|
:ivar dwell_time_actual: A source of point-by-point actual dwell
|
|
635
792
|
times for SPEC scans. Required when applying a
|
|
636
793
|
CorrectionConfig tool.
|
|
637
|
-
:type dwell_time_actual:
|
|
638
|
-
:ivar
|
|
794
|
+
:type dwell_time_actual: DwellTimeActual, optional
|
|
795
|
+
:ivar postsample_intensity: A source of point-by-point postsample
|
|
639
796
|
beam intensity data. Required when applying a CorrectionConfig
|
|
640
|
-
tool with `correction_type=
|
|
641
|
-
`correction_type=
|
|
642
|
-
:type
|
|
797
|
+
tool with `correction_type='flux_absorption'` or
|
|
798
|
+
`correction_type='flux_absorption_background'`.
|
|
799
|
+
:type postsample_intensity: PresampleIntensity, optional
|
|
643
800
|
:ivar scalar_data: A list of the sources of data representing
|
|
644
801
|
other scalar raw data values collected at each point on the
|
|
645
802
|
map. In the NeXus file representation of the map, datasets for
|
|
646
|
-
these values will be included
|
|
647
|
-
:type
|
|
803
|
+
these values will be included, defaults to `[]`.
|
|
804
|
+
:type scalar_data: list[PointByPointScanData], optional
|
|
648
805
|
:ivar map_type: Type of map, structured or unstructured,
|
|
649
806
|
defaults to `'structured'`.
|
|
650
|
-
:type map_type:
|
|
807
|
+
:type map_type: Literal['structured', 'unstructured'], optional
|
|
651
808
|
"""
|
|
652
809
|
title: constr(strip_whitespace=True, min_length=1)
|
|
653
|
-
station: Literal['id1a3','id3a','id3b']
|
|
810
|
+
station: Literal['id1a3', 'id3a', 'id3b']
|
|
654
811
|
experiment_type: Literal['SAXSWAXS', 'EDD', 'XRF', 'TOMO']
|
|
655
812
|
sample: Sample
|
|
656
813
|
spec_scans: conlist(item_type=SpecScans, min_items=1)
|
|
@@ -660,6 +817,7 @@ class MapConfig(BaseModel):
|
|
|
660
817
|
dwell_time_actual: Optional[DwellTimeActual]
|
|
661
818
|
postsample_intensity: Optional[PostsampleIntensity]
|
|
662
819
|
scalar_data: Optional[list[PointByPointScanData]] = []
|
|
820
|
+
attrs: Optional[dict] = {}
|
|
663
821
|
map_type: Optional[Literal['structured', 'unstructured']] = 'structured'
|
|
664
822
|
_coords: dict = PrivateAttr()
|
|
665
823
|
_dims: tuple = PrivateAttr()
|
|
@@ -681,7 +839,6 @@ class MapConfig(BaseModel):
|
|
|
681
839
|
allow_reuse=True)(validate_data_source_for_map_config)
|
|
682
840
|
_validate_scalar_data = validator(
|
|
683
841
|
'scalar_data',
|
|
684
|
-
each_item=True,
|
|
685
842
|
allow_reuse=True)(validate_data_source_for_map_config)
|
|
686
843
|
|
|
687
844
|
@root_validator(pre=True)
|
|
@@ -689,7 +846,7 @@ class MapConfig(BaseModel):
|
|
|
689
846
|
"""Ensure that a valid configuration was provided and finalize
|
|
690
847
|
spec_file filepaths.
|
|
691
848
|
|
|
692
|
-
:param values: Dictionary of class field values.
|
|
849
|
+
:param values: Dictionary of validated class field values.
|
|
693
850
|
:type values: dict
|
|
694
851
|
:return: The validated list of `values`.
|
|
695
852
|
:rtype: dict
|
|
@@ -706,6 +863,67 @@ class MapConfig(BaseModel):
|
|
|
706
863
|
values['spec_scans'] = spec_scans
|
|
707
864
|
return values
|
|
708
865
|
|
|
866
|
+
@validator('experiment_type')
|
|
867
|
+
def validate_experiment_type(cls, value, values):
|
|
868
|
+
"""Ensure values for the station and experiment_type fields are
|
|
869
|
+
compatible.
|
|
870
|
+
|
|
871
|
+
:param value: Field value to validate (`experiment_type`).
|
|
872
|
+
:type value: dict
|
|
873
|
+
:param values: Dictionary of validated class field values.
|
|
874
|
+
:type values: dict
|
|
875
|
+
:raises ValueError: Invalid experiment type.
|
|
876
|
+
:return: The validated field for `experiment_type`.
|
|
877
|
+
:rtype: str
|
|
878
|
+
"""
|
|
879
|
+
station = values['station']
|
|
880
|
+
if station == 'id1a3':
|
|
881
|
+
allowed_experiment_types = ['SAXSWAXS', 'EDD', 'TOMO']
|
|
882
|
+
elif station == 'id3a':
|
|
883
|
+
allowed_experiment_types = ['EDD', 'TOMO']
|
|
884
|
+
elif station == 'id3b':
|
|
885
|
+
allowed_experiment_types = ['SAXSWAXS', 'XRF', 'TOMO']
|
|
886
|
+
else:
|
|
887
|
+
allowed_experiment_types = []
|
|
888
|
+
if value not in allowed_experiment_types:
|
|
889
|
+
raise ValueError(
|
|
890
|
+
f'For station {station}, allowed experiment types are '
|
|
891
|
+
f'{", ".join(allowed_experiment_types)}. '
|
|
892
|
+
f'Supplied experiment type {value} is not allowed.')
|
|
893
|
+
return value
|
|
894
|
+
|
|
895
|
+
@validator('attrs', always=True)
|
|
896
|
+
def validate_attrs(cls, value, values):
|
|
897
|
+
"""Read any additional attributes depending on the values for
|
|
898
|
+
the station and experiment_type fields.
|
|
899
|
+
|
|
900
|
+
:param value: Field value to validate (`attrs`).
|
|
901
|
+
:type value: dict
|
|
902
|
+
:param values: Dictionary of validated class field values.
|
|
903
|
+
:type values: dict
|
|
904
|
+
:raises ValueError: Invalid attribute.
|
|
905
|
+
:return: The validated field for `attrs`.
|
|
906
|
+
:rtype: dict
|
|
907
|
+
"""
|
|
908
|
+
# Get the map's scan_type for EDD experiments
|
|
909
|
+
station = values['station']
|
|
910
|
+
experiment_type = values['experiment_type']
|
|
911
|
+
if station in ['id1a3', 'id3a'] and experiment_type == 'EDD':
|
|
912
|
+
value['scan_type'] = cls.get_smb_par_attr(values, 'scan_type')
|
|
913
|
+
value['config_id'] = cls.get_smb_par_attr(values, 'config_id')
|
|
914
|
+
value['dataset_id'] = cls.get_smb_par_attr(values, 'dataset_id')
|
|
915
|
+
axes_labels = {1: 'fly_labx', 2: 'fly_laby', 3: 'fly_labz',
|
|
916
|
+
4: 'fly_ometotal'}
|
|
917
|
+
if value['scan_type'] is None:
|
|
918
|
+
return value
|
|
919
|
+
if value['scan_type'] != 0:
|
|
920
|
+
value['fly_axis_labels'] = [
|
|
921
|
+
axes_labels[cls.get_smb_par_attr(values, 'fly_axis0')]]
|
|
922
|
+
if value['scan_type'] in (2, 3, 5):
|
|
923
|
+
value['fly_axis_labels'].append(
|
|
924
|
+
axes_labels[cls.get_smb_par_attr(values, 'fly_axis1')])
|
|
925
|
+
return value
|
|
926
|
+
|
|
709
927
|
@validator('map_type', pre=True, always=True)
|
|
710
928
|
def validate_map_type(cls, map_type, values):
|
|
711
929
|
"""Validate the map_type field.
|
|
@@ -713,16 +931,26 @@ class MapConfig(BaseModel):
|
|
|
713
931
|
:param map_type: Type of map, structured or unstructured,
|
|
714
932
|
defaults to `'structured'`.
|
|
715
933
|
:type map_type: Literal['structured', 'unstructured']]
|
|
716
|
-
:param values: Dictionary of
|
|
934
|
+
:param values: Dictionary of validated class field values.
|
|
717
935
|
:type values: dict
|
|
718
936
|
:return: The validated value for map_type.
|
|
719
937
|
:rtype: str
|
|
720
938
|
"""
|
|
721
939
|
dims = {}
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
940
|
+
attrs = values.get('attrs', {})
|
|
941
|
+
scan_type = attrs.get('scan_type', -1)
|
|
942
|
+
fly_axis_labels = attrs.get('fly_axis_labels', [])
|
|
943
|
+
spec_scans = values['spec_scans']
|
|
944
|
+
independent_dimensions = values['independent_dimensions']
|
|
945
|
+
scalar_data = values['scalar_data']
|
|
946
|
+
import_scanparser(values['station'], values['experiment_type'])
|
|
725
947
|
for i, dim in enumerate(deepcopy(independent_dimensions)):
|
|
948
|
+
if dim.label in fly_axis_labels:
|
|
949
|
+
relative = True
|
|
950
|
+
ndigits = 3
|
|
951
|
+
else:
|
|
952
|
+
relative = False
|
|
953
|
+
ndigits = None
|
|
726
954
|
dims[dim.label] = []
|
|
727
955
|
for scans in spec_scans:
|
|
728
956
|
for scan_number in scans.scan_numbers:
|
|
@@ -730,7 +958,8 @@ class MapConfig(BaseModel):
|
|
|
730
958
|
for scan_step_index in range(
|
|
731
959
|
scanparser.spec_scan_npts):
|
|
732
960
|
dims[dim.label].append(dim.get_value(
|
|
733
|
-
|
|
961
|
+
scans, scan_number, scan_step_index,
|
|
962
|
+
scalar_data, relative, ndigits))
|
|
734
963
|
dims[dim.label] = np.unique(dims[dim.label])
|
|
735
964
|
if dim.end is None:
|
|
736
965
|
dim.end = len(dims[dim.label])
|
|
@@ -745,34 +974,42 @@ class MapConfig(BaseModel):
|
|
|
745
974
|
for scan_step_index in range(scanparser.spec_scan_npts):
|
|
746
975
|
coords[tuple([
|
|
747
976
|
list(dims[dim.label]).index(
|
|
748
|
-
dim.get_value(scans, scan_number, scan_step_index
|
|
977
|
+
dim.get_value(scans, scan_number, scan_step_index,
|
|
978
|
+
scalar_data, True, 3))
|
|
979
|
+
if dim.label in fly_axis_labels else
|
|
980
|
+
list(dims[dim.label]).index(
|
|
981
|
+
dim.get_value(scans, scan_number, scan_step_index,
|
|
982
|
+
scalar_data))
|
|
749
983
|
for dim in independent_dimensions])] += 1
|
|
750
984
|
if any(True for v in coords.flatten() if v == 0 or v > 1):
|
|
751
985
|
return 'unstructured'
|
|
752
986
|
else:
|
|
753
987
|
return 'structured'
|
|
754
988
|
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
989
|
+
@staticmethod
|
|
990
|
+
def get_smb_par_attr(class_fields, label, units='-', name=None):
|
|
991
|
+
"""Read an SMB par file attribute."""
|
|
992
|
+
if name is None:
|
|
993
|
+
name = label
|
|
994
|
+
scalar_data = PointByPointScanData(
|
|
995
|
+
label=label, data_type='smb_par', units=units, name=name)
|
|
996
|
+
values = []
|
|
997
|
+
for scans in class_fields.get('spec_scans'):
|
|
998
|
+
for scan_number in scans.scan_numbers:
|
|
999
|
+
scanparser = scans.get_scanparser(scan_number)
|
|
1000
|
+
try:
|
|
1001
|
+
values.append(scanparser.pars[name])
|
|
1002
|
+
except:
|
|
1003
|
+
print(
|
|
1004
|
+
f'Warning: No value found for .par file value "{name}"'
|
|
1005
|
+
+ f' on scan {scan_number} in spec file '
|
|
1006
|
+
+ f'{scans.spec_file}.')
|
|
1007
|
+
values.append(None)
|
|
1008
|
+
values = list(set(values))
|
|
1009
|
+
if len(values) != 1:
|
|
1010
|
+
raise ValueError(f'More than one {name} in map not allowed '
|
|
1011
|
+
f'({values})')
|
|
1012
|
+
return values[0]
|
|
776
1013
|
|
|
777
1014
|
@property
|
|
778
1015
|
def all_scalar_data(self):
|
|
@@ -793,9 +1030,17 @@ class MapConfig(BaseModel):
|
|
|
793
1030
|
"""Return a dictionary of the values of each independent
|
|
794
1031
|
dimension across the map.
|
|
795
1032
|
"""
|
|
796
|
-
if not hasattr(self,
|
|
1033
|
+
if not hasattr(self, '_coords'):
|
|
1034
|
+
scan_type = self.attrs.get('scan_type', -1)
|
|
1035
|
+
fly_axis_labels = self.attrs.get('fly_axis_labels', [])
|
|
797
1036
|
coords = {}
|
|
798
1037
|
for dim in self.independent_dimensions:
|
|
1038
|
+
if dim.label in fly_axis_labels:
|
|
1039
|
+
relative = True
|
|
1040
|
+
ndigits = 3
|
|
1041
|
+
else:
|
|
1042
|
+
relative = False
|
|
1043
|
+
ndigits = None
|
|
799
1044
|
coords[dim.label] = []
|
|
800
1045
|
for scans in self.spec_scans:
|
|
801
1046
|
for scan_number in scans.scan_numbers:
|
|
@@ -803,7 +1048,8 @@ class MapConfig(BaseModel):
|
|
|
803
1048
|
for scan_step_index in range(
|
|
804
1049
|
scanparser.spec_scan_npts):
|
|
805
1050
|
coords[dim.label].append(dim.get_value(
|
|
806
|
-
scans, scan_number, scan_step_index
|
|
1051
|
+
scans, scan_number, scan_step_index,
|
|
1052
|
+
self.scalar_data, relative, ndigits))
|
|
807
1053
|
if self.map_type == 'structured':
|
|
808
1054
|
coords[dim.label] = np.unique(coords[dim.label])
|
|
809
1055
|
self._coords = coords
|
|
@@ -814,7 +1060,7 @@ class MapConfig(BaseModel):
|
|
|
814
1060
|
"""Return a tuple of the independent dimension labels for the
|
|
815
1061
|
map.
|
|
816
1062
|
"""
|
|
817
|
-
if not hasattr(self,
|
|
1063
|
+
if not hasattr(self, '_dims'):
|
|
818
1064
|
self._dims = [
|
|
819
1065
|
dim.label for dim in self.independent_dimensions[::-1]]
|
|
820
1066
|
return self._dims
|
|
@@ -825,7 +1071,7 @@ class MapConfig(BaseModel):
|
|
|
825
1071
|
object, the scan number, and scan step index for every point
|
|
826
1072
|
on the map.
|
|
827
1073
|
"""
|
|
828
|
-
if not hasattr(self,
|
|
1074
|
+
if not hasattr(self, '_scan_step_indices'):
|
|
829
1075
|
scan_step_indices = []
|
|
830
1076
|
for scans in self.spec_scans:
|
|
831
1077
|
for scan_number in scans.scan_numbers:
|
|
@@ -841,7 +1087,7 @@ class MapConfig(BaseModel):
|
|
|
841
1087
|
"""Return the shape of the map -- a tuple representing the
|
|
842
1088
|
number of unique values of each dimension across the map.
|
|
843
1089
|
"""
|
|
844
|
-
if not hasattr(self,
|
|
1090
|
+
if not hasattr(self, '_shape'):
|
|
845
1091
|
if self.map_type == 'structured':
|
|
846
1092
|
self._shape = tuple(
|
|
847
1093
|
[len(v) for k, v in self.coords.items()][::-1])
|
|
@@ -859,6 +1105,14 @@ class MapConfig(BaseModel):
|
|
|
859
1105
|
:rtype: dict
|
|
860
1106
|
"""
|
|
861
1107
|
if self.map_type == 'structured':
|
|
1108
|
+
scan_type = self.attrs.get('scan_type', -1)
|
|
1109
|
+
fly_axis_labels = self.attrs.get('fly_axis_labels', [])
|
|
1110
|
+
if (scan_type in (3, 5)
|
|
1111
|
+
and len(self.dims) ==
|
|
1112
|
+
len(map_index) + len(fly_axis_labels)):
|
|
1113
|
+
dims = [dim for dim in self.dims if dim not in fly_axis_labels]
|
|
1114
|
+
return {dim:self.coords[dim][i]
|
|
1115
|
+
for dim, i in zip(dims, map_index)}
|
|
862
1116
|
return {dim:self.coords[dim][i]
|
|
863
1117
|
for dim, i in zip(self.dims, map_index)}
|
|
864
1118
|
else:
|
|
@@ -893,12 +1147,21 @@ class MapConfig(BaseModel):
|
|
|
893
1147
|
step index.
|
|
894
1148
|
:rtype: tuple[SpecScans, int, int]
|
|
895
1149
|
"""
|
|
1150
|
+
scan_type = self.attrs.get('scan_type', -1)
|
|
1151
|
+
fly_axis_labels = self.attrs.get('fly_axis_labels', [])
|
|
896
1152
|
if self.map_type == 'structured':
|
|
897
1153
|
map_coords = self.get_coords(map_index)
|
|
898
1154
|
for scans, scan_number, scan_step_index in self.scan_step_indices:
|
|
899
|
-
coords = {dim.label:
|
|
900
|
-
|
|
901
|
-
|
|
1155
|
+
coords = {dim.label:(
|
|
1156
|
+
dim.get_value(
|
|
1157
|
+
scans, scan_number, scan_step_index,
|
|
1158
|
+
self.scalar_data, True, 3)
|
|
1159
|
+
if dim.label in fly_axis_labels
|
|
1160
|
+
else
|
|
1161
|
+
dim.get_value(
|
|
1162
|
+
scans, scan_number, scan_step_index,
|
|
1163
|
+
self.scalar_data))
|
|
1164
|
+
for dim in self.independent_dimensions}
|
|
902
1165
|
if coords == map_coords:
|
|
903
1166
|
return scans, scan_number, scan_step_index
|
|
904
1167
|
raise RuntimeError(f'Unable to match coordinates {coords}')
|
|
@@ -910,26 +1173,28 @@ class MapConfig(BaseModel):
|
|
|
910
1173
|
single point in the map.
|
|
911
1174
|
|
|
912
1175
|
:param data: The device configuration to return a value of raw
|
|
913
|
-
data for
|
|
1176
|
+
data for.
|
|
914
1177
|
:type data: PointByPointScanData
|
|
915
|
-
:param map_index: The map index to return raw data for
|
|
1178
|
+
:param map_index: The map index to return raw data for.
|
|
916
1179
|
:type map_index: tuple
|
|
917
|
-
:return: Raw data value
|
|
1180
|
+
:return: Raw data value.
|
|
918
1181
|
"""
|
|
919
1182
|
scans, scan_number, scan_step_index = \
|
|
920
1183
|
self.get_scan_step_index(map_index)
|
|
921
|
-
return data.get_value(scans, scan_number, scan_step_index
|
|
1184
|
+
return data.get_value(scans, scan_number, scan_step_index,
|
|
1185
|
+
self.scalar_data)
|
|
922
1186
|
|
|
923
1187
|
|
|
924
1188
|
def import_scanparser(station, experiment):
|
|
925
1189
|
"""Given the name of a CHESS station and experiment type, import
|
|
926
1190
|
the corresponding subclass of `ScanParser` as `ScanParser`.
|
|
927
1191
|
|
|
928
|
-
:param station: The station name
|
|
1192
|
+
:param station: The station name
|
|
1193
|
+
('IDxx', not the beamline acronym).
|
|
929
1194
|
:type station: str
|
|
930
|
-
:param experiment: The experiment type
|
|
931
|
-
:type experiment: Literal[
|
|
932
|
-
|
|
1195
|
+
:param experiment: The experiment type.
|
|
1196
|
+
:type experiment: Literal[
|
|
1197
|
+
'SAXSWAXS', 'EDD', 'XRF', 'Tomo', 'Powder']
|
|
933
1198
|
"""
|
|
934
1199
|
|
|
935
1200
|
station = station.lower()
|