dkist-processing-cryonirsp 1.4.15__py3-none-any.whl → 1.4.16rc1__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 (53) hide show
  1. changelog/170.feature.rst +11 -0
  2. changelog/170.misc.1.rst +2 -0
  3. changelog/170.misc.rst +3 -0
  4. changelog/174.bugfix.rst +1 -0
  5. dkist_processing_cryonirsp/codecs/__init__.py +5 -0
  6. dkist_processing_cryonirsp/codecs/fits.py +52 -0
  7. dkist_processing_cryonirsp/models/beam_boundaries.py +39 -0
  8. dkist_processing_cryonirsp/models/parameters.py +0 -1
  9. dkist_processing_cryonirsp/models/tags.py +34 -0
  10. dkist_processing_cryonirsp/parsers/cryonirsp_l0_fits_access.py +38 -2
  11. dkist_processing_cryonirsp/tasks/assemble_movie.py +2 -2
  12. dkist_processing_cryonirsp/tasks/bad_pixel_map.py +14 -9
  13. dkist_processing_cryonirsp/tasks/beam_boundaries_base.py +24 -43
  14. dkist_processing_cryonirsp/tasks/ci_beam_boundaries.py +1 -1
  15. dkist_processing_cryonirsp/tasks/ci_science.py +24 -6
  16. dkist_processing_cryonirsp/tasks/cryonirsp_base.py +0 -10
  17. dkist_processing_cryonirsp/tasks/dark.py +34 -14
  18. dkist_processing_cryonirsp/tasks/gain.py +69 -22
  19. dkist_processing_cryonirsp/tasks/instrument_polarization.py +131 -49
  20. dkist_processing_cryonirsp/tasks/l1_output_data.py +0 -1
  21. dkist_processing_cryonirsp/tasks/linearity_correction.py +4 -7
  22. dkist_processing_cryonirsp/tasks/make_movie_frames.py +5 -5
  23. dkist_processing_cryonirsp/tasks/quality_metrics.py +4 -4
  24. dkist_processing_cryonirsp/tasks/science_base.py +34 -10
  25. dkist_processing_cryonirsp/tasks/sp_beam_boundaries.py +1 -1
  26. dkist_processing_cryonirsp/tasks/sp_dispersion_axis_correction.py +14 -6
  27. dkist_processing_cryonirsp/tasks/sp_geometric.py +112 -39
  28. dkist_processing_cryonirsp/tasks/sp_science.py +53 -11
  29. dkist_processing_cryonirsp/tasks/sp_solar_gain.py +108 -29
  30. dkist_processing_cryonirsp/tests/local_trial_workflows/l0_cals_only.py +2 -10
  31. dkist_processing_cryonirsp/tests/local_trial_workflows/l0_to_l1.py +8 -11
  32. dkist_processing_cryonirsp/tests/local_trial_workflows/local_trial_helpers.py +1 -1
  33. dkist_processing_cryonirsp/tests/test_bad_pixel_maps.py +1 -2
  34. dkist_processing_cryonirsp/tests/test_ci_beam_boundaries.py +6 -5
  35. dkist_processing_cryonirsp/tests/test_ci_science.py +25 -24
  36. dkist_processing_cryonirsp/tests/test_cryo_base.py +41 -43
  37. dkist_processing_cryonirsp/tests/test_dark.py +20 -28
  38. dkist_processing_cryonirsp/tests/test_gain.py +46 -35
  39. dkist_processing_cryonirsp/tests/test_instrument_polarization.py +22 -16
  40. dkist_processing_cryonirsp/tests/test_linearity_correction.py +1 -4
  41. dkist_processing_cryonirsp/tests/test_quality.py +1 -2
  42. dkist_processing_cryonirsp/tests/test_sp_beam_boundaries.py +6 -5
  43. dkist_processing_cryonirsp/tests/test_sp_dispersion_axis_correction.py +10 -9
  44. dkist_processing_cryonirsp/tests/test_sp_geometric.py +108 -53
  45. dkist_processing_cryonirsp/tests/test_sp_science.py +49 -35
  46. dkist_processing_cryonirsp/tests/test_sp_solar.py +70 -38
  47. {dkist_processing_cryonirsp-1.4.15.dist-info → dkist_processing_cryonirsp-1.4.16rc1.dist-info}/METADATA +2 -2
  48. {dkist_processing_cryonirsp-1.4.15.dist-info → dkist_processing_cryonirsp-1.4.16rc1.dist-info}/RECORD +50 -46
  49. dkist_processing_cryonirsp/tasks/mixin/beam_access.py +0 -52
  50. dkist_processing_cryonirsp/tasks/mixin/intermediate_frame.py +0 -193
  51. dkist_processing_cryonirsp/tasks/mixin/linearized_frame.py +0 -309
  52. {dkist_processing_cryonirsp-1.4.15.dist-info → dkist_processing_cryonirsp-1.4.16rc1.dist-info}/WHEEL +0 -0
  53. {dkist_processing_cryonirsp-1.4.15.dist-info → dkist_processing_cryonirsp-1.4.16rc1.dist-info}/top_level.txt +0 -0
