ChessAnalysisPipeline 0.0.15__py3-none-any.whl → 0.0.16__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/common/models/map.py CHANGED
@@ -15,16 +15,17 @@ from typing import (
15
15
  import numpy as np
16
16
  from pydantic import (
17
17
  BaseModel,
18
+ Field,
19
+ FilePath,
20
+ PrivateAttr,
18
21
  conint,
19
22
  conlist,
20
23
  constr,
21
- FilePath,
22
- PrivateAttr,
23
- root_validator,
24
- validator,
24
+ field_validator,
25
+ model_validator,
25
26
  )
26
27
  from pyspec.file.spec import FileSpec
27
-
28
+ from typing_extensions import Annotated
28
29
 
29
30
  class Sample(BaseModel):
30
31
  """Class representing a sample metadata configuration.
@@ -35,7 +36,7 @@ class Sample(BaseModel):
35
36
  :type description: str, optional
36
37
  """
37
38
  name: constr(min_length=1)
38
- description: Optional[str]
39
+ description: Optional[str] = ''
39
40
 
40
41
 
41
42
  class SpecScans(BaseModel):
@@ -44,24 +45,23 @@ class SpecScans(BaseModel):
44
45
  :ivar spec_file: Path to the SPEC file.
45
46
  :type spec_file: str
46
47
  :ivar scan_numbers: List of scan numbers to use.
47
- :type scan_numbers: list[int]
48
+ :type scan_numbers: Union(int, list[int], str)
48
49
  :ivar par_file: Path to a non-default SMB par file.
49
50
  :type par_file: str, optional
50
51
  """
51
52
  spec_file: FilePath
52
- scan_numbers: conlist(item_type=conint(gt=0), min_items=1)
53
- par_file: Optional[FilePath]
53
+ scan_numbers: conlist(item_type=conint(gt=0), min_length=1)
54
+ par_file: Optional[FilePath] = None
54
55
 
55
- @validator('spec_file', allow_reuse=True)
56
- def validate_spec_file(cls, spec_file, values):
56
+ @field_validator('spec_file')
57
+ @classmethod
58
+ def validate_spec_file(cls, spec_file):
57
59
  """Validate the specified SPEC file.
58
60
 
59
61
  :param spec_file: Path to the SPEC file.
60
62
  :type spec_file: str
61
- :param values: Dictionary of validated class field values.
62
- :type values: dict
63
63
  :raises ValueError: If the SPEC file is invalid.
64
- :return: Absolute path to the SPEC file, if it is valid.
64
+ :return: Absolute path to the SPEC file.
65
65
  :rtype: str
66
66
  """
67
67
  try:
@@ -71,26 +71,29 @@ class SpecScans(BaseModel):
71
71
  raise ValueError(f'Invalid SPEC file {spec_file}')
72
72
  return spec_file
73
73
 
74
- @validator('scan_numbers', pre=True, allow_reuse=True)
75
- def validate_scan_numbers(cls, scan_numbers, values):
74
+ @field_validator('scan_numbers', mode='before')
75
+ @classmethod
76
+ def validate_scan_numbers(cls, scan_numbers, info):
76
77
  """Validate the specified list of scan numbers.
77
78
 
78
79
  :param scan_numbers: List of scan numbers.
79
- :type scan_numbers: list of int
80
- :param values: Dictionary of validated class field values.
81
- :type values: dict
80
+ :type scan_numbers: Union(int, list[int], str)
81
+ :param info: Pydantic validator info object.
82
+ :type info: pydantic_core._pydantic_core.ValidationInfo
82
83
  :raises ValueError: If a specified scan number is not found in
83
84
  the SPEC file.
84
85
  :return: List of scan numbers.
85
- :rtype: list of int
86
+ :rtype: list[int]
86
87
  """
87
- if isinstance(scan_numbers, str):
88
+ if isinstance(scan_numbers, int):
89
+ scan_numbers = [scan_numbers]
90
+ elif isinstance(scan_numbers, str):
88
91
  # Local modules
89
92
  from CHAP.utils.general import string_to_list
90
93
 
91
94
  scan_numbers = string_to_list(scan_numbers)
92
95
 
93
- spec_file = values.get('spec_file')
96
+ spec_file = info.data.get('spec_file')
94
97
  if spec_file is not None:
95
98
  spec_scans = FileSpec(spec_file)
96
99
  for scan_number in scan_numbers:
@@ -100,16 +103,15 @@ class SpecScans(BaseModel):
100
103
  f'No scan number {scan_number} in {spec_file}')
101
104
  return scan_numbers
102
105
 
103
- @validator('par_file', allow_reuse=True)
104
- def validate_par_file(cls, par_file, values):
106
+ @field_validator('par_file')
107
+ @classmethod
108
+ def validate_par_file(cls, par_file):
105
109
  """Validate the specified SMB par file.
106
110
 
107
111
  :param par_file: Path to a non-default SMB par file.
108
112
  :type par_file: str
109
- :param values: Dictionary of validated class field values.
110
- :type values: dict
111
113
  :raises ValueError: If the SMB par file is invalid.
112
- :return: Absolute path to the SMB par file, if it is valid.
114
+ :return: Absolute path to the SMB par file.
113
115
  :rtype: str
114
116
  """
115
117
  if par_file is None or not par_file:
@@ -244,7 +246,8 @@ class PointByPointScanData(BaseModel):
244
246
  'smb_par', 'expression']
245
247
  name: constr(strip_whitespace=True, min_length=1)
246
248
 
247
- @validator('label')
249
+ @field_validator('label')
250
+ @classmethod
248
251
  def validate_label(cls, label):
249
252
  """Validate that the supplied `label` does not conflict with
250
253
  any of the values for `label` reserved for certain data needed
@@ -253,8 +256,7 @@ class PointByPointScanData(BaseModel):
253
256
  :param label: The value of `label` to validate.
254
257
  :type label: str
255
258
  :raises ValueError: If `label` is one of the reserved values.
256
- :return: The original supplied value `label`, if it is
257
- allowed.
259
+ :return: The originally supplied value `label`.
258
260
  :rtype: str
259
261
  """
260
262
  if ((not issubclass(cls,CorrectionsData))
@@ -329,6 +331,7 @@ class PointByPointScanData(BaseModel):
329
331
  needed for evaluating the expression.
330
332
  :return: None
331
333
  """
334
+ # Third party modules
332
335
  from ast import parse
333
336
  from asteval import get_ast_names
334
337
 
@@ -401,7 +404,7 @@ class PointByPointScanData(BaseModel):
401
404
  return get_smb_par_value(spec_scans.spec_file,
402
405
  scan_number,
403
406
  self.name)
404
- elif self.data_type == 'expression':
407
+ if self.data_type == 'expression':
405
408
  return get_expression_value(spec_scans,
406
409
  scan_number,
407
410
  scan_step_index,
@@ -526,8 +529,10 @@ def get_expression_value(spec_scans:SpecScans, scan_number:int,
526
529
  :return: The value of the .par file value for the scan requested.
527
530
  :rtype: float
528
531
  """
532
+ # Third party modules
529
533
  from ast import parse
530
534
  from asteval import get_ast_names, Interpreter
535
+
531
536
  labels = get_ast_names(parse(expression))
532
537
  symtable = {}
533
538
  for l in labels:
@@ -540,35 +545,35 @@ def get_expression_value(spec_scans:SpecScans, scan_number:int,
540
545
  aeval = Interpreter(symtable=symtable)
541
546
  return aeval(expression)
542
547
 
543
- def validate_data_source_for_map_config(data_source, values):
548
+ def validate_data_source_for_map_config(data_source, info):
544
549
  """Confirm that an instance of PointByPointScanData is valid for
545
550
  the station and scans provided by a map configuration dictionary.
546
551
 
547
552
  :param data_source: The input object to validate.
548
- :type data_source: PintByPointScanData
549
- :param values: The map configuration dictionary.
550
- :type values: dict
551
- :raises Exception: If `data_source` cannot be validated for
552
- `values`.
553
- :return: `data_source`, if it is valid.
553
+ :type data_source: PointByPointScanData
554
+ :param info: Pydantic validator info object.
555
+ :type info: pydantic_core._pydantic_core.ValidationInfo
556
+ :raises Exception: If `data_source` cannot be validated.
557
+ :return: the validated `data_source` instance.
554
558
  :rtype: PointByPointScanData
555
559
  """
556
560
  def _validate_data_source_for_map_config(
557
- data_source, values, parent_list=None):
561
+ data_source, info, parent_list=None):
558
562
  if isinstance(data_source, list):
559
563
  return [_validate_data_source_for_map_config(
560
- d_s, values, parent_list=data_source) for d_s in data_source]
564
+ d_s, info, parent_list=data_source) for d_s in data_source]
561
565
  if data_source is not None:
566
+ values = info.data
562
567
  if data_source.data_type == 'expression':
563
- data_source.validate_for_scalar_data(
564
- values.get('scalar_data', parent_list))
568
+ data_source.validate_for_scalar_data(values['scalar_data'])
565
569
  else:
566
570
  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)
571
+ values['station'], values['experiment_type'])
572
+ data_source.validate_for_station(values['station'])
573
+ data_source.validate_for_spec_scans(values['spec_scans'])
574
+ return data_source
575
+
576
+ return _validate_data_source_for_map_config(data_source, info)
572
577
 
573
578
 
574
579
  class IndependentDimension(PointByPointScanData):
@@ -601,11 +606,20 @@ class IndependentDimension(PointByPointScanData):
601
606
  end: Optional[int] = None
602
607
  step: Optional[conint(gt=0)] = 1
603
608
 
604
- # @validator('step')
605
- # def validate_step(cls, value):
606
- # if value == 0 :
609
+ # @field_validator('step')
610
+ # @classmethod
611
+ # def validate_step(cls, step):
612
+ # """Validate that the supplied value of `step`.
613
+ #
614
+ # :param step: The value of `step` to validate.
615
+ # :type step: str
616
+ # :raises ValueError: If `step` is zero.
617
+ # :return: The originally supplied value `step`.
618
+ # :rtype: int
619
+ # """
620
+ # if step == 0 :
607
621
  # raise ValueError('slice step cannot be zero')
608
- # return value
622
+ # return step
609
623
 
610
624
 
611
625
  class CorrectionsData(PointByPointScanData):
@@ -641,7 +655,7 @@ class CorrectionsData(PointByPointScanData):
641
655
  :return: A list of reserved labels.
642
656
  :rtype: list[str]
643
657
  """
644
- return list((*cls.__fields__['label'].type_.__args__, 'round'))
658
+ return list((*cls.model_fields['label'].annotation.__args__, 'round'))
645
659
 
646
660
 
647
661
  class PresampleIntensity(CorrectionsData):
@@ -713,60 +727,68 @@ class SpecConfig(BaseModel):
713
727
  :type spec_scans: list[SpecScans]
714
728
  """
715
729
  station: Literal['id1a3', 'id3a', 'id3b']
716
- experiment_type: Literal['SAXSWAXS', 'EDD', 'XRF', 'TOMO']
717
- spec_scans: conlist(item_type=SpecScans, min_items=1)
730
+ experiment_type: Literal['EDD', 'GIWAXS', 'SAXSWAXS', 'TOMO', 'XRF']
731
+ spec_scans: conlist(item_type=SpecScans, min_length=1)
718
732
 
719
- @root_validator(pre=True)
720
- def validate_config(cls, values):
733
+ @model_validator(mode='before')
734
+ @classmethod
735
+ def validate_config(cls, data):
721
736
  """Ensure that a valid configuration was provided and finalize
722
737
  spec_file filepaths.
723
738
 
724
- :param values: Dictionary of validated class field values.
725
- :type values: dict
726
- :return: The validated list of `values`.
739
+ :param data: Pydantic validator data object.
740
+ :type data: SpecConfig, pydantic_core._pydantic_core.ValidationInfo
741
+ :return: The currently validated list of class properties.
727
742
  :rtype: dict
728
743
  """
729
- inputdir = values.get('inputdir')
744
+ inputdir = data.get('inputdir')
730
745
  if inputdir is not None:
731
- spec_scans = values.get('spec_scans')
746
+ spec_scans = data.get('spec_scans')
732
747
  for i, scans in enumerate(deepcopy(spec_scans)):
733
- spec_file = scans['spec_file']
734
- if not os.path.isabs(spec_file):
735
- spec_scans[i]['spec_file'] = os.path.join(
736
- inputdir, spec_file)
737
- spec_scans[i] = SpecScans(**spec_scans[i], **values)
738
- values['spec_scans'] = spec_scans
739
- return values
740
-
741
- @validator('experiment_type')
742
- def validate_experiment_type(cls, value, values):
748
+ if isinstance(scans, dict):
749
+ spec_file = scans['spec_file']
750
+ if not os.path.isabs(spec_file):
751
+ spec_scans[i]['spec_file'] = os.path.join(
752
+ inputdir, spec_file)
753
+ else:
754
+ spec_file = scans.spec_file
755
+ if not os.path.isabs(spec_file):
756
+ spec_scans[i].spec_file = os.path.join(
757
+ inputdir, spec_file)
758
+ data['spec_scans'] = spec_scans
759
+ return data
760
+
761
+ @field_validator('experiment_type')
762
+ @classmethod
763
+ def validate_experiment_type(cls, experiment_type, info):
743
764
  """Ensure values for the station and experiment_type fields are
744
765
  compatible
745
766
 
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
767
+ :param experiment_type: The value of `experiment_type` to
768
+ validate.
769
+ :type experiment_type: str
770
+ :param info: Pydantic validator info object.
771
+ :type info: pydantic_core._pydantic_core.ValidationInfo
750
772
  :raises ValueError: Invalid experiment type.
751
773
  :return: The validated field for `experiment_type`.
752
774
  :rtype: str
753
775
  """
754
- station = values.get('station')
776
+ station = info.data.get('station')
755
777
  if station == 'id1a3':
756
- allowed_experiment_types = ['SAXSWAXS', 'EDD', 'TOMO']
778
+ allowed_experiment_types = ['EDD', 'SAXSWAXS', 'TOMO']
757
779
  elif station == 'id3a':
758
780
  allowed_experiment_types = ['EDD', 'TOMO']
759
781
  elif station == 'id3b':
760
- allowed_experiment_types = ['SAXSWAXS', 'XRF', 'TOMO']
782
+ allowed_experiment_types = ['GIWAXS', 'SAXSWAXS', 'TOMO', 'XRF']
761
783
  else:
762
784
  allowed_experiment_types = []
763
- if value not in allowed_experiment_types:
785
+ if experiment_type not in allowed_experiment_types:
764
786
  raise ValueError(
765
787
  f'For station {station}, allowed experiment types are '
766
788
  f'{", ".join(allowed_experiment_types)}. '
767
- f'Supplied experiment type {value} is not allowed.')
768
- import_scanparser(station, value)
769
- return value
789
+ f'Supplied experiment type {experiment_type} is not allowed.')
790
+ import_scanparser(station, experiment_type)
791
+ return experiment_type
770
792
 
771
793
 
772
794
  class MapConfig(BaseModel):
@@ -802,189 +824,124 @@ class MapConfig(BaseModel):
802
824
  map. In the NeXus file representation of the map, datasets for
803
825
  these values will be included, defaults to `[]`.
804
826
  :type scalar_data: list[PointByPointScanData], optional
805
- :ivar map_type: Type of map, structured or unstructured,
806
- defaults to `'structured'`.
807
- :type map_type: Literal['structured', 'unstructured'], optional
808
827
  """
809
828
  title: constr(strip_whitespace=True, min_length=1)
810
829
  station: Literal['id1a3', 'id3a', 'id3b']
811
- experiment_type: Literal['SAXSWAXS', 'EDD', 'XRF', 'TOMO']
830
+ experiment_type: Literal['EDD', 'GIWAXS', 'SAXSWAXS', 'TOMO', 'XRF']
812
831
  sample: Sample
813
- spec_scans: conlist(item_type=SpecScans, min_items=1)
814
- independent_dimensions: conlist(
815
- item_type=IndependentDimension, min_items=1)
816
- presample_intensity: Optional[PresampleIntensity]
817
- dwell_time_actual: Optional[DwellTimeActual]
818
- postsample_intensity: Optional[PostsampleIntensity]
832
+ spec_scans: conlist(item_type=SpecScans, min_length=1)
819
833
  scalar_data: Optional[list[PointByPointScanData]] = []
820
- attrs: Optional[dict] = {}
821
- map_type: Optional[Literal['structured', 'unstructured']] = 'structured'
822
- _coords: dict = PrivateAttr()
834
+ independent_dimensions: conlist(
835
+ item_type=IndependentDimension, min_length=1)
836
+ presample_intensity: Optional[PresampleIntensity] = None
837
+ dwell_time_actual: Optional[DwellTimeActual] = None
838
+ postsample_intensity: Optional[PostsampleIntensity] = None
839
+ attrs: Optional[Annotated[dict, Field(validate_default=True)]] = {}
840
+ # _coords: dict = PrivateAttr()
823
841
  _dims: tuple = PrivateAttr()
824
- _scan_step_indices: list = PrivateAttr()
825
- _shape: tuple = PrivateAttr()
826
-
827
- _validate_independent_dimensions = validator(
828
- 'independent_dimensions',
829
- each_item=True,
830
- allow_reuse=True)(validate_data_source_for_map_config)
831
- _validate_presample_intensity = validator(
832
- 'presample_intensity',
833
- allow_reuse=True)(validate_data_source_for_map_config)
834
- _validate_dwell_time_actual = validator(
835
- 'dwell_time_actual',
836
- allow_reuse=True)(validate_data_source_for_map_config)
837
- _validate_postsample_intensity = validator(
838
- 'postsample_intensity',
839
- allow_reuse=True)(validate_data_source_for_map_config)
840
- _validate_scalar_data = validator(
841
- 'scalar_data',
842
- allow_reuse=True)(validate_data_source_for_map_config)
843
-
844
- @root_validator(pre=True)
845
- def validate_config(cls, values):
842
+ # _scan_step_indices: list = PrivateAttr()
843
+ # _shape: tuple = PrivateAttr()
844
+
845
+ _validate_independent_dimensions = field_validator(
846
+ 'independent_dimensions')(validate_data_source_for_map_config)
847
+ _validate_presample_intensity = field_validator(
848
+ 'presample_intensity')(validate_data_source_for_map_config)
849
+ _validate_dwell_time_actual = field_validator(
850
+ 'dwell_time_actual')(validate_data_source_for_map_config)
851
+ _validate_postsample_intensity = field_validator(
852
+ 'postsample_intensity')(validate_data_source_for_map_config)
853
+ _validate_scalar_data = field_validator(
854
+ 'scalar_data')(validate_data_source_for_map_config)
855
+
856
+ @model_validator(mode='before')
857
+ @classmethod
858
+ def validate_config(cls, data):
846
859
  """Ensure that a valid configuration was provided and finalize
847
860
  spec_file filepaths.
848
861
 
849
- :param values: Dictionary of validated class field values.
850
- :type values: dict
851
- :return: The validated list of `values`.
862
+ :param data: Pydantic validator data object.
863
+ :type data:
864
+ MapConfig, pydantic_core._pydantic_core.ValidationInfo
865
+ :return: The currently validated list of class properties.
852
866
  :rtype: dict
853
867
  """
854
- inputdir = values.get('inputdir')
868
+ inputdir = data.get('inputdir')
855
869
  if inputdir is not None:
856
- spec_scans = values.get('spec_scans')
870
+ spec_scans = data.get('spec_scans')
857
871
  for i, scans in enumerate(deepcopy(spec_scans)):
858
872
  spec_file = scans['spec_file']
859
873
  if not os.path.isabs(spec_file):
860
874
  spec_scans[i]['spec_file'] = os.path.join(
861
875
  inputdir, spec_file)
862
- spec_scans[i] = SpecScans(**spec_scans[i], **values)
863
- values['spec_scans'] = spec_scans
864
- return values
876
+ spec_scans[i] = SpecScans(**spec_scans[i], **data)
877
+ data['spec_scans'] = spec_scans
878
+ return data
865
879
 
866
- @validator('experiment_type')
867
- def validate_experiment_type(cls, value, values):
880
+ @field_validator('experiment_type')
881
+ @classmethod
882
+ def validate_experiment_type(cls, experiment_type, info):
868
883
  """Ensure values for the station and experiment_type fields are
869
884
  compatible.
870
885
 
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
886
+ :param experiment_type: The value of `experiment_type` to
887
+ validate.
888
+ :type experiment_type: dict
889
+ :param info: Pydantic validator info object.
890
+ :type info: pydantic_core._pydantic_core.ValidationInfo
875
891
  :raises ValueError: Invalid experiment type.
876
892
  :return: The validated field for `experiment_type`.
877
893
  :rtype: str
878
894
  """
879
- station = values['station']
895
+ station = info.data['station']
880
896
  if station == 'id1a3':
881
- allowed_experiment_types = ['SAXSWAXS', 'EDD', 'TOMO']
897
+ allowed_experiment_types = ['EDD', 'SAXSWAXS', 'TOMO']
882
898
  elif station == 'id3a':
883
899
  allowed_experiment_types = ['EDD', 'TOMO']
884
900
  elif station == 'id3b':
885
- allowed_experiment_types = ['SAXSWAXS', 'XRF', 'TOMO']
901
+ allowed_experiment_types = ['GIWAXS', 'SAXSWAXS', 'TOMO', 'XRF']
886
902
  else:
887
903
  allowed_experiment_types = []
888
- if value not in allowed_experiment_types:
904
+ if experiment_type not in allowed_experiment_types:
889
905
  raise ValueError(
890
906
  f'For station {station}, allowed experiment types are '
891
907
  f'{", ".join(allowed_experiment_types)}. '
892
- f'Supplied experiment type {value} is not allowed.')
893
- return value
908
+ f'Supplied experiment type {experiment_type} is not allowed.')
909
+ return experiment_type
894
910
 
895
- @validator('attrs', always=True)
896
- def validate_attrs(cls, value, values):
911
+ #RV maybe better to use model_validator, see v2 docs?
912
+ @field_validator('attrs')
913
+ @classmethod
914
+ def validate_attrs(cls, attrs, info):
897
915
  """Read any additional attributes depending on the values for
898
916
  the station and experiment_type fields.
899
917
 
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
918
+ :param attrs: Any additional attributes to the MapConfig class.
919
+ :type attrs: dict
920
+ :param info: Pydantic validator info object.
921
+ :type info: pydantic_core._pydantic_core.ValidationInfo
904
922
  :raises ValueError: Invalid attribute.
905
923
  :return: The validated field for `attrs`.
906
924
  :rtype: dict
907
925
  """
908
926
  # Get the map's scan_type for EDD experiments
927
+ values = info.data
909
928
  station = values['station']
910
929
  experiment_type = values['experiment_type']
911
930
  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')
931
+ attrs['scan_type'] = cls.get_smb_par_attr(values, 'scan_type')
932
+ attrs['config_id'] = cls.get_smb_par_attr(values, 'config_id')
933
+ attrs['dataset_id'] = cls.get_smb_par_attr(values, 'dataset_id')
915
934
  axes_labels = {1: 'fly_labx', 2: 'fly_laby', 3: 'fly_labz',
916
935
  4: 'fly_ometotal'}
917
- if value['scan_type'] is None:
918
- return value
919
- if value['scan_type'] != 0:
920
- value['fly_axis_labels'] = [
936
+ if attrs['scan_type'] is None:
937
+ return attrs
938
+ if attrs['scan_type'] != 0:
939
+ attrs['fly_axis_labels'] = [
921
940
  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(
941
+ if attrs['scan_type'] in (2, 3, 5):
942
+ attrs['fly_axis_labels'].append(
924
943
  axes_labels[cls.get_smb_par_attr(values, 'fly_axis1')])
925
- return value
926
-
927
- @validator('map_type', pre=True, always=True)
928
- def validate_map_type(cls, map_type, values):
929
- """Validate the map_type field.
930
-
931
- :param map_type: Type of map, structured or unstructured,
932
- defaults to `'structured'`.
933
- :type map_type: Literal['structured', 'unstructured']]
934
- :param values: Dictionary of validated class field values.
935
- :type values: dict
936
- :return: The validated value for map_type.
937
- :rtype: str
938
- """
939
- dims = {}
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'])
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
954
- dims[dim.label] = []
955
- for scans in spec_scans:
956
- for scan_number in scans.scan_numbers:
957
- scanparser = scans.get_scanparser(scan_number)
958
- for scan_step_index in range(
959
- scanparser.spec_scan_npts):
960
- dims[dim.label].append(dim.get_value(
961
- scans, scan_number, scan_step_index,
962
- scalar_data, relative, ndigits))
963
- dims[dim.label] = np.unique(dims[dim.label])
964
- if dim.end is None:
965
- dim.end = len(dims[dim.label])
966
- dims[dim.label] = dims[dim.label][slice(
967
- dim.start, dim.end, dim.step)]
968
- independent_dimensions[i] = dim
969
-
970
- coords = np.zeros([v.size for v in dims.values()], dtype=np.int64)
971
- for scans in spec_scans:
972
- for scan_number in scans.scan_numbers:
973
- scanparser = scans.get_scanparser(scan_number)
974
- for scan_step_index in range(scanparser.spec_scan_npts):
975
- coords[tuple([
976
- list(dims[dim.label]).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))
983
- for dim in independent_dimensions])] += 1
984
- if any(True for v in coords.flatten() if v == 0 or v > 1):
985
- return 'unstructured'
986
- else:
987
- return 'structured'
944
+ return attrs
988
945
 
989
946
  @staticmethod
990
947
  def get_smb_par_attr(class_fields, label, units='-', name=None):
@@ -1002,8 +959,8 @@ class MapConfig(BaseModel):
1002
959
  except:
1003
960
  print(
1004
961
  f'Warning: No value found for .par file value "{name}"'
1005
- + f' on scan {scan_number} in spec file '
1006
- + f'{scans.spec_file}.')
962
+ f' on scan {scan_number} in spec file '
963
+ f'{scans.spec_file}.')
1007
964
  values.append(None)
1008
965
  values = list(set(values))
1009
966
  if len(values) != 1:
@@ -1030,6 +987,7 @@ class MapConfig(BaseModel):
1030
987
  """Return a dictionary of the values of each independent
1031
988
  dimension across the map.
1032
989
  """
990
+ raise RuntimeError(f'property coords not implemented')
1033
991
  if not hasattr(self, '_coords'):
1034
992
  scan_type = self.attrs.get('scan_type', -1)
1035
993
  fly_axis_labels = self.attrs.get('fly_axis_labels', [])
@@ -1061,8 +1019,7 @@ class MapConfig(BaseModel):
1061
1019
  map.
1062
1020
  """
1063
1021
  if not hasattr(self, '_dims'):
1064
- self._dims = [
1065
- dim.label for dim in self.independent_dimensions[::-1]]
1022
+ self._dims = [dim.label for dim in self.independent_dimensions]
1066
1023
  return self._dims
1067
1024
 
1068
1025
  @property
@@ -1071,6 +1028,7 @@ class MapConfig(BaseModel):
1071
1028
  object, the scan number, and scan step index for every point
1072
1029
  on the map.
1073
1030
  """
1031
+ raise RuntimeError(f'property scan_step_indices not implemented')
1074
1032
  if not hasattr(self, '_scan_step_indices'):
1075
1033
  scan_step_indices = []
1076
1034
  for scans in self.spec_scans:
@@ -1087,10 +1045,10 @@ class MapConfig(BaseModel):
1087
1045
  """Return the shape of the map -- a tuple representing the
1088
1046
  number of unique values of each dimension across the map.
1089
1047
  """
1048
+ raise RuntimeError(f'property shape not implemented')
1090
1049
  if not hasattr(self, '_shape'):
1091
1050
  if self.map_type == 'structured':
1092
- self._shape = tuple(
1093
- [len(v) for k, v in self.coords.items()][::-1])
1051
+ self._shape = tuple([len(v) for k, v in self.coords.items()])
1094
1052
  else:
1095
1053
  self._shape = (len(self.scan_step_indices),)
1096
1054
  return self._shape
@@ -1104,6 +1062,7 @@ class MapConfig(BaseModel):
1104
1062
  :return: A list of coordinate values.
1105
1063
  :rtype: dict
1106
1064
  """
1065
+ raise RuntimeError(f'get_coords not implemented')
1107
1066
  if self.map_type == 'structured':
1108
1067
  scan_type = self.attrs.get('scan_type', -1)
1109
1068
  fly_axis_labels = self.attrs.get('fly_axis_labels', [])
@@ -1131,6 +1090,7 @@ class MapConfig(BaseModel):
1131
1090
  :return: One frame of raw detector data.
1132
1091
  :rtype: np.ndarray
1133
1092
  """
1093
+ raise RuntimeError(f'get_detector_data not implemented')
1134
1094
  scans, scan_number, scan_step_index = \
1135
1095
  self.get_scan_step_index(map_index)
1136
1096
  scanparser = scans.get_scanparser(scan_number)
@@ -1147,6 +1107,7 @@ class MapConfig(BaseModel):
1147
1107
  step index.
1148
1108
  :rtype: tuple[SpecScans, int, int]
1149
1109
  """
1110
+ raise RuntimeError(f'get_scan_step_index not implemented')
1150
1111
  scan_type = self.attrs.get('scan_type', -1)
1151
1112
  fly_axis_labels = self.attrs.get('fly_axis_labels', [])
1152
1113
  if self.map_type == 'structured':
@@ -1179,6 +1140,7 @@ class MapConfig(BaseModel):
1179
1140
  :type map_index: tuple
1180
1141
  :return: Raw data value.
1181
1142
  """
1143
+ raise RuntimeError(f'get_value not implemented')
1182
1144
  scans, scan_number, scan_step_index = \
1183
1145
  self.get_scan_step_index(map_index)
1184
1146
  return data.get_value(scans, scan_number, scan_step_index,
@@ -1194,40 +1156,9 @@ def import_scanparser(station, experiment):
1194
1156
  :type station: str
1195
1157
  :param experiment: The experiment type.
1196
1158
  :type experiment: Literal[
1197
- 'SAXSWAXS', 'EDD', 'XRF', 'Tomo', 'Powder']
1159
+ 'EDD', 'GIWAXS', 'SAXSWAXS', 'TOMO', 'XRF']
1198
1160
  """
1199
-
1200
- station = station.lower()
1201
- experiment = experiment.lower()
1202
-
1203
1161
  # Local modules
1204
- if station in ('id1a3', 'id3a'):
1205
- if experiment in ('saxswaxs', 'powder'):
1206
- from CHAP.utils.scanparsers \
1207
- import SMBLinearScanParser as ScanParser
1208
- elif experiment == 'edd':
1209
- from CHAP.utils.scanparsers \
1210
- import SMBMCAScanParser as ScanParser
1211
- elif experiment == 'tomo':
1212
- from CHAP.utils.scanparsers \
1213
- import SMBRotationScanParser as ScanParser
1214
- else:
1215
- raise ValueError(
1216
- f'Invalid experiment type for station {station}: {experiment}')
1217
- elif station == 'id3b':
1218
- if experiment == 'saxswaxs':
1219
- from CHAP.utils.scanparsers \
1220
- import FMBSAXSWAXSScanParser as ScanParser
1221
- elif experiment == 'tomo':
1222
- from CHAP.utils.scanparsers \
1223
- import FMBRotationScanParser as ScanParser
1224
- elif experiment == 'xrf':
1225
- from CHAP.utils.scanparsers \
1226
- import FMBXRFScanParser as ScanParser
1227
- else:
1228
- raise ValueError(
1229
- f'Invalid experiment type for station {station}: {experiment}')
1230
- else:
1231
- raise ValueError(f'Invalid station: {station}')
1162
+ from chess_scanparsers import choose_scanparser
1232
1163
 
1233
- globals()['ScanParser'] = ScanParser
1164
+ globals()['ScanParser'] = choose_scanparser(station, experiment)