ChessAnalysisPipeline 0.0.11__tar.gz → 0.0.13__tar.gz

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.

Files changed (61) hide show
  1. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/__init__.py +2 -0
  2. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/common/__init__.py +6 -2
  3. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/common/models/map.py +217 -70
  4. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/common/processor.py +249 -155
  5. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/common/reader.py +175 -130
  6. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/common/writer.py +150 -94
  7. ChessAnalysisPipeline-0.0.13/CHAP/edd/models.py +876 -0
  8. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/edd/processor.py +614 -354
  9. ChessAnalysisPipeline-0.0.13/CHAP/edd/utils.py +875 -0
  10. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/tomo/models.py +22 -18
  11. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/tomo/processor.py +1215 -892
  12. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/utils/fit.py +211 -127
  13. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/utils/general.py +789 -610
  14. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/utils/parfile.py +1 -9
  15. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/utils/scanparsers.py +101 -52
  16. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13/ChessAnalysisPipeline.egg-info}/PKG-INFO +2 -1
  17. {ChessAnalysisPipeline-0.0.11/ChessAnalysisPipeline.egg-info → ChessAnalysisPipeline-0.0.13}/PKG-INFO +2 -1
  18. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/setup.py +1 -1
  19. ChessAnalysisPipeline-0.0.11/CHAP/edd/models.py +0 -680
  20. ChessAnalysisPipeline-0.0.11/CHAP/edd/utils.py +0 -364
  21. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/TaskManager.py +0 -0
  22. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/common/models/__init__.py +0 -0
  23. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/common/models/integration.py +0 -0
  24. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/edd/__init__.py +0 -0
  25. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/edd/reader.py +0 -0
  26. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/edd/writer.py +0 -0
  27. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/inference/__init__.py +0 -0
  28. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/inference/processor.py +0 -0
  29. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/inference/reader.py +0 -0
  30. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/inference/writer.py +0 -0
  31. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/pipeline.py +0 -0
  32. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/processor.py +0 -0
  33. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/reader.py +0 -0
  34. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/runner.py +0 -0
  35. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/saxswaxs/__init__.py +0 -0
  36. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/saxswaxs/processor.py +0 -0
  37. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/saxswaxs/reader.py +0 -0
  38. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/saxswaxs/writer.py +0 -0
  39. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/server.py +0 -0
  40. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/sin2psi/__init__.py +0 -0
  41. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/sin2psi/processor.py +0 -0
  42. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/sin2psi/reader.py +0 -0
  43. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/sin2psi/writer.py +0 -0
  44. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/tomo/__init__.py +0 -0
  45. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/tomo/reader.py +0 -0
  46. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/tomo/writer.py +0 -0
  47. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/utils/__init__.py +0 -0
  48. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/utils/material.py +0 -0
  49. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/CHAP/writer.py +0 -0
  50. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/ChessAnalysisPipeline.egg-info/SOURCES.txt +0 -0
  51. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/ChessAnalysisPipeline.egg-info/dependency_links.txt +0 -0
  52. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/ChessAnalysisPipeline.egg-info/entry_points.txt +0 -0
  53. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/ChessAnalysisPipeline.egg-info/requires.txt +0 -0
  54. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/ChessAnalysisPipeline.egg-info/top_level.txt +0 -0
  55. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/LICENSE +0 -0
  56. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/MLaaS/__init__.py +0 -0
  57. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/MLaaS/ktrain.py +0 -0
  58. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/MLaaS/mnist_img.py +0 -0
  59. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/MLaaS/tfaas_client.py +0 -0
  60. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/README.md +0 -0
  61. {ChessAnalysisPipeline-0.0.11 → ChessAnalysisPipeline-0.0.13}/setup.cfg +0 -0
@@ -21,3 +21,5 @@ appear in specific `Processor` implementations.
21
21
  from CHAP.reader import Reader
22
22
  from CHAP.processor import Processor
23
23
  from CHAP.writer import Writer
24
+
25
+ version = 'v0.0.13'
@@ -9,14 +9,17 @@ validating input data in some `Processor`s.
9
9
 
10
10
  from CHAP.common.reader import (
11
11
  BinaryFileReader,
12
- SpecReader,
12
+ H5Reader,
13
13
  MapReader,
14
14
  NexusReader,
15
+ SpecReader,
15
16
  URLReader,
16
17
  YAMLReader,
17
18
  )
