ChessAnalysisPipeline 0.0.12__py3-none-any.whl → 0.0.14__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 +2 -0
- CHAP/common/__init__.py +7 -2
- CHAP/common/models/map.py +95 -70
- CHAP/common/processor.py +844 -153
- CHAP/common/reader.py +168 -131
- CHAP/common/writer.py +166 -96
- CHAP/edd/__init__.py +2 -0
- CHAP/edd/models.py +94 -48
- CHAP/edd/processor.py +625 -169
- CHAP/edd/utils.py +186 -6
- CHAP/pipeline.py +35 -3
- CHAP/runner.py +40 -13
- CHAP/tomo/models.py +18 -9
- CHAP/tomo/processor.py +1134 -902
- CHAP/utils/fit.py +98 -45
- CHAP/utils/general.py +196 -63
- CHAP/utils/scanparsers.py +403 -94
- {ChessAnalysisPipeline-0.0.12.dist-info → ChessAnalysisPipeline-0.0.14.dist-info}/METADATA +1 -1
- {ChessAnalysisPipeline-0.0.12.dist-info → ChessAnalysisPipeline-0.0.14.dist-info}/RECORD +23 -23
- {ChessAnalysisPipeline-0.0.12.dist-info → ChessAnalysisPipeline-0.0.14.dist-info}/WHEEL +1 -1
- {ChessAnalysisPipeline-0.0.12.dist-info → ChessAnalysisPipeline-0.0.14.dist-info}/LICENSE +0 -0
- {ChessAnalysisPipeline-0.0.12.dist-info → ChessAnalysisPipeline-0.0.14.dist-info}/entry_points.txt +0 -0
- {ChessAnalysisPipeline-0.0.12.dist-info → ChessAnalysisPipeline-0.0.14.dist-info}/top_level.txt +0 -0
CHAP/utils/scanparsers.py
CHANGED
|
@@ -15,6 +15,9 @@ import numpy as np
|
|
|
15
15
|
from pyspec.file.spec import FileSpec
|
|
16
16
|
from pyspec.file.tiff import TiffFile
|
|
17
17
|
|
|
18
|
+
@cache
|
|
19
|
+
def filespec(spec_file_name):
|
|
20
|
+
return FileSpec(spec_file_name)
|
|
18
21
|
|
|
19
22
|
class ScanParser:
|
|
20
23
|
"""Partial implementation of a class representing a SPEC scan and
|
|
@@ -49,7 +52,12 @@ class ScanParser:
|
|
|
49
52
|
|
|
50
53
|
self._detector_data_path = None
|
|
51
54
|
|
|
52
|
-
|
|
55
|
+
if isinstance(self, FMBRotationScanParser) and scan_number > 1:
|
|
56
|
+
scanparser = FMBRotationScanParser(spec_file_name, scan_number-1)
|
|
57
|
+
if (scanparser.spec_macro in ('rams4_step_ome', 'rams4_fly_ome')
|
|
58
|
+
and len(scanparser.spec_args) == 5):
|
|
59
|
+
self._rams4_args = scanparser.spec_args
|
|
60
|
+
|
|
53
61
|
return (f'{self.__class__.__name__}'
|
|
54
62
|
f'({self.spec_file_name}, {self.scan_number}) '
|
|
55
63
|
f'-- {self.spec_command}')
|
|
@@ -59,7 +67,7 @@ class ScanParser:
|
|
|
59
67
|
# NB This FileSpec instance is not stored as a private
|
|
60
68
|
# attribute because it cannot be pickled (and therefore could
|
|
61
69
|
# cause problems for parallel code that uses ScanParsers).
|
|
62
|
-
return
|
|
70
|
+
return filespec(self.spec_file_name)
|
|
63
71
|
|
|
64
72
|
@property
|
|
65
73
|
def scan_path(self):
|
|
@@ -318,6 +326,10 @@ class SMBScanParser(ScanParser):
|
|
|
318
326
|
json_files = fnmatch_filter(
|
|
319
327
|
os.listdir(self.scan_path),
|
|
320
328
|
f'{self._par_file_pattern}.json')
|
|
329
|
+
if not json_files:
|
|
330
|
+
json_files = fnmatch_filter(
|
|
331
|
+
os.listdir(self.scan_path),
|
|
332
|
+
f'*.json')
|
|
321
333
|
if len(json_files) != 1:
|
|
322
334
|
raise RuntimeError(f'{self.scan_title}: cannot find the '
|
|
323
335
|
'.json file to decode the .par file')
|
|
@@ -331,15 +343,19 @@ class SMBScanParser(ScanParser):
|
|
|
331
343
|
raise RuntimeError(f'{self.scan_title}: cannot find scan pars '
|
|
332
344
|
'without a "SCAN_N" column in the par file')
|
|
333
345
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
346
|
+
if hasattr(self, '_par_file'):
|
|
347
|
+
par_file = self._par_file
|
|
348
|
+
else:
|
|
349
|
+
par_files = fnmatch_filter(
|
|
350
|
+
os.listdir(self.scan_path),
|
|
351
|
+
f'{self._par_file_pattern}.par')
|
|
352
|
+
if len(par_files) != 1:
|
|
353
|
+
raise RuntimeError(f'{self.scan_title}: cannot find the .par '
|
|
354
|
+
'file for this scan directory')
|
|
355
|
+
par_file = os.path.join(self.scan_path, par_files[0])
|
|
340
356
|
par_dict = None
|
|
341
|
-
with open(
|
|
342
|
-
par_reader = reader(
|
|
357
|
+
with open(par_file) as f:
|
|
358
|
+
par_reader = reader(f, delimiter=' ')
|
|
343
359
|
for row in par_reader:
|
|
344
360
|
if len(row) == len(par_col_names):
|
|
345
361
|
row_scann = int(row[scann_col_idx])
|
|
@@ -526,8 +542,18 @@ class FMBLinearScanParser(LinearScanParser, FMBScanParser):
|
|
|
526
542
|
"""
|
|
527
543
|
|
|
528
544
|
def get_spec_scan_motor_mnes(self):
|
|
529
|
-
if self.spec_macro
|
|
530
|
-
|
|
545
|
+
if self.spec_macro in ('flymesh', 'mesh', 'flydmesh', 'dmesh'):
|
|
546
|
+
m1_mne = self.spec_args[0]
|
|
547
|
+
try:
|
|
548
|
+
# Try post-summer-2022 format
|
|
549
|
+
dwell = float(self.spec_args[4])
|
|
550
|
+
except:
|
|
551
|
+
# Accommodate pre-summer-2022 format
|
|
552
|
+
m2_mne_i = 4
|
|
553
|
+
else:
|
|
554
|
+
m2_mne_i = 5
|
|
555
|
+
m2_mne = self.spec_args[m2_mne_i]
|
|
556
|
+
return (m1_mne, m2_mne)
|
|
531
557
|
if self.spec_macro in ('flyscan', 'ascan'):
|
|
532
558
|
return (self.spec_args[0],)
|
|
533
559
|
if self.spec_macro in ('tseries', 'loopscan'):
|
|
@@ -536,13 +562,27 @@ class FMBLinearScanParser(LinearScanParser, FMBScanParser):
|
|
|
536
562
|
f'for scans of type {self.spec_macro}')
|
|
537
563
|
|
|
538
564
|
def get_spec_scan_motor_vals(self):
|
|
539
|
-
if self.spec_macro
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
565
|
+
if self.spec_macro in ('flymesh', 'mesh', 'flydmesh', 'dmesh'):
|
|
566
|
+
m1_start = float(self.spec_args[1])
|
|
567
|
+
m1_end = float(self.spec_args[2])
|
|
568
|
+
m1_npt = int(self.spec_args[3]) + 1
|
|
569
|
+
try:
|
|
570
|
+
# Try post-summer-2022 format
|
|
571
|
+
dwell = float(self.spec_args[4])
|
|
572
|
+
except:
|
|
573
|
+
# Accommodate pre-summer-2022 format
|
|
574
|
+
m2_start_i = 5
|
|
575
|
+
m2_end_i = 6
|
|
576
|
+
m2_nint_i = 7
|
|
577
|
+
else:
|
|
578
|
+
m2_start_i = 6
|
|
579
|
+
m2_end_i = 7
|
|
580
|
+
m2_nint_i = 8
|
|
581
|
+
m2_start = float(self.spec_args[m2_start_i])
|
|
582
|
+
m2_end = float(self.spec_args[m2_end_i])
|
|
583
|
+
m2_npt = int(self.spec_args[m2_nint_i]) + 1
|
|
584
|
+
fast_mot_vals = np.linspace(m1_start, m1_end, m1_npt)
|
|
585
|
+
slow_mot_vals = np.linspace(m2_start, m2_end, m2_npt)
|
|
546
586
|
return (fast_mot_vals, slow_mot_vals)
|
|
547
587
|
if self.spec_macro in ('flyscan', 'ascan'):
|
|
548
588
|
mot_vals = np.linspace(float(self.spec_args[1]),
|
|
@@ -555,9 +595,17 @@ class FMBLinearScanParser(LinearScanParser, FMBScanParser):
|
|
|
555
595
|
f'for scans of type {self.spec_macro}')
|
|
556
596
|
|
|
557
597
|
def get_spec_scan_shape(self):
|
|
558
|
-
if self.spec_macro
|
|
559
|
-
fast_mot_npts = int(self.spec_args[3])+1
|
|
560
|
-
|
|
598
|
+
if self.spec_macro in ('flymesh', 'mesh', 'flydmesh', 'dmesh'):
|
|
599
|
+
fast_mot_npts = int(self.spec_args[3]) + 1
|
|
600
|
+
try:
|
|
601
|
+
# Try post-summer-2022 format
|
|
602
|
+
dwell = float(self.spec_args[4])
|
|
603
|
+
except:
|
|
604
|
+
# Accommodate pre-summer-2022 format
|
|
605
|
+
m2_nint_i = 7
|
|
606
|
+
else:
|
|
607
|
+
m2_nint_i = 8
|
|
608
|
+
slow_mot_npts = int(self.spec_args[m2_nint_i]) + 1
|
|
561
609
|
return (fast_mot_npts, slow_mot_npts)
|
|
562
610
|
if self.spec_macro in ('flyscan', 'ascan'):
|
|
563
611
|
mot_npts = int(self.spec_args[3])+1
|
|
@@ -568,7 +616,15 @@ class FMBLinearScanParser(LinearScanParser, FMBScanParser):
|
|
|
568
616
|
f'for scans of type {self.spec_macro}')
|
|
569
617
|
|
|
570
618
|
def get_spec_scan_dwell(self):
|
|
571
|
-
if self.
|
|
619
|
+
if self.macro in ('flymesh', 'mesh', 'flydmesh', 'dmesh'):
|
|
620
|
+
try:
|
|
621
|
+
# Try post-summer-2022 format
|
|
622
|
+
dwell = float(self.spec_args[4])
|
|
623
|
+
except:
|
|
624
|
+
# Accommodate pre-summer-2022 format
|
|
625
|
+
dwell = float(self.spec_args[8])
|
|
626
|
+
return dwell
|
|
627
|
+
if self.spec_macro in ('flyscan', 'ascan'):
|
|
572
628
|
return float(self.spec_args[4])
|
|
573
629
|
if self.spec_macro in ('tseries', 'loopscan'):
|
|
574
630
|
return float(self.spec_args[1])
|
|
@@ -609,8 +665,10 @@ class FMBSAXSWAXSScanParser(FMBLinearScanParser):
|
|
|
609
665
|
return f'{self.scan_name}_{self.scan_number:03d}'
|
|
610
666
|
|
|
611
667
|
def get_detector_data_file(self, detector_prefix, scan_step_index:int):
|
|
612
|
-
detector_files = list_fmb_saxswaxs_detector_files(
|
|
613
|
-
|
|
668
|
+
detector_files = list_fmb_saxswaxs_detector_files(
|
|
669
|
+
self.detector_data_path, detector_prefix)
|
|
670
|
+
return os.path.join(
|
|
671
|
+
self.detector_data_path, detector_files[scan_step_index])
|
|
614
672
|
|
|
615
673
|
def get_detector_data(self, detector_prefix, scan_step_index:int):
|
|
616
674
|
image_file = self.get_detector_data_file(detector_prefix,
|
|
@@ -657,7 +715,15 @@ class SMBLinearScanParser(LinearScanParser, SMBScanParser):
|
|
|
657
715
|
"""
|
|
658
716
|
|
|
659
717
|
def get_spec_scan_dwell(self):
|
|
660
|
-
if self.spec_macro in ('flymesh', '
|
|
718
|
+
if self.spec_macro in ('flymesh', 'mesh'):
|
|
719
|
+
try:
|
|
720
|
+
# Try post-summer-2022 format
|
|
721
|
+
dwell = float(self.spec_args[4])
|
|
722
|
+
except:
|
|
723
|
+
# Accommodate pre-summer-2022 format
|
|
724
|
+
dwell = float(self.spec_args[8])
|
|
725
|
+
return dwell
|
|
726
|
+
if self.spec_macro in ('flyscan', 'ascan'):
|
|
661
727
|
return float(self.spec_args[4])
|
|
662
728
|
if self.spec_macro == 'tseries':
|
|
663
729
|
return float(self.spec_args[1])
|
|
@@ -667,8 +733,18 @@ class SMBLinearScanParser(LinearScanParser, SMBScanParser):
|
|
|
667
733
|
f'for scans of type {self.spec_macro}')
|
|
668
734
|
|
|
669
735
|
def get_spec_scan_motor_mnes(self):
|
|
670
|
-
if self.spec_macro
|
|
671
|
-
|
|
736
|
+
if self.spec_macro in ('flymesh', 'mesh'):
|
|
737
|
+
m1_mne = self.spec_args[0]
|
|
738
|
+
try:
|
|
739
|
+
# Try post-summer-2022 format
|
|
740
|
+
dwell = float(self.spec_args[4])
|
|
741
|
+
except:
|
|
742
|
+
# Accommodate pre-summer-2022 format
|
|
743
|
+
m2_mne_i = 4
|
|
744
|
+
else:
|
|
745
|
+
m2_mne_i = 5
|
|
746
|
+
m2_mne = self.spec_args[m2_mne_i]
|
|
747
|
+
return (m1_mne, m2_mne)
|
|
672
748
|
if self.spec_macro in ('flyscan', 'ascan'):
|
|
673
749
|
return (self.spec_args[0],)
|
|
674
750
|
if self.spec_macro in ('tseries', 'loopscan'):
|
|
@@ -677,13 +753,27 @@ class SMBLinearScanParser(LinearScanParser, SMBScanParser):
|
|
|
677
753
|
f'for scans of type {self.spec_macro}')
|
|
678
754
|
|
|
679
755
|
def get_spec_scan_motor_vals(self):
|
|
680
|
-
if self.spec_macro
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
756
|
+
if self.spec_macro in ('flymesh', 'mesh'):
|
|
757
|
+
m1_start = float(self.spec_args[1])
|
|
758
|
+
m1_end = float(self.spec_args[2])
|
|
759
|
+
m1_npt = int(self.spec_args[3]) + 1
|
|
760
|
+
try:
|
|
761
|
+
# Try post-summer-2022 format
|
|
762
|
+
dwell = float(self.spec_args[4])
|
|
763
|
+
except:
|
|
764
|
+
# Accommodate pre-summer-2022 format
|
|
765
|
+
m2_start_i = 5
|
|
766
|
+
m2_end_i = 6
|
|
767
|
+
m2_nint_i = 7
|
|
768
|
+
else:
|
|
769
|
+
m2_start_i = 6
|
|
770
|
+
m2_end_i = 7
|
|
771
|
+
m2_nint_i = 8
|
|
772
|
+
m2_start = float(self.spec_args[m2_start_i])
|
|
773
|
+
m2_end = float(self.spec_args[m2_end_i])
|
|
774
|
+
m2_npt = int(self.spec_args[m2_nint_i]) + 1
|
|
775
|
+
fast_mot_vals = np.linspace(m1_start, m1_end, m1_npt)
|
|
776
|
+
slow_mot_vals = np.linspace(m2_start, m2_end, m2_npt)
|
|
687
777
|
return (fast_mot_vals, slow_mot_vals)
|
|
688
778
|
if self.spec_macro in ('flyscan', 'ascan'):
|
|
689
779
|
mot_vals = np.linspace(float(self.spec_args[1]),
|
|
@@ -691,31 +781,31 @@ class SMBLinearScanParser(LinearScanParser, SMBScanParser):
|
|
|
691
781
|
int(self.spec_args[3])+1)
|
|
692
782
|
return (mot_vals,)
|
|
693
783
|
if self.spec_macro in ('tseries', 'loopscan'):
|
|
694
|
-
return self.spec_scan.data[:,0]
|
|
784
|
+
return (self.spec_scan.data[:,0],)
|
|
695
785
|
raise RuntimeError(f'{self.scan_title}: cannot determine scan motors '
|
|
696
786
|
f'for scans of type {self.spec_macro}')
|
|
697
787
|
|
|
698
788
|
def get_spec_scan_shape(self):
|
|
699
|
-
if self.spec_macro
|
|
700
|
-
fast_mot_npts = int(self.spec_args[3])+1
|
|
701
|
-
|
|
789
|
+
if self.spec_macro in ('flymesh', 'mesh'):
|
|
790
|
+
fast_mot_npts = int(self.spec_args[3]) + 1
|
|
791
|
+
try:
|
|
792
|
+
# Try post-summer-2022 format
|
|
793
|
+
dwell = float(self.spec_args[4])
|
|
794
|
+
except:
|
|
795
|
+
# Accommodate pre-summer-2022 format
|
|
796
|
+
m2_nint_i = 7
|
|
797
|
+
else:
|
|
798
|
+
m2_nint_i = 8
|
|
799
|
+
slow_mot_npts = int(self.spec_args[m2_nint_i]) + 1
|
|
702
800
|
return (fast_mot_npts, slow_mot_npts)
|
|
703
801
|
if self.spec_macro in ('flyscan', 'ascan'):
|
|
704
802
|
mot_npts = int(self.spec_args[3])+1
|
|
705
803
|
return (mot_npts,)
|
|
706
804
|
if self.spec_macro in ('tseries', 'loopscan'):
|
|
707
|
-
return len(np.array(self.spec_scan.data[:,0]))
|
|
805
|
+
return (len(np.array(self.spec_scan.data[:,0])),)
|
|
708
806
|
raise RuntimeError(f'{self.scan_title}: cannot determine scan shape '
|
|
709
807
|
f'for scans of type {self.spec_macro}')
|
|
710
808
|
|
|
711
|
-
def get_spec_scan_dwell(self):
|
|
712
|
-
if self.spec_macro == 'flymesh':
|
|
713
|
-
return float(self.spec_args[4])
|
|
714
|
-
if self.spec_macro in ('flyscan', 'ascan'):
|
|
715
|
-
return float(self.spec_args[-1])
|
|
716
|
-
raise RuntimeError(f'{self.scan_title}: cannot determine dwell time '
|
|
717
|
-
f'for scans of type {self.spec_macro}')
|
|
718
|
-
|
|
719
809
|
def get_detector_data_path(self):
|
|
720
810
|
return os.path.join(self.scan_path, str(self.scan_number))
|
|
721
811
|
|
|
@@ -793,7 +883,17 @@ class FMBRotationScanParser(RotationScanParser, FMBScanParser):
|
|
|
793
883
|
with the typical tomography setup at FMB.
|
|
794
884
|
"""
|
|
795
885
|
|
|
886
|
+
def get_spec_scan_data(self):
|
|
887
|
+
spec_scan_data = super().get_spec_scan_data()
|
|
888
|
+
if hasattr(self, '_rams4_args'):
|
|
889
|
+
spec_scan_data['theta'] = np.linspace(
|
|
890
|
+
float(self._rams4_args[0]), float(self._rams4_args[1]),
|
|
891
|
+
1+int(self._rams4_args[2]))
|
|
892
|
+
return spec_scan_data
|
|
893
|
+
|
|
796
894
|
def get_spec_scan_npts(self):
|
|
895
|
+
if hasattr(self, '_rams4_args'):
|
|
896
|
+
return 1+int(self._rams4_args[2])
|
|
797
897
|
if self.spec_macro == 'flyscan':
|
|
798
898
|
if len(self.spec_args) == 2:
|
|
799
899
|
return 1+int(self.spec_args[0])
|
|
@@ -802,12 +902,12 @@ class FMBRotationScanParser(RotationScanParser, FMBScanParser):
|
|
|
802
902
|
raise RuntimeError(f'{self.scan_title}: cannot obtain number of '
|
|
803
903
|
f'points from {self.spec_macro} with arguments '
|
|
804
904
|
f'{self.spec_args}')
|
|
805
|
-
|
|
806
|
-
if len(self.spec_args) == 5:
|
|
807
|
-
return int(self.spec_args[3])
|
|
808
|
-
raise RuntimeError(f'{self.scan_title}: cannot obtain number of '
|
|
809
|
-
f'points from {self.spec_macro} with arguments '
|
|
810
|
-
f'{self.spec_args}')
|
|
905
|
+
# if self.spec_macro == 'ascan':
|
|
906
|
+
# if len(self.spec_args) == 5:
|
|
907
|
+
# return int(self.spec_args[3])
|
|
908
|
+
# raise RuntimeError(f'{self.scan_title}: cannot obtain number of '
|
|
909
|
+
# f'points from {self.spec_macro} with arguments '
|
|
910
|
+
# f'{self.spec_args}')
|
|
811
911
|
raise RuntimeError(f'{self.scan_title}: cannot determine rotation '
|
|
812
912
|
f' angles for scans of type {self.spec_macro}')
|
|
813
913
|
|
|
@@ -815,21 +915,10 @@ class FMBRotationScanParser(RotationScanParser, FMBScanParser):
|
|
|
815
915
|
return 0
|
|
816
916
|
|
|
817
917
|
def get_starting_image_offset(self):
|
|
918
|
+
if hasattr(self, '_rams4_args'):
|
|
919
|
+
return int(self.spec_args[0]) - self.spec_scan_npts
|
|
818
920
|
if self.spec_macro == 'flyscan':
|
|
819
|
-
# if len(self.spec_args) == 2:
|
|
820
|
-
# return 1
|
|
821
|
-
# if len(self.spec_args) == 5:
|
|
822
|
-
# return 1
|
|
823
921
|
return 1
|
|
824
|
-
# raise RuntimeError(f'{self.scan_title}: cannot obtain starting '
|
|
825
|
-
# f'image offset {self.spec_macro} with arguments'
|
|
826
|
-
# f' {self.spec_args}')
|
|
827
|
-
# elif self.spec_macro == 'ascan':
|
|
828
|
-
# if len(self.spec_args) == 5:
|
|
829
|
-
# return 0
|
|
830
|
-
# raise RuntimeError(f'{self.scan_title}: cannot obtain starting '
|
|
831
|
-
# f'image offset {self.spec_macro} with arguments'
|
|
832
|
-
# f' {self.spec_args}')
|
|
833
922
|
raise RuntimeError(f'{self.scan_title}: cannot determine starting '
|
|
834
923
|
f'image offset for scans of type {self.spec_macro}')
|
|
835
924
|
|
|
@@ -855,8 +944,6 @@ class FMBRotationScanParser(RotationScanParser, FMBScanParser):
|
|
|
855
944
|
if scan_step_index is None:
|
|
856
945
|
detector_data = h5_file['/entry/instrument/detector/data'][
|
|
857
946
|
self.starting_image_offset:]
|
|
858
|
-
# sum_det = list(np.sum(detector_data, (1,2)))
|
|
859
|
-
# print(f'\n\nsum scanparser ({len(sum_det)}):\n{sum_det}')
|
|
860
947
|
elif isinstance(scan_step_index, int):
|
|
861
948
|
detector_data = h5_file['/entry/instrument/detector/data'][
|
|
862
949
|
self.starting_image_offset+scan_step_index]
|
|
@@ -873,10 +960,8 @@ class FMBRotationScanParser(RotationScanParser, FMBScanParser):
|
|
|
873
960
|
def get_detector_data(self, detector_prefix, scan_step_index=None):
|
|
874
961
|
try:
|
|
875
962
|
# Detector files in h5 format
|
|
876
|
-
# print('data in h5 file')
|
|
877
963
|
detector_data = self.get_all_detector_data_in_file(
|
|
878
964
|
detector_prefix, scan_step_index)
|
|
879
|
-
# print(f'detector_data {detector_prefix} {scan_step_index}:\n{detector_data.shape}')
|
|
880
965
|
except:
|
|
881
966
|
# Detector files in tiff format
|
|
882
967
|
if scan_step_index is None:
|
|
@@ -921,12 +1006,14 @@ class SMBRotationScanParser(RotationScanParser, SMBScanParser):
|
|
|
921
1006
|
with the typical tomography setup at SMB.
|
|
922
1007
|
"""
|
|
923
1008
|
|
|
924
|
-
def __init__(self, spec_file_name, scan_number):
|
|
1009
|
+
def __init__(self, spec_file_name, scan_number, par_file=None):
|
|
925
1010
|
self._scan_type = None
|
|
926
1011
|
super().__init__(spec_file_name, scan_number)
|
|
927
1012
|
|
|
928
1013
|
self._katefix = 0 # RV remove when no longer needed
|
|
929
1014
|
self._par_file_pattern = f'id*-*tomo*-{self.scan_name}'
|
|
1015
|
+
if par_file is not None:
|
|
1016
|
+
self._par_file = par_file
|
|
930
1017
|
|
|
931
1018
|
@property
|
|
932
1019
|
def scan_type(self):
|
|
@@ -996,22 +1083,20 @@ class SMBRotationScanParser(RotationScanParser, SMBScanParser):
|
|
|
996
1083
|
if os.path.isfile(file_name_full):
|
|
997
1084
|
return file_name_full
|
|
998
1085
|
raise RuntimeError(f'{self.scan_title}: could not find detector image '
|
|
999
|
-
f'file for scan step
|
|
1086
|
+
f'file ({file_name_full}) for scan step '
|
|
1087
|
+
f'({scan_step_index})')
|
|
1000
1088
|
|
|
1001
1089
|
def get_detector_data(self, detector_prefix, scan_step_index=None):
|
|
1002
|
-
# print(f'\n\nin get_detector_data: {detector_prefix} {scan_step_index}')
|
|
1003
1090
|
if scan_step_index is None:
|
|
1004
1091
|
detector_data = []
|
|
1005
1092
|
for index in range(self.spec_scan_npts):
|
|
1006
1093
|
detector_data.append(
|
|
1007
1094
|
self.get_detector_data(detector_prefix, index))
|
|
1008
1095
|
detector_data = np.asarray(detector_data)
|
|
1009
|
-
# print(f'detector_data shape {type(detector_data)} {detector_data.shape}:\n{detector_data}')
|
|
1010
1096
|
elif isinstance(scan_step_index, int):
|
|
1011
1097
|
image_file = self.get_detector_data_file(scan_step_index)
|
|
1012
1098
|
with TiffFile(image_file) as tiff_file:
|
|
1013
1099
|
detector_data = tiff_file.asarray()
|
|
1014
|
-
# print(f'\t{scan_step_index} {image_file} {np.sum(np.asarray(detector_data))}')
|
|
1015
1100
|
elif (isinstance(scan_step_index, (list, tuple))
|
|
1016
1101
|
and len(scan_step_index) == 2):
|
|
1017
1102
|
detector_data = []
|
|
@@ -1051,10 +1136,66 @@ class SMBMCAScanParser(MCAScanParser, SMBLinearScanParser):
|
|
|
1051
1136
|
"""Concrete implementation of a class representing a scan taken
|
|
1052
1137
|
with the typical EDD setup at SMB or FAST.
|
|
1053
1138
|
"""
|
|
1139
|
+
detector_data_formats = ('spec', 'h5')
|
|
1140
|
+
def __init__(self, spec_file_name, scan_number, detector_data_format=None):
|
|
1141
|
+
"""Constructor for SMBMCAScnaParser.
|
|
1142
|
+
|
|
1143
|
+
:param spec_file: Path to scan's SPEC file
|
|
1144
|
+
:type spec_file: str
|
|
1145
|
+
:param scan_number: Number of the SPEC scan
|
|
1146
|
+
:type scan_number: int
|
|
1147
|
+
:param detector_data_format: Format of the MCA data collected,
|
|
1148
|
+
defaults to None
|
|
1149
|
+
:type detector_data_format: Optional[Literal["spec", "h5"]]
|
|
1150
|
+
"""
|
|
1151
|
+
super().__init__(spec_file_name, scan_number)
|
|
1054
1152
|
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1153
|
+
self.detector_data_format = None
|
|
1154
|
+
if detector_data_format is None:
|
|
1155
|
+
self.init_detector_data_format()
|
|
1156
|
+
else:
|
|
1157
|
+
if detector_data_format.lower() in self.detector_data_formats:
|
|
1158
|
+
self.detector_data_format = detector_data_format.lower()
|
|
1159
|
+
else:
|
|
1160
|
+
raise ValueError(
|
|
1161
|
+
'Unrecognized value for detector_data_format: '
|
|
1162
|
+
+ f'{detector_data_format}. Allowed values are: '
|
|
1163
|
+
+ ', '.join(self.detector_data_formats))
|
|
1164
|
+
|
|
1165
|
+
def init_detector_data_format(self):
|
|
1166
|
+
"""Determine and set a value for the instance variable
|
|
1167
|
+
`detector_data_format` based on the presence / absence of
|
|
1168
|
+
detector data files of different formats conventionally
|
|
1169
|
+
associated with this scan. Also set the corresponding
|
|
1170
|
+
appropriate value for `_detector_data_path`.
|
|
1171
|
+
"""
|
|
1172
|
+
try:
|
|
1173
|
+
self._detector_data_path = self.scan_path
|
|
1174
|
+
detector_file = self.get_detector_data_file_spec()
|
|
1175
|
+
except OSError:
|
|
1176
|
+
try:
|
|
1177
|
+
self._detector_data_path = os.path.join(
|
|
1178
|
+
self.scan_path, str(self.scan_number), 'edd')
|
|
1179
|
+
detector_file = self.get_detector_data_file_h5()
|
|
1180
|
+
except OSError:
|
|
1181
|
+
raise RuntimeError(
|
|
1182
|
+
f"{self.scan_title}: Can't determine detector data format")
|
|
1183
|
+
else:
|
|
1184
|
+
self.detector_data_format = 'h5'
|
|
1185
|
+
else:
|
|
1186
|
+
self.detector_data_format = 'spec'
|
|
1187
|
+
|
|
1188
|
+
def get_detector_data_path(self):
|
|
1189
|
+
raise NotImplementedError
|
|
1190
|
+
|
|
1191
|
+
def get_detector_num_bins(self, element_index=0):
|
|
1192
|
+
if self.detector_data_format == 'spec':
|
|
1193
|
+
return self.get_detector_num_bins_spec()
|
|
1194
|
+
elif self.detector_data_format == 'h5':
|
|
1195
|
+
return self.get_detector_num_bins_h5(element_index)
|
|
1196
|
+
|
|
1197
|
+
def get_detector_num_bins_spec(self):
|
|
1198
|
+
with open(self.get_detector_data_file_spec()) as detector_file:
|
|
1058
1199
|
lines = detector_file.readlines()
|
|
1059
1200
|
for line in lines:
|
|
1060
1201
|
if line.startswith('#@CHANN'):
|
|
@@ -1064,33 +1205,101 @@ class SMBMCAScanParser(MCAScanParser, SMBLinearScanParser):
|
|
|
1064
1205
|
return int(number_saved)
|
|
1065
1206
|
except:
|
|
1066
1207
|
continue
|
|
1067
|
-
raise RuntimeError(f'{self.scan_title}: could not find num_bins
|
|
1068
|
-
f'detector {detector_prefix}')
|
|
1069
|
-
|
|
1070
|
-
def get_detector_data_path(self):
|
|
1071
|
-
return self.scan_path
|
|
1208
|
+
raise RuntimeError(f'{self.scan_title}: could not find num_bins')
|
|
1072
1209
|
|
|
1073
|
-
def
|
|
1210
|
+
def get_detector_num_bins_h5(self, element_index):
|
|
1211
|
+
from h5py import File
|
|
1212
|
+
detector_file = self.get_detector_data_file_h5()
|
|
1213
|
+
with File(detector_file) as h5_file:
|
|
1214
|
+
dset_shape = h5_file['/entry/data/data'].shape
|
|
1215
|
+
return dset_shape[-1]
|
|
1216
|
+
|
|
1217
|
+
def get_detector_data_file(self, scan_step_index=0):
|
|
1218
|
+
if self.detector_data_format == 'spec':
|
|
1219
|
+
return self.get_detector_data_file_spec()
|
|
1220
|
+
elif self.detector_data_format == 'h5':
|
|
1221
|
+
return self.get_detector_data_file_h5(
|
|
1222
|
+
scan_step_index=scan_step_index)
|
|
1223
|
+
|
|
1224
|
+
def get_detector_data_file_spec(self):
|
|
1225
|
+
"""Return the filename (full absolute path) to the file
|
|
1226
|
+
containing spec-formatted MCA data for this scan.
|
|
1227
|
+
"""
|
|
1074
1228
|
file_name = f'spec.log.scan{self.scan_number}.mca1.mca'
|
|
1075
1229
|
file_name_full = os.path.join(self.detector_data_path, file_name)
|
|
1076
1230
|
if os.path.isfile(file_name_full):
|
|
1077
1231
|
return file_name_full
|
|
1078
|
-
raise
|
|
1079
|
-
|
|
1232
|
+
raise OSError(
|
|
1233
|
+
'{self.scan_title}: could not find detector image file'
|
|
1234
|
+
)
|
|
1235
|
+
|
|
1236
|
+
def get_detector_data_file_h5(self, scan_step_index=0):
|
|
1237
|
+
"""Return the filename (full absolute path) to the file
|
|
1238
|
+
containing h5-formatted MCA data for this scan.
|
|
1239
|
+
|
|
1240
|
+
:param scan_step_index:
|
|
1241
|
+
"""
|
|
1242
|
+
scan_step = self.get_scan_step(scan_step_index)
|
|
1243
|
+
if len(self.spec_scan_shape) == 1:
|
|
1244
|
+
filename_index = 0
|
|
1245
|
+
elif len(self.spec_scan_shape) == 2:
|
|
1246
|
+
scan_step = self.get_scan_step(scan_step_index)
|
|
1247
|
+
filename_index = scan_step[0]
|
|
1248
|
+
else:
|
|
1249
|
+
raise NotImplementedError(
|
|
1250
|
+
'Cannot find detector file for scans with dimension > 2')
|
|
1251
|
+
file_name = list_smb_mca_detector_files_h5(
|
|
1252
|
+
self.detector_data_path)[filename_index]
|
|
1253
|
+
file_name_full = os.path.join(self.detector_data_path, file_name)
|
|
1254
|
+
if os.path.isfile(file_name_full):
|
|
1255
|
+
return file_name_full
|
|
1256
|
+
raise OSError(
|
|
1257
|
+
'{self.scan_title}: could not find detector image file'
|
|
1258
|
+
)
|
|
1259
|
+
|
|
1080
1260
|
|
|
1081
|
-
def get_all_detector_data(self,
|
|
1261
|
+
def get_all_detector_data(self, detector):
|
|
1262
|
+
"""Return a 2D array of all MCA spectra collected in this scan
|
|
1263
|
+
by the detector element indicated with `detector`.
|
|
1264
|
+
|
|
1265
|
+
:param detector: For detector data collected in SPEC format,
|
|
1266
|
+
this is the detector prefix as it appears in the spec MCA
|
|
1267
|
+
data file. For detector data collected in H5 format, this
|
|
1268
|
+
is the index of a particular detector element.
|
|
1269
|
+
:type detector: Union[str, int]
|
|
1270
|
+
:rtype: numpy.ndarray
|
|
1271
|
+
"""
|
|
1272
|
+
if self.detector_data_format == 'spec':
|
|
1273
|
+
return self.get_all_detector_data_spec(detector)
|
|
1274
|
+
elif self.detector_data_format == 'h5':
|
|
1275
|
+
try:
|
|
1276
|
+
element_index = int(detector)
|
|
1277
|
+
except:
|
|
1278
|
+
raise TypeError(f'{detector} is not an integer element index')
|
|
1279
|
+
return self.get_all_detector_data_h5(element_index)
|
|
1280
|
+
|
|
1281
|
+
def get_all_detector_data_spec(self, detector_prefix):
|
|
1282
|
+
"""Return a 2D array of all MCA spectra collected by a
|
|
1283
|
+
detector in the spec MCA file format during the scan.
|
|
1284
|
+
|
|
1285
|
+
:param detector_prefix: Detector name at is appears in the
|
|
1286
|
+
spec MCA file.
|
|
1287
|
+
:type detector_prefix: str
|
|
1288
|
+
:returns: 2D array of MCA spectra
|
|
1289
|
+
:rtype: numpy.ndarray
|
|
1290
|
+
"""
|
|
1082
1291
|
# This should be easy with pyspec, but there are bugs in
|
|
1083
1292
|
# pyspec for MCA data..... or is the 'bug' from a nonstandard
|
|
1084
1293
|
# implementation of some macro on our end? According to spec
|
|
1085
1294
|
# manual and pyspec code, mca data should always begin w/ '@A'
|
|
1086
|
-
# In example scans, it begins with '@
|
|
1295
|
+
# In example scans, it begins with '@{detector_prefix}'
|
|
1296
|
+
# instead
|
|
1087
1297
|
data = []
|
|
1088
1298
|
|
|
1089
|
-
with open(self.
|
|
1090
|
-
as detector_file:
|
|
1299
|
+
with open(self.get_detector_data_file_spec()) as detector_file:
|
|
1091
1300
|
lines = [line.strip("\\\n") for line in detector_file.readlines()]
|
|
1092
1301
|
|
|
1093
|
-
num_bins = self.get_detector_num_bins(
|
|
1302
|
+
num_bins = self.get_detector_num_bins()
|
|
1094
1303
|
|
|
1095
1304
|
counter = 0
|
|
1096
1305
|
for line in lines:
|
|
@@ -1117,6 +1326,106 @@ class SMBMCAScanParser(MCAScanParser, SMBLinearScanParser):
|
|
|
1117
1326
|
|
|
1118
1327
|
return np.array(data)
|
|
1119
1328
|
|
|
1120
|
-
def
|
|
1121
|
-
|
|
1329
|
+
def get_all_detector_data_h5(self, element_index):
|
|
1330
|
+
"""Return a 2D array of all MCA spectra collected by a
|
|
1331
|
+
detector in the h5 file format during the scan.
|
|
1332
|
+
|
|
1333
|
+
:param element_index: The index of a particualr MCA element to
|
|
1334
|
+
return data for.
|
|
1335
|
+
:type element_index: int
|
|
1336
|
+
:returns: 2D array of MCA spectra
|
|
1337
|
+
:rtype: numpy.ndarray
|
|
1338
|
+
"""
|
|
1339
|
+
detector_data = np.empty(
|
|
1340
|
+
(self.spec_scan_npts,
|
|
1341
|
+
self.get_detector_num_bins_h5(element_index)))
|
|
1342
|
+
detector_files = list_smb_mca_detector_files_h5(
|
|
1343
|
+
self.detector_data_path)
|
|
1344
|
+
for i, detector_file in enumerate(detector_files):
|
|
1345
|
+
full_filename = os.path.join(
|
|
1346
|
+
self.detector_data_path, detector_file)
|
|
1347
|
+
element_data = get_all_mca_data_h5(
|
|
1348
|
+
full_filename)[:,element_index,:]
|
|
1349
|
+
i_0 = i * self.spec_scan_shape[0]
|
|
1350
|
+
if len(self.spec_scan_shape) == 2:
|
|
1351
|
+
i_f = i_0 + self.spec_scan_shape[1]
|
|
1352
|
+
else:
|
|
1353
|
+
i_f = self.spec_scan_npts
|
|
1354
|
+
detector_data[i_0:i_f] = element_data
|
|
1355
|
+
return detector_data
|
|
1356
|
+
|
|
1357
|
+
def get_detector_data(self, detector, scan_step_index:int):
|
|
1358
|
+
"""Return a single MCA spectrum for the detector indicated.
|
|
1359
|
+
|
|
1360
|
+
:param detector: If this scan collected MCA data in "spec"
|
|
1361
|
+
format, this is the detector prefix as it appears in the
|
|
1362
|
+
spec MCA data file. If this scan collected data in .h5
|
|
1363
|
+
format, this is the index of the detector element of
|
|
1364
|
+
interest.:type detector: typing.Union[str, int]
|
|
1365
|
+
:param scan_step_index: Index of the scan step to return the
|
|
1366
|
+
spectrum from.
|
|
1367
|
+
:type scan_step_index: int
|
|
1368
|
+
:returns: A single MCA spectrum
|
|
1369
|
+
:rtype: numpy.ndarray
|
|
1370
|
+
"""
|
|
1371
|
+
detector_data = self.get_all_detector_data(detector)
|
|
1122
1372
|
return detector_data[scan_step_index]
|
|
1373
|
+
|
|
1374
|
+
@cache
|
|
1375
|
+
def list_smb_mca_detector_files_h5(detector_data_path):
|
|
1376
|
+
"""Return a sorted list of all *.hdf5 files in a directory
|
|
1377
|
+
|
|
1378
|
+
:param detector_data_path: Directory to return *.hdf5 files from
|
|
1379
|
+
:type detector_data_path: str
|
|
1380
|
+
:returns: Sorted list of detector data filenames
|
|
1381
|
+
:rtype: list[str]
|
|
1382
|
+
"""
|
|
1383
|
+
return sorted(
|
|
1384
|
+
[f for f in os.listdir(detector_data_path) if f.endswith('.hdf5')])
|
|
1385
|
+
|
|
1386
|
+
@cache
|
|
1387
|
+
def get_all_mca_data_h5(filename):
|
|
1388
|
+
"""Return all data from all elements from an MCA data file
|
|
1389
|
+
|
|
1390
|
+
:param filename: Name of the MCA h5 data file
|
|
1391
|
+
:type filename: str
|
|
1392
|
+
:returns: 3D array of MCA spectra where the first axis is scan
|
|
1393
|
+
step, second index is detector element, third index is channel
|
|
1394
|
+
energy.
|
|
1395
|
+
:rtype: numpy.ndarray
|
|
1396
|
+
"""
|
|
1397
|
+
import os
|
|
1398
|
+
|
|
1399
|
+
from h5py import File
|
|
1400
|
+
import numpy as np
|
|
1401
|
+
|
|
1402
|
+
with File(filename) as h5_file:
|
|
1403
|
+
data = h5_file['/entry/data/data'][:]
|
|
1404
|
+
|
|
1405
|
+
# Prior to 2023-12-12, there was an issue where the XPS23 detector
|
|
1406
|
+
# was capturing one or two frames of all 0s at the start of the
|
|
1407
|
+
# dataset in every hdf5 file. In both cases, there is only ONE
|
|
1408
|
+
# extra frame of data relative to the number of frames that should
|
|
1409
|
+
# be there (based on the number of points in the spec scan). If
|
|
1410
|
+
# one frame of all 0s is present: skip it and deliver only the
|
|
1411
|
+
# real data. If two frames of all 0s are present: detector data
|
|
1412
|
+
# will be missing for the LAST step in the scan. Skip the first
|
|
1413
|
+
# two frames of all 0s in the hdf5 dataset, then add a frame of
|
|
1414
|
+
# fake data (all 0-s) to the end of that real data so that the
|
|
1415
|
+
# number of detector data frames matches the number of points in
|
|
1416
|
+
# the spec scan.
|
|
1417
|
+
check_zeros_before = 1702357200
|
|
1418
|
+
file_mtime = os.path.getmtime(filename)
|
|
1419
|
+
if file_mtime <= check_zeros_before:
|
|
1420
|
+
if not np.any(data[0]):
|
|
1421
|
+
# If present, remove first frame of blank data
|
|
1422
|
+
print('Warning: removing blank first frame of detector data')
|
|
1423
|
+
data = data[1:]
|
|
1424
|
+
if not np.any(data[0]):
|
|
1425
|
+
# If present, shift second frame of blank data to the
|
|
1426
|
+
# end
|
|
1427
|
+
print('Warning: shifting second frame of blank detector data '
|
|
1428
|
+
+ 'to the end of the scan')
|
|
1429
|
+
data = np.concatenate((data[1:], np.asarray([data[0]])))
|
|
1430
|
+
|
|
1431
|
+
return data
|