dkist-processing-cryonirsp 1.3.4__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 dkist-processing-cryonirsp might be problematic. Click here for more details.

Files changed (111) hide show
  1. changelog/.gitempty +0 -0
  2. dkist_processing_cryonirsp/__init__.py +11 -0
  3. dkist_processing_cryonirsp/config.py +12 -0
  4. dkist_processing_cryonirsp/models/__init__.py +1 -0
  5. dkist_processing_cryonirsp/models/constants.py +248 -0
  6. dkist_processing_cryonirsp/models/exposure_conditions.py +26 -0
  7. dkist_processing_cryonirsp/models/parameters.py +296 -0
  8. dkist_processing_cryonirsp/models/tags.py +168 -0
  9. dkist_processing_cryonirsp/models/task_name.py +14 -0
  10. dkist_processing_cryonirsp/parsers/__init__.py +1 -0
  11. dkist_processing_cryonirsp/parsers/cryonirsp_l0_fits_access.py +111 -0
  12. dkist_processing_cryonirsp/parsers/cryonirsp_l1_fits_access.py +30 -0
  13. dkist_processing_cryonirsp/parsers/exposure_conditions.py +163 -0
  14. dkist_processing_cryonirsp/parsers/map_repeats.py +40 -0
  15. dkist_processing_cryonirsp/parsers/measurements.py +55 -0
  16. dkist_processing_cryonirsp/parsers/modstates.py +31 -0
  17. dkist_processing_cryonirsp/parsers/optical_density_filters.py +40 -0
  18. dkist_processing_cryonirsp/parsers/polarimetric_check.py +120 -0
  19. dkist_processing_cryonirsp/parsers/scan_step.py +412 -0
  20. dkist_processing_cryonirsp/parsers/time.py +80 -0
  21. dkist_processing_cryonirsp/parsers/wavelength.py +26 -0
  22. dkist_processing_cryonirsp/tasks/__init__.py +19 -0
  23. dkist_processing_cryonirsp/tasks/assemble_movie.py +202 -0
  24. dkist_processing_cryonirsp/tasks/bad_pixel_map.py +96 -0
  25. dkist_processing_cryonirsp/tasks/beam_boundaries_base.py +279 -0
  26. dkist_processing_cryonirsp/tasks/ci_beam_boundaries.py +55 -0
  27. dkist_processing_cryonirsp/tasks/ci_science.py +169 -0
  28. dkist_processing_cryonirsp/tasks/cryonirsp_base.py +67 -0
  29. dkist_processing_cryonirsp/tasks/dark.py +98 -0
  30. dkist_processing_cryonirsp/tasks/gain.py +251 -0
  31. dkist_processing_cryonirsp/tasks/instrument_polarization.py +447 -0
  32. dkist_processing_cryonirsp/tasks/l1_output_data.py +44 -0
  33. dkist_processing_cryonirsp/tasks/linearity_correction.py +582 -0
  34. dkist_processing_cryonirsp/tasks/make_movie_frames.py +302 -0
  35. dkist_processing_cryonirsp/tasks/mixin/__init__.py +1 -0
  36. dkist_processing_cryonirsp/tasks/mixin/beam_access.py +52 -0
  37. dkist_processing_cryonirsp/tasks/mixin/corrections.py +177 -0
  38. dkist_processing_cryonirsp/tasks/mixin/intermediate_frame.py +193 -0
  39. dkist_processing_cryonirsp/tasks/mixin/linearized_frame.py +309 -0
  40. dkist_processing_cryonirsp/tasks/mixin/shift_measurements.py +297 -0
  41. dkist_processing_cryonirsp/tasks/parse.py +281 -0
  42. dkist_processing_cryonirsp/tasks/quality_metrics.py +271 -0
  43. dkist_processing_cryonirsp/tasks/science_base.py +511 -0
  44. dkist_processing_cryonirsp/tasks/sp_beam_boundaries.py +270 -0
  45. dkist_processing_cryonirsp/tasks/sp_dispersion_axis_correction.py +484 -0
  46. dkist_processing_cryonirsp/tasks/sp_geometric.py +585 -0
  47. dkist_processing_cryonirsp/tasks/sp_science.py +299 -0
  48. dkist_processing_cryonirsp/tasks/sp_solar_gain.py +475 -0
  49. dkist_processing_cryonirsp/tasks/trial_output_data.py +61 -0
  50. dkist_processing_cryonirsp/tasks/write_l1.py +1033 -0
  51. dkist_processing_cryonirsp/tests/__init__.py +1 -0
  52. dkist_processing_cryonirsp/tests/conftest.py +456 -0
  53. dkist_processing_cryonirsp/tests/header_models.py +592 -0
  54. dkist_processing_cryonirsp/tests/local_trial_workflows/__init__.py +0 -0
  55. dkist_processing_cryonirsp/tests/local_trial_workflows/l0_cals_only.py +541 -0
  56. dkist_processing_cryonirsp/tests/local_trial_workflows/l0_to_l1.py +615 -0
  57. dkist_processing_cryonirsp/tests/local_trial_workflows/linearize_only.py +96 -0
  58. dkist_processing_cryonirsp/tests/local_trial_workflows/local_trial_helpers.py +592 -0
  59. dkist_processing_cryonirsp/tests/test_assemble_movie.py +144 -0
  60. dkist_processing_cryonirsp/tests/test_assemble_qualilty.py +517 -0
  61. dkist_processing_cryonirsp/tests/test_bad_pixel_maps.py +115 -0
  62. dkist_processing_cryonirsp/tests/test_ci_beam_boundaries.py +106 -0
  63. dkist_processing_cryonirsp/tests/test_ci_science.py +355 -0
  64. dkist_processing_cryonirsp/tests/test_corrections.py +126 -0
  65. dkist_processing_cryonirsp/tests/test_cryo_base.py +202 -0
  66. dkist_processing_cryonirsp/tests/test_cryo_constants.py +76 -0
  67. dkist_processing_cryonirsp/tests/test_dark.py +287 -0
  68. dkist_processing_cryonirsp/tests/test_gain.py +278 -0
  69. dkist_processing_cryonirsp/tests/test_instrument_polarization.py +531 -0
  70. dkist_processing_cryonirsp/tests/test_linearity_correction.py +245 -0
  71. dkist_processing_cryonirsp/tests/test_make_movie_frames.py +111 -0
  72. dkist_processing_cryonirsp/tests/test_parameters.py +266 -0
  73. dkist_processing_cryonirsp/tests/test_parse.py +1439 -0
  74. dkist_processing_cryonirsp/tests/test_quality.py +203 -0
  75. dkist_processing_cryonirsp/tests/test_sp_beam_boundaries.py +112 -0
  76. dkist_processing_cryonirsp/tests/test_sp_dispersion_axis_correction.py +155 -0
  77. dkist_processing_cryonirsp/tests/test_sp_geometric.py +319 -0
  78. dkist_processing_cryonirsp/tests/test_sp_make_movie_frames.py +121 -0
  79. dkist_processing_cryonirsp/tests/test_sp_science.py +483 -0
  80. dkist_processing_cryonirsp/tests/test_sp_solar.py +198 -0
  81. dkist_processing_cryonirsp/tests/test_trial_create_quality_report.py +79 -0
  82. dkist_processing_cryonirsp/tests/test_trial_output_data.py +251 -0
  83. dkist_processing_cryonirsp/tests/test_workflows.py +9 -0
  84. dkist_processing_cryonirsp/tests/test_write_l1.py +436 -0
  85. dkist_processing_cryonirsp/workflows/__init__.py +2 -0
  86. dkist_processing_cryonirsp/workflows/ci_l0_processing.py +77 -0
  87. dkist_processing_cryonirsp/workflows/sp_l0_processing.py +84 -0
  88. dkist_processing_cryonirsp/workflows/trial_workflows.py +190 -0
  89. dkist_processing_cryonirsp-1.3.4.dist-info/METADATA +194 -0
  90. dkist_processing_cryonirsp-1.3.4.dist-info/RECORD +111 -0
  91. dkist_processing_cryonirsp-1.3.4.dist-info/WHEEL +5 -0
  92. dkist_processing_cryonirsp-1.3.4.dist-info/top_level.txt +4 -0
  93. docs/Makefile +134 -0
  94. docs/bad_pixel_calibration.rst +47 -0
  95. docs/beam_angle_calculation.rst +53 -0
  96. docs/beam_boundary_computation.rst +88 -0
  97. docs/changelog.rst +7 -0
  98. docs/ci_science_calibration.rst +33 -0
  99. docs/conf.py +52 -0
  100. docs/index.rst +21 -0
  101. docs/l0_to_l1_cryonirsp_ci-full-trial.rst +10 -0
  102. docs/l0_to_l1_cryonirsp_ci.rst +10 -0
  103. docs/l0_to_l1_cryonirsp_sp-full-trial.rst +10 -0
  104. docs/l0_to_l1_cryonirsp_sp.rst +10 -0
  105. docs/linearization.rst +43 -0
  106. docs/make.bat +170 -0
  107. docs/requirements.txt +1 -0
  108. docs/requirements_table.rst +8 -0
  109. docs/scientific_changelog.rst +10 -0
  110. docs/sp_science_calibration.rst +59 -0
  111. licenses/LICENSE.rst +11 -0