18
19
  from CHAP.common.processor import (
20
+ AnimationProcessor,
19
21
  AsyncProcessor,
22
+ ImageProcessor,
20
23
  IntegrationProcessor,
21
24
  IntegrateMapProcessor,
22
25
  MapProcessor,
@@ -30,9 +33,10 @@ from CHAP.common.processor import (
30
33
  )
31
34
  from CHAP.common.writer import (
32
35
  ExtractArchiveWriter,
36
+ FileTreeWriter,
37
+ MatplotlibAnimationWriter,
33
38
  MatplotlibFigureWriter,
34
39
  NexusWriter,
35
40
  YAMLWriter,
36
41
  TXTWriter,
37
- FileTreeWriter,
38
42
  )
@@ -1,17 +1,28 @@
1
1
  # System modules
2
- from functools import cache, lru_cache
2
+ from copy import deepcopy
3
+ from functools import (
4
+ cache,
5
+ lru_cache,
6
+ )
3
7
  import os
4
- from typing import Literal, Optional, Union
8
+ from typing import (
9
+ Literal,
10
+ Optional,
11
+ Union,
12
+ )
5
13
 
6
14
  # Third party modules
7
15
  import numpy as np
8
- from pydantic import (BaseModel,
9
- conint,
10
- conlist,
11
- constr,
12
- FilePath,
13
- PrivateAttr,
14
- validator)
16
+ from pydantic import (
17
+ BaseModel,
18
+ conint,
19
+ conlist,
20
+ constr,
21
+ FilePath,
22
+ PrivateAttr,
23
+ root_validator,
24
+ validator,
25
+ )
15
26
  from pyspec.file.spec import FileSpec
16
27
 
17
28
 
@@ -39,7 +50,7 @@ class SpecScans(BaseModel):
39
50
  scan_numbers: conlist(item_type=conint(gt=0), min_items=1)
40
51
 
41
52
  @validator('spec_file', allow_reuse=True)
42
- def validate_spec_file(cls, spec_file):
53
+ def validate_spec_file(cls, spec_file, values):
43
54
  """Validate the specified SPEC file.
44
55
 
45
56
  :param spec_file: Path to the SPEC file.
@@ -55,7 +66,7 @@ class SpecScans(BaseModel):
55
66
  raise ValueError(f'Invalid SPEC file {spec_file}')
56
67
  return spec_file
57
68
 
58
- @validator('scan_numbers', allow_reuse=True)
69
+ @validator('scan_numbers', pre=True, allow_reuse=True)
59
70
  def validate_scan_numbers(cls, scan_numbers, values):
60
71
  """Validate the specified list of scan numbers.
61
72
 
@@ -68,6 +79,12 @@ class SpecScans(BaseModel):
68
79
  :return: List of scan numbers.
69
80
  :rtype: list of int
70
81
  """
82
+ if isinstance(scan_numbers, str):
83
+ # Local modules
84
+ from CHAP.utils.general import string_to_list
85
+
86
+ scan_numbers = string_to_list(scan_numbers)
87
+
71
88
  spec_file = values.get('spec_file')
72
89
  if spec_file is not None:
73
90
  spec_scans = FileSpec(spec_file)
@@ -550,6 +567,28 @@ class SpecConfig(BaseModel):
550
567
  experiment_type: Literal['SAXSWAXS', 'EDD', 'XRF', 'TOMO']
551
568
  spec_scans: conlist(item_type=SpecScans, min_items=1)
552
569
 
570
+ @root_validator(pre=True)
571
+ def validate_config(cls, values):
572
+ """Ensure that a valid configuration was provided and finalize
573
+ spec_file filepaths.
574
+
575
+ :param values: Dictionary of class field values.
576
+ :type values: dict
577
+ :return: The validated list of `values`.
578
+ :rtype: dict
579
+ """
580
+ inputdir = values.get('inputdir')
581
+ if inputdir is not None:
582
+ spec_scans = values.get('spec_scans')
583
+ for i, scans in enumerate(deepcopy(spec_scans)):
584
+ spec_file = scans['spec_file']
585
+ if not os.path.isabs(spec_file):
586
+ spec_scans[i]['spec_file'] = os.path.join(
587
+ inputdir, spec_file)
588
+ spec_scans[i] = SpecScans(**spec_scans[i], **values)
589
+ values['spec_scans'] = spec_scans
590
+ return values
591
+
553
592
  @validator('experiment_type')
554
593
  def validate_experiment_type(cls, value, values):
555
594
  """Ensure values for the station and experiment_type fields are
@@ -606,19 +645,26 @@ class MapConfig(BaseModel):
606
645
  map. In the NeXus file representation of the map, datasets for
607
646
  these values will be included.
608
647
  :type scalar_values: Optional[list[PointByPointScanData]]
648
+ :ivar map_type: Type of map, structured or unstructured,
649
+ defaults to `'structured'`.
650
+ :type map_type: Optional[Literal['structured', 'unstructured']]
609
651
  """
610
652
  title: constr(strip_whitespace=True, min_length=1)
611
653
  station: Literal['id1a3','id3a','id3b']
612
654
  experiment_type: Literal['SAXSWAXS', 'EDD', 'XRF', 'TOMO']
613
655
  sample: Sample
614
656
  spec_scans: conlist(item_type=SpecScans, min_items=1)
615
- independent_dimensions: conlist(item_type=IndependentDimension,
616
- min_items=1)
657
+ independent_dimensions: conlist(
658
+ item_type=IndependentDimension, min_items=1)
617
659
  presample_intensity: Optional[PresampleIntensity]
618
660
  dwell_time_actual: Optional[DwellTimeActual]
619
661
  postsample_intensity: Optional[PostsampleIntensity]
620
662
  scalar_data: Optional[list[PointByPointScanData]] = []
663
+ map_type: Optional[Literal['structured', 'unstructured']] = 'structured'
621
664
  _coords: dict = PrivateAttr()
665
+ _dims: tuple = PrivateAttr()
666
+ _scan_step_indices: list = PrivateAttr()
667
+ _shape: tuple = PrivateAttr()
622
668
 
623
669
  _validate_independent_dimensions = validator(
624
670
  'independent_dimensions',
@@ -638,6 +684,75 @@ class MapConfig(BaseModel):
638
684
  each_item=True,
639
685
  allow_reuse=True)(validate_data_source_for_map_config)
640
686
 
687
+ @root_validator(pre=True)
688
+ def validate_config(cls, values):
689
+ """Ensure that a valid configuration was provided and finalize
690
+ spec_file filepaths.
691
+
692
+ :param values: Dictionary of class field values.
693
+ :type values: dict
694
+ :return: The validated list of `values`.
695
+ :rtype: dict
696
+ """
697
+ inputdir = values.get('inputdir')
698
+ if inputdir is not None:
699
+ spec_scans = values.get('spec_scans')
700
+ for i, scans in enumerate(deepcopy(spec_scans)):
701
+ spec_file = scans['spec_file']
702
+ if not os.path.isabs(spec_file):
703
+ spec_scans[i]['spec_file'] = os.path.join(
704
+ inputdir, spec_file)
705
+ spec_scans[i] = SpecScans(**spec_scans[i], **values)
706
+ values['spec_scans'] = spec_scans
707
+ return values
708
+
709
+ @validator('map_type', pre=True, always=True)
710
+ def validate_map_type(cls, map_type, values):
711
+ """Validate the map_type field.
712
+
713
+ :param map_type: Type of map, structured or unstructured,
714
+ defaults to `'structured'`.
715
+ :type map_type: Literal['structured', 'unstructured']]
716
+ :param values: Dictionary of values for all fields of the model.
717
+ :type values: dict
718
+ :return: The validated value for map_type.
719
+ :rtype: str
720
+ """
721
+ dims = {}
722
+ spec_scans = values.get('spec_scans')
723
+ independent_dimensions = values.get('independent_dimensions')
724
+ import_scanparser(values.get('station'), values.get('experiment_type'))
725
+ for i, dim in enumerate(deepcopy(independent_dimensions)):
726
+ dims[dim.label] = []
727
+ for scans in spec_scans:
728
+ for scan_number in scans.scan_numbers:
729
+ scanparser = scans.get_scanparser(scan_number)
730
+ for scan_step_index in range(
731
+ scanparser.spec_scan_npts):
732
+ dims[dim.label].append(dim.get_value(
733
+ scans, scan_number, scan_step_index))
734
+ dims[dim.label] = np.unique(dims[dim.label])
735
+ if dim.end is None:
736
+ dim.end = len(dims[dim.label])
737
+ dims[dim.label] = dims[dim.label][slice(
738
+ dim.start, dim.end, dim.step)]
739
+ independent_dimensions[i] = dim
740
+
741
+ coords = np.zeros([v.size for v in dims.values()], dtype=np.int64)
742
+ for scans in spec_scans:
743
+ for scan_number in scans.scan_numbers:
744
+ scanparser = scans.get_scanparser(scan_number)
745
+ for scan_step_index in range(scanparser.spec_scan_npts):
746
+ coords[tuple([
747
+ list(dims[dim.label]).index(
748
+ dim.get_value(scans, scan_number, scan_step_index))
749
+ for dim in independent_dimensions])] += 1
750
+ if any(True for v in coords.flatten() if v == 0 or v > 1):
751
+ return 'unstructured'
752
+ else:
753
+ return 'structured'
754
+
755
+
641
756
  @validator('experiment_type')
642
757
  def validate_experiment_type(cls, value, values):
643
758
  """Ensure values for the station and experiment_type fields are