@@ -1,193 +0,0 @@
1
- """Mixin class providing support for loading and writing intermediate arrays."""
2
- from collections.abc import Generator
3
- from collections.abc import Iterable
4
-
5
- import numpy as np
6
- from astropy.io import fits
7
- from dkist_processing_common.codecs.fits import fits_hdu_decoder
8
- from dkist_processing_common.codecs.fits import fits_hdulist_encoder
9
- from dkist_service_configuration.logging import logger
10
-
11
- from dkist_processing_cryonirsp.models.exposure_conditions import ExposureConditions
12
- from dkist_processing_cryonirsp.models.tags import CryonirspTag
13
-
14
-
15
- class IntermediateFrameMixin:
16
- """Mixin for methods that support easy loading and writing of intermediate frames."""
17
-
18
- def intermediate_frame_load_intermediate_arrays(
19
- self,
20
- tags: [str],
21
- ) -> Generator[np.ndarray, None, None]:
22
- """Yield a generator that produces ndarrays for the requested tags."""
23
- tags = list(set([CryonirspTag.intermediate(), CryonirspTag.frame()] + tags))
24
-
25
- if self.scratch.count_all(tags=tags) == 0:
26
- raise RuntimeError(f"No files found matching {tags =}")
27
- for hdu in self.read(
28
- tags=tags,
29
- decoder=fits_hdu_decoder,
30
- ):
31
- yield hdu.data
32
-
33
- def intermediate_frame_load_beam_boundaries(self, beam: int) -> np.ndarray:
34
- """Return array containing beam boundaries for a given beam."""
35
- tags = [CryonirspTag.task_beam_boundaries(), CryonirspTag.beam(beam)]
36
- return next(self.intermediate_frame_load_intermediate_arrays(tags=tags))
37
-
38
- def intermediate_frame_load_full_bad_pixel_map(self) -> np.ndarray:
39
- """Return the full array version of the bad-pixel map."""
40
- tags = [CryonirspTag.task_bad_pixel_map()]
41
- bad_pixel_map = next(self.intermediate_frame_load_intermediate_arrays(tags=tags))
42
- return bad_pixel_map
43
-
44
- def intermediate_frame_load_bad_pixel_map(self, beam: int) -> np.ndarray:
45
- """Return bad-pixel map for given beam."""
46
- bad_pixel_map = self.intermediate_frame_load_full_bad_pixel_map()
47
- return self.beam_access_get_beam(bad_pixel_map, beam)
48
-
49
- def intermediate_frame_load_dark_array(
50
- self,
51
- exposure_conditions: ExposureConditions,
52
- beam: int,
53
- ) -> np.ndarray:
54
- """Load an existing dark array."""
55
- tags = [
56
- CryonirspTag.task_dark(),
57
- CryonirspTag.exposure_conditions(exposure_conditions),
58
- CryonirspTag.beam(beam),
59
- ]
60
- return next(self.intermediate_frame_load_intermediate_arrays(tags=tags))
61
-
62
- def intermediate_frame_load_polcal_dark_array(
63
- self,
64
- beam: int,
65
- exposure_conditions: ExposureConditions,
66
- ) -> np.ndarray:
67
- """Load an existing polcal dark array."""
68
- tags = [
69
- CryonirspTag.task_polcal_dark(),
70
- CryonirspTag.exposure_conditions(exposure_conditions),
71
- CryonirspTag.beam(beam),
72
- ]
73
- return next(self.intermediate_frame_load_intermediate_arrays(tags=tags))
74
-
75
- def intermediate_frame_load_solar_gain_array(self, beam: int) -> np.ndarray:
76
- """Load an existing solar gain array."""
77
- tags = [CryonirspTag.task_solar_gain(), CryonirspTag.beam(beam)]
78
- return next(self.intermediate_frame_load_intermediate_arrays(tags=tags))
79
-
80
- def intermediate_frame_load_lamp_gain_array(self, beam: int) -> np.ndarray:
81
- """Load an existing lamp gain array."""
82
- tags = [CryonirspTag.task_lamp_gain(), CryonirspTag.beam(beam)]
83
- return next(self.intermediate_frame_load_intermediate_arrays(tags=tags))
84
-
85
- def intermediate_frame_load_polcal_gain_array(
86
- self,
87
- beam: int,
88
- exposure_conditions: ExposureConditions,
89
- ) -> np.ndarray:
90
- """Load an existing polcal gain array."""
91
- tags = [
92
- CryonirspTag.task_polcal_gain(),
93
- CryonirspTag.exposure_conditions(exposure_conditions),
94
- CryonirspTag.beam(beam),
95
- ]
96
- return next(self.intermediate_frame_load_intermediate_arrays(tags=tags))
97
-
98
- def intermediate_frame_load_demod_matrices(
99
- self,
100
- beam: int,
101
- ) -> np.ndarray:
102
- """Load existing demodulation matrices."""
103
- tags = [CryonirspTag.task_demodulation_matrices(), CryonirspTag.beam(beam)]
104
- return next(self.intermediate_frame_load_intermediate_arrays(tags=tags))
105
-
106
- def intermediate_frame_load_angle(self, beam: int) -> float:
107
- """Return the geometric correction angle for a given beam."""
108
- tags = [CryonirspTag.task_geometric_angle(), CryonirspTag.beam(beam)]
109
- angle_array = next(self.intermediate_frame_load_intermediate_arrays(tags=tags))
110
- return float(angle_array[0])
111
-
112
- def intermediate_frame_load_state_offset(
113
- self,
114
- beam: int,
115
- ) -> np.ndarray:
116
- """Return state offset shifts for a given beam."""
117
- tags = [CryonirspTag.task_geometric_offset(), CryonirspTag.beam(beam)]
118
- offset = next(self.intermediate_frame_load_intermediate_arrays(tags=tags))
119
- return offset
120
-
121
- def intermediate_frame_load_spec_shift(self, beam: int) -> np.ndarray:
122
- """Return spectral shifts for a given beam."""
123
- tags = [CryonirspTag.task_geometric_sepectral_shifts(), CryonirspTag.beam(beam)]
124
- shifts = next(self.intermediate_frame_load_intermediate_arrays(tags=tags))
125
- return shifts
126
-
127
- def intermediate_frame_write_arrays(
128
- self,
129
- arrays: Iterable[np.ndarray] | np.ndarray,
130
- task_tag: str | None = None,
131
- task: str | None = None,
132
- headers: Iterable[fits.Header] | fits.Header | None = None,
133
- beam: int | None = None,
134
- modstate: int | None = None,
135
- map_scan: int | None = None,
136
- scan_step: int | None = None,
137
- exposure_time: float | None = None,
138
- exposure_conditions: ExposureConditions | None = None,
139
- meas_num: int | None = None,
140
- cs_step: int | None = None,
141
- ) -> None:
142
- """Write an intermediate fits files given a list of input arrays and headers."""
143
- if task_tag is not None and task is not None:
144
- raise ValueError("Cannot specify both the raw 'task' and a formatted 'task_tag'.")
145
- if task_tag is None and task is None:
146
- raise ValueError("Must specify exactly one of raw 'task' or formatted 'task_tag'.")
147
-
148
- if task is not None:
149
- task_tag = CryonirspTag.task(task)
150
- tags = [CryonirspTag.intermediate(), CryonirspTag.frame(), task_tag]
151
- for arg, tag_func in zip(
152
- [
153
- beam,
154
- modstate,
155
- map_scan,
156
- scan_step,
157
- exposure_time,
158
- meas_num,
159
- cs_step,
160
- exposure_conditions,
161
- ],
162
- [
163
- CryonirspTag.beam,
164
- CryonirspTag.modstate,
165
- CryonirspTag.map_scan,
166
- CryonirspTag.scan_step,
167
- CryonirspTag.exposure_time,
168
- CryonirspTag.meas_num,
169
- CryonirspTag.cs_step,
170
- CryonirspTag.exposure_conditions,
171
- ],
172
- ):
173
- if arg is not None:
174
- tags.append(tag_func(arg))
175
-
176
- arrays = [arrays] if isinstance(arrays, np.ndarray) else arrays
177
- if headers is not None:
178
- headers = [headers] if isinstance(headers, fits.Header) else headers
179
- else:
180
- headers = [None] * len(list(arrays))
181
-
182
- filenames = []
183
- for array, header in zip(arrays, headers):
184
- hdul = fits.HDUList([fits.PrimaryHDU(data=array, header=header)])
185
- path = str(
186
- self.write(
187
- data=hdul,
188
- tags=tags,
189
- encoder=fits_hdulist_encoder,
190
- )
191
- )
192
- filenames.append(path)
193
- logger.info(f"Wrote intermediate file(s) for {tags = } to {filenames}")
@@ -1,309 +0,0 @@
1
- """Helper to manage input data."""
2
- from collections.abc import Generator
3
-
4
- import numpy as np
5
- from astropy.io import fits
6
- from dkist_processing_common.codecs.fits import fits_access_decoder
7
- from dkist_processing_common.models.fits_access import FitsAccessBase
8
- from dkist_processing_common.models.task_name import TaskName
9
-
10
- from dkist_processing_cryonirsp.models.exposure_conditions import ExposureConditions
11
- from dkist_processing_cryonirsp.models.tags import CryonirspTag
12
- from dkist_processing_cryonirsp.parsers.cryonirsp_l0_fits_access import CryonirspL0FitsAccess
13
-
14
-
15
- class LinearizedFrameMixin:
16
- """
17
- Mixin for methods that support easy loading of linearity corrected frames.
18
-
19
- Throughout this class wse assume that if beam is None, then it means to return the entire array.
20
- """
21
-
22
- def linearized_frame_fits_access_generator(
23
- self,
24
- tags: [str],
25
- beam: int,
26
- ) -> Generator[CryonirspL0FitsAccess, None, None]:
27
- """Load linearized fits frames based on given tags and extract the desired beam."""
28
- tags = list(set([CryonirspTag.linearized(), CryonirspTag.frame()] + tags))
29
-
30
- full_frame_generator = self.read(
31
- tags=tags,
32
- decoder=fits_access_decoder,
33
- fits_access_class=CryonirspL0FitsAccess,
34
- )
35
- for item in full_frame_generator:
36
- header = item.header
37
- flipped_data = self.linearized_frame_flip_dispersion_axis(item)
38
- data = self.beam_access_get_beam(array=flipped_data, beam=beam)
39
- hdu = fits.PrimaryHDU(header=header, data=data)
40
- yield CryonirspL0FitsAccess(hdu=hdu)
41
-
42
- def linearized_frame_full_array_generator(
43
- self,
44
- tags: [str],
45
- ) -> Generator[np.ndarray, None, None]:
46
- """Load linearized fits frames based on given tags."""
47
- tags = list(set([CryonirspTag.linearized(), CryonirspTag.frame()] + tags))
48
- frame_generator = self.read(
49
- tags=tags,
50
- decoder=fits_access_decoder,
51
- fits_access_class=CryonirspL0FitsAccess,
52
- )
53
- for frame in frame_generator:
54
- yield self.linearized_frame_flip_dispersion_axis(frame)
55
-
56
- def linearized_frame_dark_array_generator(
57
- self, exposure_conditions: ExposureConditions, beam: int
58
- ) -> Generator[np.ndarray, None, None]:
59
- """
60
- Return a fits access generator with dark frames for the given parameters.
61
-
62
- Parameters
63
- ----------
64
- exposure_conditions
65
- The current exposure conditions
66
- modstate
67
- The current modulator state
68
- beam
69
- The current beam number
70
-
71
- Returns
72
- -------
73
- A fits access generator based on the inputs provided
74
- """
75
- tags = [CryonirspTag.task_dark(), CryonirspTag.exposure_conditions(exposure_conditions)]
76
- dark_array_fits_access = self.linearized_frame_fits_access_generator(tags=tags, beam=beam)
77
- for array in dark_array_fits_access:
78
- yield array.data
79
-
80
- def linearized_frame_polcal_dark_array_generator(
81
- self,
82
- exposure_conditions: ExposureConditions,
83
- beam: int,
84
- ) -> Generator[np.ndarray, None, None]:
85
- """
86
- Return a fits access generator with polcal dark frames based on the given parameters.
87
-
88
- Parameters
89
- ----------
90
- beam
91
- The beam from which to return data
92
- exposure_conditions
93
- The current exposure conditions
94
-
95
- Returns
96
- -------
97
- A fits access generator based on the inputs provided
98
- """
99
- tags = [
100
- CryonirspTag.task_polcal_dark(),
101
- CryonirspTag.exposure_conditions(exposure_conditions),
102
- ]
103
- polcal_dark_array_fits_access = self.linearized_frame_fits_access_generator(
104
- tags=tags,
105
- beam=beam,
106
- )
107
- for array in polcal_dark_array_fits_access:
108
- yield array.data
109
-
110
- def linearized_frame_gain_array_generator(
111
- self,
112
- gain_type: str,
113
- beam: int,
114
- exposure_conditions: ExposureConditions | None = None,
115
- ) -> Generator[np.ndarray, None, None]:
116
- """
117
- Return a fits access generator with gain frames based on the parameters provided.
118
-
119
- Parameters
120
- ----------
121
- gain_type
122
- The gain type
123
- exposure_conditions
124
- The current exposure conditions
125
- beam
126
- The current beam number
127
-
128
- Returns
129
- -------
130
- A fits access generator based on the inputs provided
131
- """
132
- tags = [CryonirspTag.task(gain_type)]
133
- if exposure_conditions is not None:
134
- tags.append(CryonirspTag.exposure_conditions(exposure_conditions))
135
- gain_array_fits_access = self.linearized_frame_fits_access_generator(
136
- tags=tags,
137
- beam=beam,
138
- )
139
- for array in gain_array_fits_access:
140
- yield array.data
141
-
142
- def linearized_frame_lamp_gain_array_generator(
143
- self,
144
- beam: int,
145
- exposure_conditions: float | None = None,
146
- ) -> Generator[np.ndarray, None, None]:
147
- """
148
- Return a fits access generator with lamp gain frames based on the parameters provided.
149
-
150
- Parameters
151
- ----------
152
- exposure_conditions
153
- The current exposure conditions
154
- beam
155
- The current beam number
156
-
157
- Returns
158
- -------
159
- A fits access generator based on the inputs provided
160
- """
161
- return self.linearized_frame_gain_array_generator(
162
- gain_type=TaskName.lamp_gain.value, beam=beam, exposure_conditions=exposure_conditions
163
- )
164
-
165
- def linearized_frame_solar_gain_array_generator(
166
- self,
167
- beam: int,
168
- exposure_conditions: float | None = None,
169
- ) -> Generator[np.ndarray, None, None]:
170
- """
171
- Return a fits access generator with solar gain frames based on the parameters provided.
172
-
173
- Parameters
174
- ----------
175
- exposure_conditions
176
- The current exposure conditions
177
- beam
178
- The current beam number
179
-
180
- Returns
181
- -------
182
- A fits access generator based on the inputs provided
183
- """
184
- return self.linearized_frame_gain_array_generator(
185
- gain_type=TaskName.solar_gain.value, beam=beam, exposure_conditions=exposure_conditions
186
- )
187
-
188
- def linearized_frame_polcal_gain_array_generator(
189
- self,
190
- exposure_conditions: ExposureConditions,
191
- beam: int,
192
- ) -> Generator[np.ndarray, None, None]:
193
- """
194
- Return a fits access generator with polcal gain frames based on the given parameters.
195
-
196
- Parameters
197
- ----------
198
- beam
199
- The beam from which to return data
200
- exposure_conditions
201
- The current exposure conditions
202
-
203
- Returns
204
- -------
205
- A fits access generator based on the inputs provided
206
- """
207
- tags = [
208
- CryonirspTag.task_polcal_gain(),
209
- CryonirspTag.exposure_conditions(exposure_conditions),
210
- ]
211
- polcal_gain_array_fits_access = self.linearized_frame_fits_access_generator(
212
- tags=tags,
213
- beam=beam,
214
- )
215
- for array in polcal_gain_array_fits_access:
216
- yield array.data
217
-
218
- def linearized_frame_observe_fits_access_generator(
219
- self,
220
- modstate: int,
221
- meas_num: int,
222
- scan_step: int,
223
- map_scan: int,
224
- exposure_conditions: ExposureConditions,
225
- beam: int,
226
- ) -> Generator[FitsAccessBase, None, None]:
227
- """
228
- Return a fits access generator of observe frames based on the parameters provided.
229
-
230
- Parameters
231
- ----------
232
- beam
233
- The beam from which to return data
234
- modstate
235
- The current modulator state
236
- meas_num
237
- The current measurement number
238
- scan_step
239
- The current scan step
240
- map_scan
241
- The current map_scan number
242
- exposure_conditions
243
- The current exposure conditions
244
-
245
- Returns
246
- -------
247
- A fits access generator based on the inputs provided
248
- """
249
- tags = [
250
- CryonirspTag.task_observe(),
251
- CryonirspTag.modstate(modstate),
252
- CryonirspTag.meas_num(meas_num),
253
- CryonirspTag.scan_step(scan_step),
254
- CryonirspTag.map_scan(map_scan),
255
- CryonirspTag.exposure_conditions(exposure_conditions),
256
- ]
257
- return self.linearized_frame_fits_access_generator(
258
- tags=tags,
259
- beam=beam,
260
- )
261
-
262
- def linearized_frame_polcal_fits_access_generator(
263
- self,
264
- modstate: int,
265
- cs_step: int,
266
- exposure_conditions: ExposureConditions,
267
- beam: int,
268
- ) -> Generator[FitsAccessBase, None, None]:
269
- """
270
- Return a fits access generator of polcal frames based on the parameters provided.
271
-
272
- Parameters
273
- ----------
274
- modstate
275
- The current modulator state
276
- cs_step
277
- The current cs_step number
278
- exposure_conditions
279
- The current exposure conditions
280
- beam
281
- The current beam number
282
-
283
- Returns
284
- -------
285
- A fits access generator based on the inputs provided
286
- """
287
- tags = [
288
- CryonirspTag.task_polcal(),
289
- CryonirspTag.modstate(modstate),
290
- CryonirspTag.cs_step(cs_step),
291
- CryonirspTag.exposure_conditions(exposure_conditions),
292
- ]
293
- return self.linearized_frame_fits_access_generator(
294
- tags=tags,
295
- beam=beam,
296
- )
297
-
298
- @staticmethod
299
- def linearized_frame_flip_dispersion_axis(frame: CryonirspL0FitsAccess) -> np.ndarray:
300
- """
301
- Flip dispersion axis. Cryo's wavelength decreases from left to right, so we flip it here to match the other instruments.
302
-
303
- :param frame: frame to be flipped
304
- :return: flipped array (SP), array (CI)
305
- """
306
- if frame.arm_id == "SP":
307
- return np.flip(frame.data, 1)
308
- elif frame.arm_id == "CI":
309
- return frame.data