ChessAnalysisPipeline 0.0.4__py3-none-any.whl → 0.0.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ChessAnalysisPipeline might be problematic. Click here for more details.

Files changed (43) hide show
  1. CHAP/TaskManager.py +214 -0
  2. CHAP/common/models/__init__.py +0 -2
  3. CHAP/common/models/integration.py +392 -249
  4. CHAP/common/models/map.py +350 -198
  5. CHAP/common/processor.py +229 -191
  6. CHAP/common/reader.py +52 -39
  7. CHAP/common/utils/__init__.py +0 -37
  8. CHAP/common/utils/fit.py +1197 -991
  9. CHAP/common/utils/general.py +629 -372
  10. CHAP/common/utils/material.py +158 -121
  11. CHAP/common/utils/scanparsers.py +735 -339
  12. CHAP/common/writer.py +31 -25
  13. CHAP/edd/models.py +65 -51
  14. CHAP/edd/processor.py +136 -113
  15. CHAP/edd/reader.py +1 -1
  16. CHAP/edd/writer.py +1 -1
  17. CHAP/inference/processor.py +35 -28
  18. CHAP/inference/reader.py +1 -1
  19. CHAP/inference/writer.py +1 -1
  20. CHAP/pipeline.py +14 -28
  21. CHAP/processor.py +44 -75
  22. CHAP/reader.py +49 -40
  23. CHAP/runner.py +73 -32
  24. CHAP/saxswaxs/processor.py +1 -1
  25. CHAP/saxswaxs/reader.py +1 -1
  26. CHAP/saxswaxs/writer.py +1 -1
  27. CHAP/server.py +130 -0
  28. CHAP/sin2psi/processor.py +1 -1
  29. CHAP/sin2psi/reader.py +1 -1
  30. CHAP/sin2psi/writer.py +1 -1
  31. CHAP/tomo/__init__.py +1 -4
  32. CHAP/tomo/models.py +53 -31
  33. CHAP/tomo/processor.py +1326 -902
  34. CHAP/tomo/reader.py +4 -2
  35. CHAP/tomo/writer.py +4 -2
  36. CHAP/writer.py +47 -41
  37. {ChessAnalysisPipeline-0.0.4.dist-info → ChessAnalysisPipeline-0.0.6.dist-info}/METADATA +1 -1
  38. ChessAnalysisPipeline-0.0.6.dist-info/RECORD +52 -0
  39. ChessAnalysisPipeline-0.0.4.dist-info/RECORD +0 -50
  40. {ChessAnalysisPipeline-0.0.4.dist-info → ChessAnalysisPipeline-0.0.6.dist-info}/LICENSE +0 -0
  41. {ChessAnalysisPipeline-0.0.4.dist-info → ChessAnalysisPipeline-0.0.6.dist-info}/WHEEL +0 -0
  42. {ChessAnalysisPipeline-0.0.4.dist-info → ChessAnalysisPipeline-0.0.6.dist-info}/entry_points.txt +0 -0
  43. {ChessAnalysisPipeline-0.0.4.dist-info → ChessAnalysisPipeline-0.0.6.dist-info}/top_level.txt +0 -0
@@ -1,17 +1,8 @@
1
1
  import copy
2
- from functools import cache, lru_cache
3
- import json
4
- import logging
2
+ from functools import cache
5
3
  import os
6
- from time import time
7
4
  from typing import Literal, Optional
8
5
 
9
- # from multiprocessing.pool import ThreadPool
10
- # from nexusformat.nexus import (NXdata,
11
- # NXdetector,
12
- # NXfield,
13
- # NXprocess,
14
- # NXroot)
15
6
  import numpy as np