@@ -659,17 +774,26 @@ class MapConfig(BaseModel):
659
774
  f'Supplied experiment type {value} is not allowed.')
660
775
  return value
661
776
 
777
+ @property
778
+ def all_scalar_data(self):
779
+ """Return a list of all instances of `PointByPointScanData`
780
+ for which this map configuration will collect dataset-like
781
+ data (as opposed to axes-like data).
782
+
783
+ This will be any and all of the items in the
784
+ corrections-data-related fields, as well as any additional
785
+ items in the optional `scalar_data` field.
786
+ """
787
+ return [getattr(self, label, None)
788
+ for label in CorrectionsData.reserved_labels()
789
+ if getattr(self, label, None) is not None] + self.scalar_data
790
+
662
791
  @property
663
792
  def coords(self):
664
793
  """Return a dictionary of the values of each independent
665
794
  dimension across the map.
666
-
667
- :returns: A dictionary ofthe map's coordinate values.
668
- :rtype: dict[str,list[float]]
669
795
  """
670
- try:
671
- coords = self._coords
672
- except:
796
+ if not hasattr(self, "_coords"):
673
797
  coords = {}
674
798
  for dim in self.independent_dimensions:
675
799
  coords[dim.label] = []
@@ -680,67 +804,106 @@ class MapConfig(BaseModel):
680
804
  scanparser.spec_scan_npts):