@@ -0,0 +1,270 @@
1
+ """Cryonirsp SP beam boundaries task."""
2
+ import math
3
+
4
+ import numpy as np
5
+ from dkist_service_configuration.logging import logger
6
+
7
+ from dkist_processing_cryonirsp.tasks.beam_boundaries_base import BeamBoundariesCalibrationBase
8
+ from dkist_processing_cryonirsp.tasks.beam_boundaries_base import BeamBoundary
9
+ from dkist_processing_cryonirsp.tasks.mixin.shift_measurements import ShiftMeasurementsMixin
10
+ from dkist_processing_cryonirsp.tasks.mixin.shift_measurements import SPATIAL
11
+ from dkist_processing_cryonirsp.tasks.mixin.shift_measurements import SPECTRAL
12
+
13
+ __all__ = ["SPBeamBoundariesCalibration"]
14
+
15
+
16
+ class SPBeamBoundariesCalibration(BeamBoundariesCalibrationBase, ShiftMeasurementsMixin):
17
+ """Task class for calculation of the SP beam boundaries for later use during calibration."""
18
+
19
+ def split_beams(self, array: np.ndarray) -> list[np.ndarray]:
20
+ """
21
+ Split the SP image into two beams, leaving out the predefined split boundary region.
22
+
23
+ Parameters
24
+ ----------
25
+ array
26
+ The input dual-beam SP array
27
+
28
+ Returns
29
+ -------
30
+ [beam_1, beam_2]
31
+ A list containing the split beam images.
32
+ """
33
+ self.split_boundary = self.compute_split_boundary(array)
34
+ beam_1 = array[:, : self.split_boundary[0]]
35
+ beam_2 = array[:, self.split_boundary[1] :]
36
+ return [beam_1, beam_2]
37
+
38
+ def compute_final_beam_boundaries(
39
+ self, arrays: list[np.ndarray], boundaries: list[BeamBoundary]
40
+ ):
41
+ """
42
+ Compute the final SP beam boundaries from the illuminated beams.
43
+
44
+ Parameters
45
+ ----------
46
+ arrays
47
+ A list of smoothed solar gain arrays, ready for beam identification
48
+ boundaries
49
+ A list of the illuminated region BeamBoundary objects for each beam [beam_1_boundaries, beam_2_boundaries]
50
+
51
+ Returns
52
+ -------
53
+ [beam_1_boundaries, beam_2_boundaries]
54
+ A list of final adjusted BeamBoundary objects, one for each beam
55
+ """
56
+ # Step 7.:
57
+ beam_1_array = arrays[0][boundaries[0].y_slice, boundaries[0].x_slice]
58
+ beam_2_array = arrays[1][boundaries[1].y_slice, boundaries[1].x_slice]
59
+
60
+ # Step 8:
61
+ spectral_shift, spatial_shift = self.compute_offset(beam_1_array, beam_2_array)
62
+
63
+ # Step 9:
64
+ new_beam_boundaries = self.compute_shared_boundaries_from_overlapping_beams(
65
+ boundaries, spectral_shift, spatial_shift
66
+ )
67
+ return new_beam_boundaries
68
+
69
+ def compute_split_boundary(self, average_solar_gain_array: np.ndarray) -> list[int]:
70
+ """
71
+ Compute the split boundary for an SP dual-beam image.
72
+
73
+ The split boundary is a transition region in the middle of the spectral axis between the two beams
74
+ and is not used for processing.
75
+
76
+ Parameters
77
+ ----------
78
+ average_solar_gain_array
79
+ An average solar gain array over the full ROI odf the detector
80
+
81
+ Returns
82
+ -------
83
+ list[region_start, region_end]
84
+ """
85
+ spectral_center = average_solar_gain_array.shape[1] // 2
86
+ transition_region_size = round(
87
+ average_solar_gain_array.shape[1]
88
+ * self.parameters.beam_boundaries_sp_beam_transition_region_size_fraction
89
+ )
90
+
91
+ split_boundary = [
92
+ spectral_center - transition_region_size,
93
+ spectral_center + transition_region_size,
94
+ ]
95
+ return split_boundary
96
+
97
+ def compute_offset(self, beam_1: np.ndarray, beam_2: np.ndarray) -> list[int]:
98
+ """
99
+ Higher-level helper function to compute the (x, y) offset between beams.
100
+
101
+ Parameters
102
+ ----------
103
+ beam_1
104
+ The beam_1 image array
105
+ beam_2
106
+ The beam_2 image array
107
+
108
+ Returns
109
+ -------
110
+ [x, y]
111
+ integer pixel offset between beams along eacj axis
112
+ """
113
+ raw_spatial_shift = self.shift_measurements_compute_shift_along_axis(
114
+ SPATIAL, beam_1, beam_2, upsample_factor=10
115
+ )
116
+ raw_spectral_shift = self.shift_measurements_compute_shift_along_axis(
117
+ SPECTRAL, beam_1, beam_2, upsample_factor=10
118
+ )
119
+ # Round these values to the nearest integer.
120
+ # When rounding up (to a larger abs value), round away from zero
121
+ spatial_shift = int(math.copysign(round(abs(raw_spatial_shift)), raw_spatial_shift))
122
+ spectral_shift = int(math.copysign(round(abs(raw_spectral_shift)), raw_spectral_shift))
123
+ shift = [spectral_shift, spatial_shift]
124
+ logger.info(f"Offset for beam 2 relative to beam 1, [SPECTRAL, SPATIAL] = {shift}")
125
+ return shift
126
+
127
+ def compute_shared_boundaries_from_overlapping_beams(
128
+ self,
129
+ boundaries: list[BeamBoundary],
130
+ spectral_shift: int,
131
+ spatial_shift: int,
132
+ ) -> list[BeamBoundary]:
133
+ """
134
+ Adjust the boundaries of each beam to align the beams based on the input shift parameters.
135
+
136
+ The shift values represent the measured shift of beam 2 relative to beam 1
137
+ A negative shift means that beam 2 is shifted to either to the left of, or below, beam 1, depending
138
+ on the particular axis involved, and must be shifted to the right, or up, to align the images.
139
+ Conversely, a positive shift means that beam 2 is shifted to the right of, or above, beam 1 and
140
+ must be shifted to the left, or down, to align the images. The images must also be cropped along
141
+ each axis as well, so that both images have the same dimensions and represent the same spectral and
142
+ spatial region.
143
+ Note: we are computing and shifting to integer pixel values here solely for the purpose of determining
144
+ the beam boundaries. We will compute sub-pixel adjustments later in the geometric task.
145
+
146
+ Parameters
147
+ ----------
148
+ boundaries
149
+ The boundaries of the illuminated regions of each beam.
150
+
151
+ spectral_shift
152
+ The shift required to align the two beam images along the spectral (horizontal or x) axis
153
+
154
+ spatial_shift
155
+ The shift required to align the two beam images along the spatial (vertical or y) axis
156
+
157
+ Returns
158
+ -------
159
+ [beam_1_boundaries, beam_2_boundaries]
160
+ A list of BeamBoundary objects representing the final beam boundaries
161
+ """
162
+ # These values are relative to the images AFTER they have been split, based on the illumination boundaries
163
+ (
164
+ beam_1_spatial_min,
165
+ beam_1_spatial_max,
166
+ beam_1_spectral_min,
167
+ beam_1_spectral_max,
168
+ ) = boundaries[0].beam_boundaries
169
+ (
170
+ beam_2_spatial_min,
171
+ beam_2_spatial_max,
172
+ beam_2_spectral_min,
173
+ beam_2_spectral_max,
174
+ ) = boundaries[1].beam_boundaries
175
+
176
+ # Now adjust these values to align the beams based on the shift parameters
177
+ (
178
+ beam_1_spectral_min_new,
179
+ beam_1_spectral_max_new,
180
+ beam_2_spectral_min_new,
181
+ beam_2_spectral_max_new,
182
+ ) = self.adjust_boundaries(
183
+ beam_1_spectral_min,
184
+ beam_1_spectral_max,
185
+ beam_2_spectral_min,
186
+ beam_2_spectral_max,
187
+ spectral_shift,
188
+ )
189
+ (
190
+ beam_1_spatial_min_new,
191
+ beam_1_spatial_max_new,
192
+ beam_2_spatial_min_new,
193
+ beam_2_spatial_max_new,
194
+ ) = self.adjust_boundaries(
195
+ beam_1_spatial_min,
196
+ beam_1_spatial_max,
197
+ beam_2_spatial_min,
198
+ beam_2_spatial_max,
199
+ spatial_shift,
200
+ )
201
+
202
+ # Now the beams are aligned, and we must translate the boundaries back to
203
+ # the original dual beam image. This impacts only the beam 2 spectral boundaries
204
+ beam_2_spectral_min_new += self.split_boundary[1]
205
+ beam_2_spectral_max_new += self.split_boundary[1]
206
+
207
+ beam_1_boundaries = BeamBoundary(
208
+ beam_1_spatial_min_new,
209
+ beam_1_spatial_max_new,
210
+ beam_1_spectral_min_new,
211
+ beam_1_spectral_max_new,
212
+ )
213
+ beam_2_boundaries = BeamBoundary(
214
+ beam_2_spatial_min_new,
215
+ beam_2_spatial_max_new,
216
+ beam_2_spectral_min_new,
217
+ beam_2_spectral_max_new,
218
+ )
219
+
220
+ logger.info(f"{beam_1_boundaries = }")
221
+ logger.info(f"{beam_2_boundaries = }")
222
+
223
+ # NB: The upper bounds are exclusive, ready to be used in array slicing
224
+ return [beam_1_boundaries, beam_2_boundaries]
225
+
226
+ @staticmethod
227
+ def adjust_boundaries(
228
+ b1_curr_min, b1_curr_max, b2_curr_min, b2_curr_max: int, shift: int
229
+ ) -> tuple[int, ...]:
230
+ """
231
+ Adjust the beam boundaries along an axis based on the shift required to align them.
232
+
233
+ The resulting boundaries will have the same dimensions (max - min) after the adjustment.
234
+
235
+ Parameters
236
+ ----------
237
+ b1_curr_min
238
+ The current minimum index for beam 1 on this axis
239
+ b1_curr_max
240
+ The current maximum index for beam 1 on this axis
241
+ b2_curr_min
242
+ The current minimum index for beam 2 on this axis
243
+ b2_curr_max
244
+ The current maximum index for beam 2 on this axis
245
+ shift
246
+ The amount beam 2 must be shifted to align to beam 1 on this axis
247
+ Returns
248
+ -------
249
+ A tuple containing the adjusted boundaries for this axis.
250
+
251
+ """
252
+ if shift < 0:
253
+ beam_1_new_min = b1_curr_min - shift
254
+ beam_2_new_min = b2_curr_min
255
+ beam_1_new_max = b1_curr_max
256
+ beam_2_new_max = b2_curr_max + shift
257
+
258
+ else:
259
+ beam_1_new_min = b1_curr_min
260
+ beam_2_new_min = b2_curr_min + shift
261
+ beam_1_new_max = b1_curr_max - shift
262
+ beam_2_new_max = b2_curr_max
263
+
264
+ # Now trim where needed to make both the same size
265
+ beam_1_extent = beam_1_new_max - beam_1_new_min
266
+ beam_2_extent = beam_2_new_max - beam_2_new_min
267
+ new_extent = min(beam_1_extent, beam_2_extent)
268
+ beam_1_new_max = beam_1_new_min + new_extent
269
+ beam_2_new_max = beam_2_new_min + new_extent
270
+ return beam_1_new_min, beam_1_new_max, beam_2_new_min, beam_2_new_max