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.
- changelog/.gitempty +0 -0
- dkist_processing_cryonirsp/__init__.py +11 -0
- dkist_processing_cryonirsp/config.py +12 -0
- dkist_processing_cryonirsp/models/__init__.py +1 -0
- dkist_processing_cryonirsp/models/constants.py +248 -0
- dkist_processing_cryonirsp/models/exposure_conditions.py +26 -0
- dkist_processing_cryonirsp/models/parameters.py +296 -0
- dkist_processing_cryonirsp/models/tags.py +168 -0
- dkist_processing_cryonirsp/models/task_name.py +14 -0
- dkist_processing_cryonirsp/parsers/__init__.py +1 -0
- dkist_processing_cryonirsp/parsers/cryonirsp_l0_fits_access.py +111 -0
- dkist_processing_cryonirsp/parsers/cryonirsp_l1_fits_access.py +30 -0
- dkist_processing_cryonirsp/parsers/exposure_conditions.py +163 -0
- dkist_processing_cryonirsp/parsers/map_repeats.py +40 -0
- dkist_processing_cryonirsp/parsers/measurements.py +55 -0
- dkist_processing_cryonirsp/parsers/modstates.py +31 -0
- dkist_processing_cryonirsp/parsers/optical_density_filters.py +40 -0
- dkist_processing_cryonirsp/parsers/polarimetric_check.py +120 -0
- dkist_processing_cryonirsp/parsers/scan_step.py +412 -0
- dkist_processing_cryonirsp/parsers/time.py +80 -0
- dkist_processing_cryonirsp/parsers/wavelength.py +26 -0
- dkist_processing_cryonirsp/tasks/__init__.py +19 -0
- dkist_processing_cryonirsp/tasks/assemble_movie.py +202 -0
- dkist_processing_cryonirsp/tasks/bad_pixel_map.py +96 -0
- dkist_processing_cryonirsp/tasks/beam_boundaries_base.py +279 -0
- dkist_processing_cryonirsp/tasks/ci_beam_boundaries.py +55 -0
- dkist_processing_cryonirsp/tasks/ci_science.py +169 -0
- dkist_processing_cryonirsp/tasks/cryonirsp_base.py +67 -0
- dkist_processing_cryonirsp/tasks/dark.py +98 -0
- dkist_processing_cryonirsp/tasks/gain.py +251 -0
- dkist_processing_cryonirsp/tasks/instrument_polarization.py +447 -0
- dkist_processing_cryonirsp/tasks/l1_output_data.py +44 -0
- dkist_processing_cryonirsp/tasks/linearity_correction.py +582 -0
- dkist_processing_cryonirsp/tasks/make_movie_frames.py +302 -0
- dkist_processing_cryonirsp/tasks/mixin/__init__.py +1 -0
- dkist_processing_cryonirsp/tasks/mixin/beam_access.py +52 -0
- dkist_processing_cryonirsp/tasks/mixin/corrections.py +177 -0
- dkist_processing_cryonirsp/tasks/mixin/intermediate_frame.py +193 -0
- dkist_processing_cryonirsp/tasks/mixin/linearized_frame.py +309 -0
- dkist_processing_cryonirsp/tasks/mixin/shift_measurements.py +297 -0
- dkist_processing_cryonirsp/tasks/parse.py +281 -0
- dkist_processing_cryonirsp/tasks/quality_metrics.py +271 -0
- dkist_processing_cryonirsp/tasks/science_base.py +511 -0
- dkist_processing_cryonirsp/tasks/sp_beam_boundaries.py +270 -0
- dkist_processing_cryonirsp/tasks/sp_dispersion_axis_correction.py +484 -0
- dkist_processing_cryonirsp/tasks/sp_geometric.py +585 -0
- dkist_processing_cryonirsp/tasks/sp_science.py +299 -0
- dkist_processing_cryonirsp/tasks/sp_solar_gain.py +475 -0
- dkist_processing_cryonirsp/tasks/trial_output_data.py +61 -0
- dkist_processing_cryonirsp/tasks/write_l1.py +1033 -0
- dkist_processing_cryonirsp/tests/__init__.py +1 -0
- dkist_processing_cryonirsp/tests/conftest.py +456 -0
- dkist_processing_cryonirsp/tests/header_models.py +592 -0
- dkist_processing_cryonirsp/tests/local_trial_workflows/__init__.py +0 -0
- dkist_processing_cryonirsp/tests/local_trial_workflows/l0_cals_only.py +541 -0
- dkist_processing_cryonirsp/tests/local_trial_workflows/l0_to_l1.py +615 -0
- dkist_processing_cryonirsp/tests/local_trial_workflows/linearize_only.py +96 -0
- dkist_processing_cryonirsp/tests/local_trial_workflows/local_trial_helpers.py +592 -0
- dkist_processing_cryonirsp/tests/test_assemble_movie.py +144 -0
- dkist_processing_cryonirsp/tests/test_assemble_qualilty.py +517 -0
- dkist_processing_cryonirsp/tests/test_bad_pixel_maps.py +115 -0
- dkist_processing_cryonirsp/tests/test_ci_beam_boundaries.py +106 -0
- dkist_processing_cryonirsp/tests/test_ci_science.py +355 -0
- dkist_processing_cryonirsp/tests/test_corrections.py +126 -0
- dkist_processing_cryonirsp/tests/test_cryo_base.py +202 -0
- dkist_processing_cryonirsp/tests/test_cryo_constants.py +76 -0
- dkist_processing_cryonirsp/tests/test_dark.py +287 -0
- dkist_processing_cryonirsp/tests/test_gain.py +278 -0
- dkist_processing_cryonirsp/tests/test_instrument_polarization.py +531 -0
- dkist_processing_cryonirsp/tests/test_linearity_correction.py +245 -0
- dkist_processing_cryonirsp/tests/test_make_movie_frames.py +111 -0
- dkist_processing_cryonirsp/tests/test_parameters.py +266 -0
- dkist_processing_cryonirsp/tests/test_parse.py +1439 -0
- dkist_processing_cryonirsp/tests/test_quality.py +203 -0
- dkist_processing_cryonirsp/tests/test_sp_beam_boundaries.py +112 -0
- dkist_processing_cryonirsp/tests/test_sp_dispersion_axis_correction.py +155 -0
- dkist_processing_cryonirsp/tests/test_sp_geometric.py +319 -0
- dkist_processing_cryonirsp/tests/test_sp_make_movie_frames.py +121 -0
- dkist_processing_cryonirsp/tests/test_sp_science.py +483 -0
- dkist_processing_cryonirsp/tests/test_sp_solar.py +198 -0
- dkist_processing_cryonirsp/tests/test_trial_create_quality_report.py +79 -0
- dkist_processing_cryonirsp/tests/test_trial_output_data.py +251 -0
- dkist_processing_cryonirsp/tests/test_workflows.py +9 -0
- dkist_processing_cryonirsp/tests/test_write_l1.py +436 -0
- dkist_processing_cryonirsp/workflows/__init__.py +2 -0
- dkist_processing_cryonirsp/workflows/ci_l0_processing.py +77 -0
- dkist_processing_cryonirsp/workflows/sp_l0_processing.py +84 -0
- dkist_processing_cryonirsp/workflows/trial_workflows.py +190 -0
- dkist_processing_cryonirsp-1.3.4.dist-info/METADATA +194 -0
- dkist_processing_cryonirsp-1.3.4.dist-info/RECORD +111 -0
- dkist_processing_cryonirsp-1.3.4.dist-info/WHEEL +5 -0
- dkist_processing_cryonirsp-1.3.4.dist-info/top_level.txt +4 -0
- docs/Makefile +134 -0
- docs/bad_pixel_calibration.rst +47 -0
- docs/beam_angle_calculation.rst +53 -0
- docs/beam_boundary_computation.rst +88 -0
- docs/changelog.rst +7 -0
- docs/ci_science_calibration.rst +33 -0
- docs/conf.py +52 -0
- docs/index.rst +21 -0
- docs/l0_to_l1_cryonirsp_ci-full-trial.rst +10 -0
- docs/l0_to_l1_cryonirsp_ci.rst +10 -0
- docs/l0_to_l1_cryonirsp_sp-full-trial.rst +10 -0
- docs/l0_to_l1_cryonirsp_sp.rst +10 -0
- docs/linearization.rst +43 -0
- docs/make.bat +170 -0
- docs/requirements.txt +1 -0
- docs/requirements_table.rst +8 -0
- docs/scientific_changelog.rst +10 -0
- docs/sp_science_calibration.rst +59 -0
- 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
|