681
805
  coords[dim.label].append(dim.get_value(
682
806
  scans, scan_number, scan_step_index))
683
- coords[dim.label] = np.unique(coords[dim.label])
684
- if dim.end is None:
685
- dim.end = len(coords[dim.label])
686
- coords[dim.label] = coords[dim.label][slice(
687
- dim.start, dim.end, dim.step)]
807
+ if self.map_type == 'structured':
808
+ coords[dim.label] = np.unique(coords[dim.label])
688
809
  self._coords = coords
689
- return coords
810
+ return self._coords
690
811
 
691
812
  @property
692
813
  def dims(self):
693
814
  """Return a tuple of the independent dimension labels for the
694
815
  map.
695
816
  """
696
- return [point_by_point_scan_data.label
697
- for point_by_point_scan_data
698
- in self.independent_dimensions[::-1]]
817
+ if not hasattr(self, "_dims"):
818
+ self._dims = [
819
+ dim.label for dim in self.independent_dimensions[::-1]]
820
+ return self._dims
821
+
822
+ @property
823
+ def scan_step_indices(self):
824
+ """Return an ordered list in which we can look up the SpecScans
825
+ object, the scan number, and scan step index for every point
826
+ on the map.
827
+ """
828
+ if not hasattr(self, "_scan_step_indices"):
829
+ scan_step_indices = []
830
+ for scans in self.spec_scans:
831
+ for scan_number in scans.scan_numbers:
832
+ scanparser = scans.get_scanparser(scan_number)
833
+ for scan_step_index in range(scanparser.spec_scan_npts):
834
+ scan_step_indices.append(
835
+ (scans, scan_number, scan_step_index))
836
+ self._scan_step_indices = scan_step_indices
837
+ return self._scan_step_indices
699
838
 
700
839
  @property
701
840
  def shape(self):
702
841
  """Return the shape of the map -- a tuple representing the
703
842
  number of unique values of each dimension across the map.
704
843
  """
705
- return tuple([len(values) for key,values in self.coords.items()][::-1])
844
+ if not hasattr(self, "_shape"):
845
+ if self.map_type == 'structured':
846
+ self._shape = tuple(
847
+ [len(v) for k, v in self.coords.items()][::-1])
848
+ else:
849
+ self._shape = (len(self.scan_step_indices),)
850
+ return self._shape
851
+
852
+ def get_coords(self, map_index):
853
+ """Return a dictionary of the coordinate names and values of
854
+ each independent dimension for a given point on the map.
855
+
856
+ :param map_index: The map index to return coordinates for.
857
+ :type map_index: tuple
858
+ :return: A list of coordinate values.
859
+ :rtype: dict
860
+ """
861
+ if self.map_type == 'structured':
862
+ return {dim:self.coords[dim][i]
863
+ for dim, i in zip(self.dims, map_index)}
864
+ else:
865
+ return {dim:self.coords[dim][map_index[0]] for dim in self.dims}
706
866
 