16
7
  from pydantic import (BaseModel,
17
8
  validator,
@@ -20,19 +11,15 @@ from pydantic import (BaseModel,
20
11
  conint,
21
12
  confloat,
22
13
  FilePath)
23
- #import pyFAI, pyFAI.multi_geometry, pyFAI.units
24
14
  from pyFAI import load as pyfai_load
25
15
  from pyFAI.multi_geometry import MultiGeometry
26
16
  from pyFAI.units import AZIMUTHAL_UNITS, RADIAL_UNITS
27
- #from pyspec.file.tiff import TiffFile
28
-
29
- #from .map import MapConfig, SpecScans
30
17
 
31
18
 
32
19
  class Detector(BaseModel):
33
- """
34
- Detector class to represent a single detector used in the experiment.
35
-
20
+ """Detector class to represent a single detector used in the
21
+ experiment.
22
+
36
23
  :param prefix: Prefix of the detector in the SPEC file.
37
24
  :type prefix: str
38
25
  :param poni_file: Path to the poni file.
@@ -43,11 +30,12 @@ class Detector(BaseModel):
43
30
  prefix: constr(strip_whitespace=True, min_length=1)
44
31
  poni_file: FilePath
45
32
  mask_file: Optional[FilePath]
33
+
46
34
  @validator('poni_file', allow_reuse=True)
47
35
  def validate_poni_file(cls, poni_file):
48
- """
49
- Validate the poni file by checking if it's a valid PONI file.
50
-
36
+ """Validate the poni file by checking if it's a valid PONI
37
+ file.
38
+
51
39
  :param poni_file: Path to the poni file.
52
40
  :type poni_file: str
53
41
  :raises ValueError: If poni_file is not a valid PONI file.
@@ -57,52 +45,79 @@ class Detector(BaseModel):
57
45
  poni_file = os.path.abspath(poni_file)
58
46
  try:
59
47
  ai = azimuthal_integrator(poni_file)
60
- except:
61
- raise(ValueError(f'{poni_file} is not a valid PONI file'))
62
- else:
63
- return(poni_file)
48
+ except Exception as exc:
49
+ raise ValueError(f'{poni_file} is not a valid PONI file') from exc
50
+ return poni_file
51
+
64
52
  @validator('mask_file', allow_reuse=True)
65
53
  def validate_mask_file(cls, mask_file, values):
66
- """
67
- Validate the mask file. If a mask file is provided, it checks if it's a valid TIFF file.
68
-
54
+ """Validate the mask file. If a mask file is provided, it
55
+ checks if it's a valid TIFF file.
56
+
69
57
  :param mask_file: Path to the mask file.
70
58
  :type mask_file: str or None
71
59
  :param values: A dictionary of the Detector fields.
72
60
  :type values: dict
73
- :raises ValueError: If mask_file is provided and it's not a valid TIFF file.
61
+ :raises ValueError: If mask_file is provided and it's not a
62
+ valid TIFF file.
74
63
  :raises ValueError: If `'poni_file'` is not provided in `values`.
75
64
  :returns: Absolute path to the mask file or None.
76
- :rtype: str or None
65
+ :rtype: str or None
77
66
  """
78
67
  if mask_file is None:
79
- return(mask_file)
80
- else:
81
- mask_file = os.path.abspath(mask_file)
82
- poni_file = values.get('poni_file')
83
- if poni_file is None:
84
- raise(ValueError('Cannot validate mask file without a PONI file.'))
85
- else:
86
- try:
87
- mask_array = get_mask_array(mask_file, poni_file)
88
- except BaseException as e:
89
- raise(ValueError(f'Unable to open {mask_file} as a TIFF file'))
90
- else:
91
- return(mask_file)
68
+ return mask_file
69
+
70
+ mask_file = os.path.abspath(mask_file)
71
+ poni_file = values.get('poni_file')
72
+ if poni_file is None:
73
+ raise ValueError(
74
+ 'Cannot validate mask file without a PONI file.')
75
+ try:
76
+ mask_array = get_mask_array(mask_file, poni_file)
77
+ except BaseException as exc:
78
+ raise ValueError(
79
+ f'Unable to open {mask_file} as a TIFF file') from exc
80
+ return mask_file
81
+
92
82
  @property
93
83
  def azimuthal_integrator(self):
94
- return(azimuthal_integrator(self.poni_file))
84
+ """Return the azimuthal integrator associated with this
85
+ detector.
86
+ """
87
+ return azimuthal_integrator(self.poni_file)
88
+
95
89
  @property
96
90
  def mask_array(self):
97
- return(get_mask_array(self.mask_file, self.poni_file))
91
+ """Return the mask array assocated with this detector."""
92
+ return get_mask_array(self.mask_file, self.poni_file)
93
+
98
94
 
99
95
  @cache
100
96
  def azimuthal_integrator(poni_file:str):
97
+ """Return the azimuthal integrator from a PONI file
98
+
99
+ :param poni_file: path to a PONI file
100
+ :type poni_file: str
101
+ :return: azimuthal integrator
102
+ :rtype: pyFAI.azimuthal_integrator.AzimuthalIntegrator
103
+ """
101
104
  if not isinstance(poni_file, str):
102
105
  poni_file = str(poni_file)
103
- return(pyfai_load(poni_file))
106
+ return pyfai_load(poni_file)
107
+
108
+
104
109
  @cache
105
110
  def get_mask_array(mask_file:str, poni_file:str):
111
+ """Return a mask array associated with a detector loaded from a
112
+ tiff file.
113
+
114
+ :param mask_file: path to a .tiff file
115
+ :type mask_file: str
116
+ :param poni_file: path to a PONI file
117
+ :type poni_file: str
118
+ :return: the mask array loaded from `mask_file`
119
+ :rtype: numpy.ndarray
120
+ """
106
121
  if mask_file is not None:
107
122
  if not isinstance(mask_file, str):
108
123
  mask_file = str(mask_file)
@@ -112,37 +127,46 @@ def get_mask_array(mask_file:str, poni_file:str):
112
127
  mask_array = tiff.asarray()
113
128
  else:
114
129
  mask_array = np.zeros(azimuthal_integrator(poni_file).detector.shape)
115
- return(mask_array)
130
+ return mask_array
131
+
116
132
 
117
133
  class IntegrationConfig(BaseModel):
118
- """
119
- Class representing the configuration for a raw detector data integration.
134
+ """Class representing the configuration for a raw detector data
135
+ integration.
120
136
 
121
- :ivar tool_type: type of integration tool; always set to "integration"
137
+ :ivar tool_type: type of integration tool; always set to
138
+ "integration"
122
139
  :type tool_type: str, optional
123
140
  :ivar title: title of the integration
124
141
  :type title: str
125
- :ivar integration_type: type of integration, one of "azimuthal", "radial", or "cake"
142
+ :ivar integration_type: type of integration, one of "azimuthal",
143
+ "radial", or "cake"
126
144
  :type integration_type: str
127
145
  :ivar detectors: list of detectors used in the integration
128
146
  :type detectors: List[Detector]
129
- :ivar radial_units: radial units for the integration, defaults to `'q_A^-1'`
147
+ :ivar radial_units: radial units for the integration, defaults to
148
+ `'q_A^-1'`
130
149
  :type radial_units: str, optional
131
150
  :ivar radial_min: minimum radial value for the integration range
132
151
  :type radial_min: float, optional
133
152
  :ivar radial_max: maximum radial value for the integration range
134
153
  :type radial_max: float, optional
135
- :ivar radial_npt: number of points in the radial range for the integration
154
+ :ivar radial_npt: number of points in the radial range for the
155
+ integration
136
156
  :type radial_npt: int, optional
137
157
  :ivar azimuthal_units: azimuthal units for the integration
138
158
  :type azimuthal_units: str, optional
139
- :ivar azimuthal_min: minimum azimuthal value for the integration range
159
+ :ivar azimuthal_min: minimum azimuthal value for the integration
160
+ range
140
161
  :type azimuthal_min: float, optional
141
- :ivar azimuthal_max: maximum azimuthal value for the integration range
162
+ :ivar azimuthal_max: maximum azimuthal value for the integration
163
+ range
142
164
  :type azimuthal_max: float, optional
143
- :ivar azimuthal_npt: number of points in the azimuthal range for the integration
165
+ :ivar azimuthal_npt: number of points in the azimuthal range for
166
+ the integration
144
167
  :type azimuthal_npt: int, optional
145
- :ivar error_model: error model for the integration, one of "poisson" or "azimuthal"
168
+ :ivar error_model: error model for the integration, one of
169
+ "poisson" or "azimuthal"
146
170
  :type error_model: str, optional
147
171
  """
148
172
  tool_type: Literal['integration'] = 'integration'
@@ -159,71 +183,95 @@ class IntegrationConfig(BaseModel):
159
183
  azimuthal_npt: conint(gt=0) = 3600
160
184
  error_model: Optional[Literal['poisson', 'azimuthal']]
161
185
  sequence_index: Optional[conint(gt=0)]
186
+
162
187
  @validator('radial_units', allow_reuse=True)
163
188
  def validate_radial_units(cls, radial_units):
164
- """
165
- Validate the radial units for the integration.
189
+ """Validate the radial units for the integration.
166
190
 
167
- :param radial_units: unvalidated radial units for the integration
191
+ :param radial_units: unvalidated radial units for the
192
+ integration
168
193
  :type radial_units: str
169
- :raises ValueError: if radial units are not one of the recognized radial units
194
+ :raises ValueError: if radial units are not one of the
195
+ recognized radial units
170
196
  :return: validated radial units
171
197
  :rtype: str
172
198
  """
173
199
  if radial_units in RADIAL_UNITS.keys():
174
- return(radial_units)
175
- else:
176
- raise(ValueError(f'Invalid radial units: {radial_units}. Must be one of {", ".join(RADIAL_UNITS.keys())}'))
200
+ return radial_units
201
+ raise ValueError(
202
+ f'Invalid radial units: {radial_units}. '
203
+ f'Must be one of {", ".join(RADIAL_UNITS.keys())}')
204
+
177
205
  @validator('azimuthal_units', allow_reuse=True)
178
206
  def validate_azimuthal_units(cls, azimuthal_units):
179
- """
180
- Validate that `azimuthal_units` is one of the keys in the
207
+ """Validate that `azimuthal_units` is one of the keys in the
181
208
  `pyFAI.units.AZIMUTHAL_UNITS` dictionary.
182
209
 
183
- :param azimuthal_units: The string representing the unit to be validated.
210
+ :param azimuthal_units: The string representing the unit to be
211
+ validated.
184
212
  :type azimuthal_units: str
185
- :raises ValueError: If `azimuthal_units` is not one of the keys in `pyFAI.units.AZIMUTHAL_UNITS`
186
- :return: The original supplied value, if is one of the keys in `pyFAI.units.AZIMUTHAL_UNITS`.
213
+ :raises ValueError: If `azimuthal_units` is not one of the
214
+ keys in `pyFAI.units.AZIMUTHAL_UNITS`
215
+ :return: The original supplied value, if is one of the keys in
216
+ `pyFAI.units.AZIMUTHAL_UNITS`.
187
217
  :rtype: str
188
218
  """
189
219
  if azimuthal_units in AZIMUTHAL_UNITS.keys():
190
- return(azimuthal_units)
191
- else:
192
- raise(ValueError(f'Invalid azimuthal units: {azimuthal_units}. Must be one of {", ".join(AZIMUTHAL_UNITS.keys())}'))
220
+ return azimuthal_units
221
+ raise ValueError(
222
+ f'Invalid azimuthal units: {azimuthal_units}. '
223
+ f'Must be one of {", ".join(AZIMUTHAL_UNITS.keys())}')
224
+
193
225
  def validate_range_max(range_name:str):
194
226
  """Validate the maximum value of an integration range.
195
227
 
196
- :param range_name: The name of the integration range (e.g. radial, azimuthal).
228
+ :param range_name: The name of the integration range
229
+ (e.g. radial, azimuthal).
197
230
  :type range_name: str
198
231
  :return: The callable that performs the validation.
199
232
  :rtype: callable
200
233
  """
201
234
  def _validate_range_max(cls, range_max, values):
202
- """Check if the maximum value of the integration range is greater than its minimum value.
235
+ """Check if the maximum value of the integration range is
236
+ greater than its minimum value.
203
237
 
204
- :param range_max: The maximum value of the integration range.
238
+ :param range_max: The maximum value of the integration
239
+ range.
205
240
  :type range_max: float
206
- :param values: The values of the other fields being validated.
241
+ :param values: The values of the other fields being
242
+ validated.
207
243
  :type values: dict
208
- :raises ValueError: If the maximum value of the integration range is not greater than its minimum value.
244
+ :raises ValueError: If the maximum value of the
245
+ integration range is not greater than its minimum
246
+ value.
209
247
  :return: The validated maximum range value
210
248
  :rtype: float
211
249
  """
212
250
  range_min = values.get(f'{range_name}_min')
213
251
  if range_min < range_max:
214
- return(range_max)
215
- else:
216
- raise(ValueError(f'Maximum value of integration range must be greater than minimum value of integration range ({range_name}_min={range_min}).'))
217
- return(_validate_range_max)
218
- _validate_radial_max = validator('radial_max', allow_reuse=True)(validate_range_max('radial'))
219
- _validate_azimuthal_max = validator('azimuthal_max', allow_reuse=True)(validate_range_max('azimuthal'))
252
+ return range_max
253
+ raise ValueError(
254
+ 'Maximum value of integration range must be '
255
+ 'greater than minimum value of integration range '
256
+ f'({range_name}_min={range_min}).')
257
+ return _validate_range_max
258
+
259
+ _validate_radial_max = validator(
260
+ 'radial_max',
261
+ allow_reuse=True)(validate_range_max('radial'))
262
+ _validate_azimuthal_max = validator(
263
+ 'azimuthal_max',
264
+ allow_reuse=True)(validate_range_max('azimuthal'))
265
+
220
266
  def validate_for_map_config(self, map_config:BaseModel):
221
- """
222
- Validate the existence of the detector data file for all scan points in `map_config`.
223
-
224
- :param map_config: The `MapConfig` instance to validate against.
267
+ """Validate the existence of the detector data file for all
268
+ scan points in `map_config`.
269
+
270
+ :param map_config: The `MapConfig` instance to validate
271
+ against.
225
272
  :type map_config: MapConfig
226
- :raises RuntimeError: If a detector data file could not be found for a scan point occurring in `map_config`.
273
+ :raises RuntimeError: If a detector data file could not be
274
+ found for a scan point occurring in `map_config`.
227
275
  :return: None
228
276
  :rtype: None
229
277
  """
@@ -232,39 +280,55 @@ class IntegrationConfig(BaseModel):
232
280
  for scan_number in scans.scan_numbers:
233
281
  scanparser = scans.get_scanparser(scan_number)
234
282
  for scan_step_index in range(scanparser.spec_scan_npts):
235
- # Make sure the detector data file exists for all scan points
283
+ # Make sure the detector data file exists for
284
+ # all scan points
236
285
  try:
237
- detector_data_file = scanparser.get_detector_data_file(detector.prefix, scan_step_index)
238
- except:
239
- raise(RuntimeError(f'Could not find data file for detector prefix {detector.prefix} on scan number {scan_number} in spec file {scans.spec_file}'))
286
+ detector_data_file = \
287
+ scanparser.get_detector_data_file(
288
+ detector.prefix, scan_step_index)
289
+ except Exception as exc:
290
+ raise RuntimeError(
291
+ 'Could not find data file for detector prefix '
292
+ f'{detector.prefix} '
293
+ f'on scan number {scan_number} '
294
+ f'in spec file {scans.spec_file}') from exc
295
+
240
296
  def get_azimuthal_adjustments(self):
241
- """To enable a continuous range of integration in the azimuthal direction
242
- for radial and cake integration, obtain adjusted values for this
243
- `IntegrationConfig`'s `azimuthal_min` and `azimuthal_max` values, the
244
- angle amount by which those values were adjusted, and the proper location
245
- of the discontinuity in the azimuthal direction.
297
+ """To enable a continuous range of integration in the
298
+ azimuthal direction for radial and cake integration, obtain
299
+ adjusted values for this `IntegrationConfig`'s `azimuthal_min`
300
+ and `azimuthal_max` values, the angle amount by which those
301
+ values were adjusted, and the proper location of the
302
+ discontinuity in the azimuthal direction.
246
303
 
247
- :return: Adjusted chi_min, adjusted chi_max, chi_offset, chi_discontinuity
304
+ :return: Adjusted chi_min, adjusted chi_max, chi_offset,
305
+ chi_discontinuity
248
306
  :rtype: tuple[float,float,float,float]
249
307
  """
250
- return(get_azimuthal_adjustments(self.azimuthal_min, self.azimuthal_max))
308
+ return get_azimuthal_adjustments(self.azimuthal_min,
309
+ self.azimuthal_max)
310
+
251
311
  def get_azimuthal_integrators(self):
252
- """Get a list of `AzimuthalIntegrator`s that correspond to the detector
253
- configurations in this instance of `IntegrationConfig`.
312
+ """Get a list of `AzimuthalIntegrator`s that correspond to the
313
+ detector configurations in this instance of
314
+ `IntegrationConfig`.
254
315
 
255
- The returned `AzimuthalIntegrator`s are (if need be) artificially rotated
256
- in the azimuthal direction to achieve a continuous range of integration
257
- in the azimuthal direction.
316
+ The returned `AzimuthalIntegrator`s are (if need be)
317
+ artificially rotated in the azimuthal direction to achieve a
318
+ continuous range of integration in the azimuthal direction.
258
319
 
259
- :returns: A list of `AzimuthalIntegrator`s appropriate for use by this
260
- `IntegrationConfig` tool
320
+ :returns: A list of `AzimuthalIntegrator`s appropriate for use
321
+ by this `IntegrationConfig` tool
261
322
  :rtype: list[pyFAI.azimuthalIntegrator.AzimuthalIntegrator]
262
323
  """
263
- chi_min, chi_max, chi_offset, chi_disc = self.get_azimuthal_adjustments()
264
- return(get_azimuthal_integrators(tuple([detector.poni_file for detector in self.detectors]), chi_offset=chi_offset))
324
+ chi_offset = self.get_azimuthal_adjustments()[2]
325
+ return get_azimuthal_integrators(
326
+ tuple([detector.poni_file for detector in self.detectors]),
327
+ chi_offset=chi_offset)
328
+
265
329
  def get_multi_geometry_integrator(self):
266
- """Get a `MultiGeometry` integrator suitable for use by this instance of
267
- `IntegrationConfig`.
330
+ """Get a `MultiGeometry` integrator suitable for use by this
331
+ instance of `IntegrationConfig`.
268
332
 
269
333
  :return: A `MultiGeometry` integrator
270
334
  :rtype: pyFAI.multi_geometry.MultiGeometry
@@ -272,94 +336,138 @@ class IntegrationConfig(BaseModel):
272
336
  poni_files = tuple([detector.poni_file for detector in self.detectors])
273
337
  radial_range = (self.radial_min, self.radial_max)
274
338
  azimuthal_range = (self.azimuthal_min, self.azimuthal_max)
275
- return(get_multi_geometry_integrator(poni_files, self.radial_units, radial_range, azimuthal_range))
276
- def get_azimuthally_integrated_data(self, spec_scans:BaseModel, scan_number:int, scan_step_index:int):
277
- """Return azimuthally-integrated data for the scan step specified.
278
-
279
- :param spec_scans: An instance of `SpecScans` containing the scan step requested.
339
+ return get_multi_geometry_integrator(poni_files, self.radial_units,
340
+ radial_range, azimuthal_range)
341
+
342
+ def get_azimuthally_integrated_data(self,
343
+ spec_scans:BaseModel,
344
+ scan_number:int,
345
+ scan_step_index:int):
346
+ """Return azimuthally-integrated data for the scan step
347
+ specified.
348
+
349
+ :param spec_scans: An instance of `SpecScans` containing the
350
+ scan step requested.
280
351
  :type spec_scans: SpecScans
281
- :param scan_number: The number of the scan containing the scan step requested.
352
+ :param scan_number: The number of the scan containing the scan
353
+ step requested.
282
354
  :type scan_number: int
283
355
  :param scan_step_index: The index of the scan step requested.
284
356
  :type scan_step_index: int
285
- :return: A 1D array of azimuthally-integrated raw detector intensities.
357
+ :return: A 1D array of azimuthally-integrated raw detector
358
+ intensities.
286
359
  :rtype: np.ndarray
287
360
  """
288
- detector_data = spec_scans.get_detector_data(self.detectors, scan_number, scan_step_index)
361
+ detector_data = spec_scans.get_detector_data(self.detectors,
362
+ scan_number,
363
+ scan_step_index)
289
364
  integrator = self.get_multi_geometry_integrator()
290
365
  lst_mask = [detector.mask_array for detector in self.detectors]
291
- result = integrator.integrate1d(detector_data, lst_mask=lst_mask, npt=self.radial_npt, error_model=self.error_model)
366
+ result = integrator.integrate1d(detector_data,
367
+ lst_mask=lst_mask,
368
+ npt=self.radial_npt,
369
+ error_model=self.error_model)
292
370
  if result.sigma is None:
293
- return(result.intensity)
294
- else:
295
- return(result.intensity, result.sigma)
296
- def get_radially_integrated_data(self, spec_scans:BaseModel, scan_number:int, scan_step_index:int):
297
- """Return radially-integrated data for the scan step specified.
298
-
299
- :param spec_scans: An instance of `SpecScans` containing the scan step requested.
371
+ return result.intensity
372
+ return result.intensity, result.sigma
373
+
374
+ def get_radially_integrated_data(self,
375
+ spec_scans:BaseModel,
376
+ scan_number:int,
377
+ scan_step_index:int):
378
+ """Return radially-integrated data for the scan step
379
+ specified.
380
+
381
+ :param spec_scans: An instance of `SpecScans` containing the
382
+ scan step requested.
300
383
  :type spec_scans: SpecScans
301
- :param scan_number: The number of the scan containing the scan step requested.
384
+ :param scan_number: The number of the scan containing the scan
385
+ step requested.
302
386
  :type scan_number: int
303
387
  :param scan_step_index: The index of the scan step requested.
304
388
  :type scan_step_index: int
305
- :return: A 1D array of radially-integrated raw detector intensities.
389
+ :return: A 1D array of radially-integrated raw detector
390
+ intensities.
306
391
  :rtype: np.ndarray
307
392
  """
308
- # Handle idiosyncracies of azimuthal ranges in pyFAI
309
- # Adjust chi ranges to get a continuous range of iintegrated data
310
- chi_min, chi_max, chi_offset, chi_disc = self.get_azimuthal_adjustments()
393
+ # Handle idiosyncracies of azimuthal ranges in pyFAI Adjust
394
+ # chi ranges to get a continuous range of iintegrated data
395
+ chi_min, chi_max, *adjust = self.get_azimuthal_adjustments()
311
396
  # Perform radial integration on a detector-by-detector basis.
312
- I_each_detector = []
397
+ intensity_each_detector = []
313
398
  variance_each_detector = []
314
399
  integrators = self.get_azimuthal_integrators()
315
- for i,(integrator,detector) in enumerate(zip(integrators,self.detectors)):
316
- detector_data = spec_scans.get_detector_data([detector], scan_number, scan_step_index)[0]
317
- result = integrator.integrate_radial(detector_data, self.azimuthal_npt,
318
- unit=self.azimuthal_units, azimuth_range=(chi_min,chi_max),
319
- radial_unit=self.radial_units, radial_range=(self.radial_min,self.radial_max),
320
- mask=detector.mask_array) #, error_model=self.error_model)
321
- I_each_detector.append(result.intensity)
400
+ for integrator, detector in zip(integrators, self.detectors):
401
+ detector_data = spec_scans.get_detector_data(
402
+ [detector], scan_number, scan_step_index)[0]
403
+ result = integrator.integrate_radial(
404
+ detector_data,
405
+ self.azimuthal_npt,
406
+ unit=self.azimuthal_units,
407
+ azimuth_range=(chi_min, chi_max),
408
+ radial_unit=self.radial_units,
409
+ radial_range=(self.radial_min, self.radial_max),
410
+ mask=detector.mask_array) # , error_model=self.error_model)
411
+ intensity_each_detector.append(result.intensity)
322
412
  if result.sigma is not None:
323
413
  variance_each_detector.append(result.sigma**2)
324
- # Add the individual detectors' integrated intensities together
325
- I = np.nansum(I_each_detector, axis=0)
414
+ # Add the individual detectors' integrated intensities
415
+ # together
416
+ intensity = np.nansum(intensity_each_detector, axis=0)
326
417
  # Ignore data at values of chi for which there was no data
327
- I = np.where(I==0, np.nan, I)
328
- if len(I_each_detector) != len(variance_each_detector):
329
- return(I)
330
- else:
331
- # Get the standard deviation of the summed detectors' intensities
332
- sigma = np.sqrt(np.nansum(variance_each_detector, axis=0))
333
- return(I, sigma)
334
- def get_cake_integrated_data(self, spec_scans:BaseModel, scan_number:int, scan_step_index:int):
418
+ intensity = np.where(intensity == 0, np.nan, intensity)
419
+ if len(intensity_each_detector) != len(variance_each_detector):
420
+ return intensity
421
+
422
+ # Get the standard deviation of the summed detectors'
423
+ # intensities
424
+ sigma = np.sqrt(np.nansum(variance_each_detector, axis=0))
425
+ return intensity, sigma
426
+
427
+ def get_cake_integrated_data(self,
428
+ spec_scans:BaseModel,
429
+ scan_number:int,
430
+ scan_step_index:int):
335
431
  """Return cake-integrated data for the scan step specified.
336
-
337
- :param spec_scans: An instance of `SpecScans` containing the scan step requested.
432
+
433
+ :param spec_scans: An instance of `SpecScans` containing the
434
+ scan step requested.
338
435
  :type spec_scans: SpecScans
339
- :param scan_number: The number of the scan containing the scan step requested.
436
+ :param scan_number: The number of the scan containing the scan
437
+ step requested.
340
438
  :type scan_number: int
341
439
  :param scan_step_index: The index of the scan step requested.
342
440
  :type scan_step_index: int
343
- :return: A 2D array of cake-integrated raw detector intensities.
441
+ :return: A 2D array of cake-integrated raw detector
442
+ intensities.
344
443
  :rtype: np.ndarray
345
444
  """
346
- detector_data = spec_scans.get_detector_data(self.detectors, scan_number, scan_step_index)
445
+ detector_data = spec_scans.get_detector_data(
446
+ self.detectors, scan_number, scan_step_index)
347
447
  integrator = self.get_multi_geometry_integrator()
348
448
  lst_mask = [detector.mask_array for detector in self.detectors]
349
- result = integrator.integrate2d(detector_data, lst_mask=lst_mask,
350
- npt_rad=self.radial_npt, npt_azim=self.azimuthal_npt,
351
- method='bbox',
352
- error_model=self.error_model)
449
+ result = integrator.integrate2d(
450
+ detector_data,
451
+ lst_mask=lst_mask,
452
+ npt_rad=self.radial_npt,
453
+ npt_azim=self.azimuthal_npt,
454
+ method='bbox',
455
+ error_model=self.error_model)
353
456
  if result.sigma is None:
354
- return(result.intensity)
355
- else:
356
- return(result.intensity, result.sigma)
357
- def get_integrated_data(self, spec_scans:BaseModel, scan_number:int, scan_step_index:int):
457
+ return result.intensity
458
+ return result.intensity, result.sigma
459
+
460
+ def get_integrated_data(self,
461
+ spec_scans:BaseModel,
462
+ scan_number:int,
463
+ scan_step_index:int):
358
464
  """Return integrated data for the scan step specified.
359
-
360
- :param spec_scans: An instance of `SpecScans` containing the scan step requested.
465
+
466
+ :param spec_scans: An instance of `SpecScans` containing the
467
+ scan step requested.
361
468
  :type spec_scans: SpecScans
362
- :param scan_number: The number of the scan containing the scan step requested.
469
+ :param scan_number: The number of the scan containing the scan
470
+ step requested.
363
471
  :type scan_number: int
364
472
  :param scan_step_index: The index of the scan step requested.
365
473
  :type scan_step_index: int
@@ -367,84 +475,106 @@ class IntegrationConfig(BaseModel):
367
475
  :rtype: np.ndarray
368
476
  """
369
477
  if self.integration_type == 'azimuthal':
370
- return(self.get_azimuthally_integrated_data(spec_scans, scan_number, scan_step_index))
371
- elif self.integration_type == 'radial':
372
- return(self.get_radially_integrated_data(spec_scans, scan_number, scan_step_index))
373
- elif self.integration_type == 'cake':
374
- return(self.get_cake_integrated_data(spec_scans, scan_number, scan_step_index))
478
+ return self.get_azimuthally_integrated_data(spec_scans,
479
+ scan_number,
480
+ scan_step_index)
481
+ if self.integration_type == 'radial':
482
+ return self.get_radially_integrated_data(spec_scans,
483
+ scan_number,
484
+ scan_step_index)
485
+ if self.integration_type == 'cake':
486
+ return self.get_cake_integrated_data(spec_scans,
487
+ scan_number,
488
+ scan_step_index)
489
+ return None
375
490
 
376
491
  @property
377
492
  def integrated_data_coordinates(self):
378
- """
379
- Return a dictionary of coordinate arrays for navigating the dimension(s)
380
- of the integrated data produced by this instance of `IntegrationConfig`.
381
-
382
- :return: A dictionary with either one or two keys: 'azimuthal' and/or
383
- 'radial', each of which points to a 1-D `numpy` array of coordinate
384
- values.
493
+ """Return a dictionary of coordinate arrays for navigating the
494
+ dimension(s) of the integrated data produced by this instance
495
+ of `IntegrationConfig`.
496
+
497
+ :return: A dictionary with either one or two keys: 'azimuthal'
498
+ and/or 'radial', each of which points to a 1-D `numpy`
499
+ array of coordinate values.
385
500
  :rtype: dict[str,np.ndarray]
386
501
  """
387
502
  if self.integration_type == 'azimuthal':
388
- return(get_integrated_data_coordinates(radial_range=(self.radial_min,self.radial_max),
389
- radial_npt=self.radial_npt))
390
- elif self.integration_type == 'radial':
391
- return(get_integrated_data_coordinates(azimuthal_range=(self.azimuthal_min,self.azimuthal_max),
392
- azimuthal_npt=self.azimuthal_npt))
393
- elif self.integration_type == 'cake':
394
- return(get_integrated_data_coordinates(radial_range=(self.radial_min,self.radial_max),
395
- radial_npt=self.radial_npt,
396
- azimuthal_range=(self.azimuthal_min,self.azimuthal_max),
397
- azimuthal_npt=self.azimuthal_npt))
503
+ return get_integrated_data_coordinates(
504
+ radial_range=(self.radial_min, self.radial_max),
505
+ radial_npt=self.radial_npt)
506
+ if self.integration_type == 'radial':
507
+ return get_integrated_data_coordinates(
508
+ azimuthal_range=(self.azimuthal_min, self.azimuthal_max),
509
+ azimuthal_npt=self.azimuthal_npt)
510
+ if self.integration_type == 'cake':
511
+ return get_integrated_data_coordinates(
512
+ radial_range=(self.radial_min, self.radial_max),
513
+ radial_npt=self.radial_npt,
514
+ azimuthal_range=(self.azimuthal_min, self.azimuthal_max),
515
+ azimuthal_npt=self.azimuthal_npt)
516
+ return None
517
+
398
518
  @property
399
519
  def integrated_data_dims(self):
400
- """Return a tuple of the coordinate labels for the integrated data
401
- produced by this instance of `IntegrationConfig`.
520
+ """Return a tuple of the coordinate labels for the integrated
521
+ data produced by this instance of `IntegrationConfig`.
402
522
  """
403
523
  directions = list(self.integrated_data_coordinates.keys())
404
- dim_names = [getattr(self, f'{direction}_units') for direction in directions]
405
- return(dim_names)
524
+ dim_names = [getattr(self, f'{direction}_units')
525
+ for direction in directions]
526
+ return dim_names
527
+
406
528
  @property
407
529
  def integrated_data_shape(self):
408
- """Return a tuple representing the shape of the integrated data
409
- produced by this instance of `IntegrationConfig` for a single scan step.
530
+ """Return a tuple representing the shape of the integrated
531
+ data produced by this instance of `IntegrationConfig` for a
532
+ single scan step.
410
533
  """
411
- return(tuple([len(coordinate_values) for coordinate_name,coordinate_values in self.integrated_data_coordinates.items()]))
534
+ return tuple([len(coordinate_values)
535
+ for coordinate_name, coordinate_values
536
+ in self.integrated_data_coordinates.items()])
537
+
412
538
 
413
539
  @cache
414
540
  def get_azimuthal_adjustments(chi_min:float, chi_max:float):
415
- """
416
- Fix chi discontinuity at 180 degrees and return the adjusted chi range,
417
- offset, and discontinuty.
541
+ """Fix chi discontinuity at 180 degrees and return the adjusted
542
+ chi range, offset, and discontinuty.
418
543
 
419
- If the discontinuity is crossed, obtain the offset to artificially rotate
420
- detectors to achieve a continuous azimuthal integration range.
544
+ If the discontinuity is crossed, obtain the offset to artificially
545
+ rotate detectors to achieve a continuous azimuthal integration
546
+ range.
421
547
 
422
548
  :param chi_min: The minimum value of the azimuthal range.
423
549
  :type chi_min: float
424
550
  :param chi_max: The maximum value of the azimuthal range.
425
551
  :type chi_max: float
426
- :return: The following four values: the adjusted minimum value of the
427
- azimuthal range, the adjusted maximum value of the azimuthal range, the
428
- value by which the chi angle was adjusted, the position of the chi
429
- discontinuity.
552
+ :return: The following four values: the adjusted minimum value of
553
+ the azimuthal range, the adjusted maximum value of the
554
+ azimuthal range, the value by which the chi angle was
555
+ adjusted, the position of the chi discontinuity.
430
556
  """
431
557
  # Fix chi discontinuity at 180 degrees for now.
432
558
  chi_disc = 180
433
- # If the discontinuity is crossed, artificially rotate the detectors to
434
- # achieve a continuous azimuthal integration range
559
+ # If the discontinuity is crossed, artificially rotate the
560
+ # detectors to achieve a continuous azimuthal integration range
435
561
  if chi_min < chi_disc and chi_max > chi_disc:
436
562
  chi_offset = chi_max - chi_disc
437
563
  else:
438
564
  chi_offset = 0
439
- return(chi_min-chi_offset, chi_max-chi_offset, chi_offset, chi_disc)
565
+ return chi_min-chi_offset, chi_max-chi_offset, chi_offset, chi_disc
566
+
567
+
440
568
  @cache
441
569
  def get_azimuthal_integrators(poni_files:tuple, chi_offset=0):
442
- """
443
- Return a list of `AzimuthalIntegrator` objects generated from PONI files.
444
-
445
- :param poni_files: Tuple of strings, each string being a path to a PONI file. : tuple
570
+ """Return a list of `AzimuthalIntegrator` objects generated from
571
+ PONI files.
572
+
573
+ :param poni_files: Tuple of strings, each string being a path to a
574
+ PONI file.
446
575
  :type poni_files: tuple
447
- :param chi_offset: The angle in degrees by which the `AzimuthalIntegrator` objects will be rotated, defaults to 0.
576
+ :param chi_offset: The angle in degrees by which the
577
+ `AzimuthalIntegrator` objects will be rotated, defaults to 0.
448
578
  :type chi_offset: float, optional
449
579
  :return: List of `AzimuthalIntegrator` objects
450
580
  :rtype: list[pyFAI.azimuthalIntegrator.AzimuthalIntegrator]
@@ -454,62 +584,75 @@ def get_azimuthal_integrators(poni_files:tuple, chi_offset=0):
454
584
  ai = copy.deepcopy(azimuthal_integrator(poni_file))
455
585
  ai.rot3 += chi_offset * np.pi/180
456
586
  ais.append(ai)
457
- return(ais)
587
+ return ais
588
+
589
+
458
590
  @cache
459
- def get_multi_geometry_integrator(poni_files:tuple, radial_unit:str, radial_range:tuple, azimuthal_range:tuple):
460
- """Return a `MultiGeometry` instance that can be used for azimuthal or cake
461
- integration.
591
+ def get_multi_geometry_integrator(poni_files:tuple, radial_unit:str,
592
+ radial_range:tuple, azimuthal_range:tuple):
593
+ """Return a `MultiGeometry` instance that can be used for
594
+ azimuthal or cake integration.
462
595
 
463
- :param poni_files: Tuple of PONI files that describe the detectors to be
464
- integrated.
596
+ :param poni_files: Tuple of PONI files that describe the detectors
597
+ to be integrated.
465
598
  :type poni_files: tuple
466
599
  :param radial_unit: Unit to use for radial integration range.
467
600
  :type radial_unit: str
468
601
  :param radial_range: Tuple describing the range for radial integration.
469
602
  :type radial_range: tuple[float,float]
470
- :param azimuthal_range:Tuple describing the range for azimuthal integration.
471
- :type azimuthal_range: tuple[float,float]
472
- :return: `MultiGeometry` instance that can be used for azimuthal or cake
603
+ :param azimuthal_range:Tuple describing the range for azimuthal
473
604
  integration.
605
+ :type azimuthal_range: tuple[float,float]
606
+ :return: `MultiGeometry` instance that can be used for azimuthal
607
+ or cake integration.
474
608
  :rtype: pyFAI.multi_geometry.MultiGeometry
475
609
  """
476
- chi_min, chi_max, chi_offset, chi_disc = get_azimuthal_adjustments(*azimuthal_range)
477
- ais = copy.deepcopy(get_azimuthal_integrators(poni_files, chi_offset=chi_offset))
478
- multi_geometry = MultiGeometry(ais,
479
- unit=radial_unit,
480
- radial_range=radial_range,
481
- azimuth_range=(chi_min,chi_max),
482
- wavelength=sum([ai.wavelength for ai in ais])/len(ais),
483
- chi_disc=chi_disc)
484
- return(multi_geometry)
610
+ chi_min, chi_max, chi_offset, chi_disc = \
611
+ get_azimuthal_adjustments(*azimuthal_range)
612
+ ais = copy.deepcopy(get_azimuthal_integrators(poni_files,
613
+ chi_offset=chi_offset))
614
+ multi_geometry = MultiGeometry(
615
+ ais,
616
+ unit=radial_unit,
617
+ radial_range=radial_range,
618
+ azimuth_range=(chi_min, chi_max),
619
+ wavelength=sum([ai.wavelength for ai in ais])/len(ais),
620
+ chi_disc=chi_disc)
621
+ return multi_geometry
622
+
623
+
485
624
  @cache
486
- def get_integrated_data_coordinates(azimuthal_range:tuple=None, azimuthal_npt:int=None, radial_range:tuple=None, radial_npt:int=None):
487
- """
488
- Return a dictionary of coordinate arrays for the specified radial and/or
489
- azimuthal integration ranges.
490
-
491
- :param azimuthal_range: Tuple specifying the range of azimuthal angles over
492
- which to generate coordinates, in the format (min, max), defaults to
493
- None.
625
+ def get_integrated_data_coordinates(azimuthal_range:tuple = None,
626
+ azimuthal_npt:int = None,
627
+ radial_range:tuple = None,
628
+ radial_npt:int = None):
629
+ """Return a dictionary of coordinate arrays for the specified
630
+ radial and/or azimuthal integration ranges.
631
+
632
+ :param azimuthal_range: Tuple specifying the range of azimuthal
633
+ angles over which to generate coordinates, in the format (min,
634
+ max), defaults to None.
494
635
  :type azimuthal_range: tuple[float,float], optional
495
- :param azimuthal_npt: Number of azimuthal coordinate points to generate,
496
- defaults to None.
636
+ :param azimuthal_npt: Number of azimuthal coordinate points to
637
+ generate, defaults to None.
497
638
  :type azimuthal_npt: int, optional
498
- :param radial_range: Tuple specifying the range of radial distances over
499
- which to generate coordinates, in the format (min, max), defaults to
500
- None.
639
+ :param radial_range: Tuple specifying the range of radial
640
+ distances over which to generate coordinates, in the format
641
+ (min, max), defaults to None.
501
642
  :type radial_range: tuple[float,float], optional
502
- :param radial_npt: Number of radial coordinate points to generate, defaults
503
- to None.
643
+ :param radial_npt: Number of radial coordinate points to generate,
644
+ defaults to None.
504
645
  :type radial_npt: int, optional
505
- :return: A dictionary with either one or two keys: 'azimuthal' and/or
506
- 'radial', each of which points to a 1-D `numpy` array of coordinate
507
- values.
646
+ :return: A dictionary with either one or two keys: 'azimuthal'
647
+ and/or 'radial', each of which points to a 1-D `numpy` array
648
+ of coordinate values.
508
649
  :rtype: dict[str,np.ndarray]
509
650
  """
510
651
  integrated_data_coordinates = {}
511
652
  if azimuthal_range is not None and azimuthal_npt is not None:
512
- integrated_data_coordinates['azimuthal'] = np.linspace(*azimuthal_range, azimuthal_npt)
653
+ integrated_data_coordinates['azimuthal'] = np.linspace(
654
+ *azimuthal_range, azimuthal_npt)
513
655
  if radial_range is not None and radial_npt is not None:
514
- integrated_data_coordinates['radial'] = np.linspace(*radial_range, radial_npt)
515
- return(integrated_data_coordinates)
656
+ integrated_data_coordinates['radial'] = np.linspace(
657
+ *radial_range, radial_npt)
658
+ return integrated_data_coordinates