707
- @property
708
- def all_scalar_data(self):
709
- """Return a list of all instances of `PointByPointScanData`
710
- for which this map configuration will collect dataset-like
711
- data (as opposed to axes-like data).
867
+ def get_detector_data(self, detector_name, map_index):
868
+ """Return detector data collected by this map for a given
869
+ point on the map.
712
870
 
713
- This will be any and all of the items in the
714
- corrections-data-related fields, as well as any additional
715
- items in the optional `scalar_data` field.
871
+ :param detector_name: Name of the detector for which to return
872
+ data. Usually the value of the detector's EPICS
873
+ areaDetector prefix macro, $P.
874
+ :type detector_name: str
875
+ :param map_index: The map index to return detector data for.
876
+ :type map_index: tuple
877
+ :return: One frame of raw detector data.
878
+ :rtype: np.ndarray
716
879
  """
717
- return [getattr(self, label, None)
718
- for label in CorrectionsData.reserved_labels()
719
- if getattr(self, label, None) is not None] + self.scalar_data
880
+ scans, scan_number, scan_step_index = \
881
+ self.get_scan_step_index(map_index)
882
+ scanparser = scans.get_scanparser(scan_number)
883
+ return scanparser.get_detector_data(detector_name, scan_step_index)
720
884
 
721
885
  def get_scan_step_index(self, map_index):
722
886
  """Return parameters to identify a single SPEC scan step that
723
887
  corresponds to the map point at the index provided.
724
888
 
725
889
  :param map_index: The index of a map point to identify as a
726
- specific SPEC scan step index
890
+ specific SPEC scan step index.
727
891
  :type map_index: tuple
728
892
  :return: A `SpecScans` configuration, scan number, and scan
729
- step index
893
+ step index.
730
894
  :rtype: tuple[SpecScans, int, int]
731
895
  """
732
- coords = {dim: self.coords[dim][i]
733
- for dim,i in zip( self.dims, map_index)}
734
- for scans in self.spec_scans:
735
- for scan_number in scans.scan_numbers:
736
- scanparser = scans.get_scanparser(scan_number)
737
- for scan_step_index in range(scanparser.spec_scan_npts):
738
- _coords = {dim.label:dim.get_value(
739
- scans, scan_number, scan_step_index)
740
- for dim in self.independent_dimensions}
741
- if _coords == coords:
742
- return scans, scan_number, scan_step_index
743
- raise RuntimeError(f'Unable to match coordinates {coords}')
896
+ if self.map_type == 'structured':
897
+ map_coords = self.get_coords(map_index)
898
+ for scans, scan_number, scan_step_index in self.scan_step_indices:
899
+ coords = {dim.label:dim.get_value(
900
+ scans, scan_number, scan_step_index)
901
+ for dim in self.independent_dimensions}
902
+ if coords == map_coords:
903
+ return scans, scan_number, scan_step_index
904
+ raise RuntimeError(f'Unable to match coordinates {coords}')
905
+ else:
906
+ return self.scan_step_indices[map_index[0]]
744
907
 
745
908
  def get_value(self, data, map_index):
746
909
  """Return the raw data collected by a single device at a
@@ -757,22 +920,6 @@ class MapConfig(BaseModel):
757
920
  self.get_scan_step_index(map_index)
758
921
  return data.get_value(scans, scan_number, scan_step_index)
759
922
 
760
- def get_detector_data(self, detector_name, map_index):
761
- """Return detector data collected by this map.
762
-
763
- :param detector_name: Name of the detector for which to return
764
- data. Usually the value of the detector's EPICS
765
- areaDetector prefix macro, $P.
766
- :type detector_name: str
767
- :param map_index: The map index to return detector data for.
768
- :return: One frame of raw detector data
769
- :rtype: np.ndarray
770
- """
771
- scans, scan_number, scan_step_index = \
772
- self.get_scan_step_index(map_index)
773
- scanparser = scans.get_scanparser(scan_number)
774
- return scanparser.get_detector_data(detector_name, scan_step_index)
775
-
776
923
 
777
924
  def import_scanparser(station, experiment):
778
925
  """Given the name of a CHESS station and experiment type, import