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,531 @@
1
+ from datetime import datetime
2
+ from unittest.mock import ANY
3
+ from unittest.mock import patch
4
+
5
+ import numpy as np
6
+ import pytest
7
+ from astropy.io import fits
8
+ from dkist_header_validator import spec122_validator
9
+ from dkist_processing_common._util.scratch import WorkflowFileSystem
10
+ from dkist_processing_common.codecs.fits import fits_hdulist_encoder
11
+ from dkist_processing_common.models.task_name import TaskName
12
+ from dkist_processing_common.tests.conftest import FakeGQLClient
13
+ from dkist_processing_pac.fitter.polcal_fitter import PolcalFitter
14
+ from dkist_processing_pac.input_data.dresser import Dresser
15
+
16
+ from dkist_processing_cryonirsp.models.exposure_conditions import AllowableOpticalDensityFilterNames
17
+ from dkist_processing_cryonirsp.models.exposure_conditions import ExposureConditions
18
+ from dkist_processing_cryonirsp.models.tags import CryonirspTag
19
+ from dkist_processing_cryonirsp.tasks.instrument_polarization import (
20
+ CIInstrumentPolarizationCalibration,
21
+ )
22
+ from dkist_processing_cryonirsp.tasks.instrument_polarization import (
23
+ SPInstrumentPolarizationCalibration,
24
+ )
25
+ from dkist_processing_cryonirsp.tests.conftest import cryonirsp_testing_parameters_factory
26
+ from dkist_processing_cryonirsp.tests.conftest import CryonirspConstantsDb
27
+ from dkist_processing_cryonirsp.tests.conftest import generate_fits_frame
28
+ from dkist_processing_cryonirsp.tests.header_models import CryonirspHeadersValidPolcalFrames
29
+
30
+
31
+ class DummyPolcalFitter(PolcalFitter):
32
+ def __init__(
33
+ self,
34
+ *,
35
+ local_dresser: Dresser,
36
+ global_dresser: Dresser,
37
+ fit_mode: str,
38
+ init_set: str,
39
+ fit_TM: bool = False,
40
+ threads: int = 1,
41
+ super_name: str = "",
42
+ _dont_fit: bool = False,
43
+ **fit_kwargs,
44
+ ):
45
+ with patch("dkist_processing_pac.fitter.polcal_fitter.FitObjects"):
46
+ super().__init__(
47
+ local_dresser=local_dresser,
48
+ global_dresser=global_dresser,
49
+ fit_mode="use_M12_I_sys_per_step",
50
+ init_set="OCCal_VIS",
51
+ _dont_fit=True,
52
+ )
53
+
54
+ self.num_modstates = local_dresser.nummod
55
+
56
+ @property
57
+ def demodulation_matrices(self) -> np.ndarray:
58
+ return np.ones((1, 1, 4, self.num_modstates))
59
+
60
+
61
+ def _create_polcal_dark_or_gain_array(
62
+ task, array_shape, num_mod, exposure_conditions, polcal_type, start_time
63
+ ):
64
+ ds = CryonirspHeadersValidPolcalFrames(
65
+ # Using array_shape here for dataset_shape so only 1 frame is created:
66
+ dataset_shape=array_shape,
67
+ array_shape=array_shape,
68
+ time_delta=10,
69
+ num_modstates=1,
70
+ modstate=1,
71
+ start_time=start_time,
72
+ )
73
+ header_generator = (
74
+ spec122_validator.validate_and_translate_to_214_l0(d.header(), return_type=fits.HDUList)[
75
+ 0
76
+ ].header
77
+ for d in ds
78
+ )
79
+ hdul = generate_fits_frame(header_generator=header_generator, shape=array_shape)
80
+ cs_step = 1 if polcal_type == TaskName.polcal_gain.value else 0
81
+ for m in range(1, num_mod + 1):
82
+ task.write(
83
+ data=hdul,
84
+ tags=[
85
+ CryonirspTag.task(polcal_type),
86
+ CryonirspTag.task_polcal(),
87
+ CryonirspTag.modstate(m),
88
+ CryonirspTag.cs_step(cs_step),
89
+ CryonirspTag.linearized(),
90
+ CryonirspTag.frame(),
91
+ CryonirspTag.exposure_conditions(exposure_conditions),
92
+ ],
93
+ encoder=fits_hdulist_encoder,
94
+ )
95
+
96
+
97
+ def _create_polcal_arrays(
98
+ task,
99
+ dataset_shape,
100
+ array_shape,
101
+ exposure_conditions,
102
+ start_time,
103
+ num_modstates,
104
+ num_cs_steps,
105
+ ):
106
+ for modstate in range(1, num_modstates + 1):
107
+ # Create polcal input frames for this modstate
108
+ ds = CryonirspHeadersValidPolcalFrames(
109
+ dataset_shape=dataset_shape,
110
+ array_shape=array_shape,
111
+ time_delta=10,
112
+ num_modstates=num_modstates,
113
+ modstate=modstate,
114
+ start_time=start_time,
115
+ )
116
+ header_generator = (
117
+ spec122_validator.validate_and_translate_to_214_l0(
118
+ d.header(), return_type=fits.HDUList
119
+ )[0].header
120
+ for d in ds
121
+ )
122
+ # cs_step does not map to a single keyword, so not needed in the fake headers
123
+ # We start at 2 because dark and gain are 0 and 1
124
+ for cs_step in range(2, num_cs_steps):
125
+ hdul = generate_fits_frame(header_generator=header_generator, shape=array_shape)
126
+ task.write(
127
+ data=hdul,
128
+ tags=[
129
+ CryonirspTag.task_polcal(),
130
+ CryonirspTag.modstate(modstate),
131
+ CryonirspTag.cs_step(cs_step),
132
+ CryonirspTag.linearized(),
133
+ CryonirspTag.frame(),
134
+ CryonirspTag.exposure_conditions(exposure_conditions),
135
+ ],
136
+ encoder=fits_hdulist_encoder,
137
+ )
138
+
139
+
140
+ @pytest.fixture(scope="function")
141
+ def ci_instrument_polarization_calibration_task(
142
+ tmp_path,
143
+ recipe_run_id,
144
+ assign_input_dataset_doc_to_task,
145
+ init_cryonirsp_constants_db,
146
+ mocker,
147
+ ):
148
+ num_beams = 1
149
+ num_modstates = 2
150
+ num_cs_steps = 2
151
+ num_spatial_steps = 1
152
+ exposure_time = 0.01 # From CryoHeadersValidPolcalFrames fixture (Check this value)
153
+ exposure_conditions = ExposureConditions(
154
+ exposure_time, AllowableOpticalDensityFilterNames.OPEN.value
155
+ )
156
+ # intermediate_shape = (10, 10)
157
+ dataset_shape = (num_cs_steps, 20, 10)
158
+ array_shape = (1, 20, 10)
159
+ constants_db = CryonirspConstantsDb(
160
+ NUM_MODSTATES=num_modstates,
161
+ NUM_BEAMS=num_beams,
162
+ NUM_CS_STEPS=num_cs_steps,
163
+ POLCAL_EXPOSURE_CONDITIONS_LIST=(exposure_conditions,),
164
+ ARM_ID="CI",
165
+ NUM_SPATIAL_STEPS=num_spatial_steps,
166
+ )
167
+ init_cryonirsp_constants_db(recipe_run_id, constants_db)
168
+ with CIInstrumentPolarizationCalibration(
169
+ recipe_run_id=recipe_run_id,
170
+ workflow_name="ci_instrument_polarization_calibration",
171
+ workflow_version="VX.Y",
172
+ ) as task:
173
+ try: # This try... block is here to make sure the dbs get cleaned up if there's a failure in the fixture
174
+ task.scratch = WorkflowFileSystem(
175
+ scratch_base_path=tmp_path, recipe_run_id=recipe_run_id
176
+ )
177
+ param_class = cryonirsp_testing_parameters_factory(param_path=tmp_path)
178
+ assign_input_dataset_doc_to_task(task, param_class())
179
+ mocker.patch(
180
+ "dkist_processing_cryonirsp.tasks.instrument_polarization.PolcalFitter",
181
+ new=DummyPolcalFitter,
182
+ )
183
+
184
+ # Don't test place-holder QA stuff for now
185
+ quality_metric_mocker = mocker.patch(
186
+ "dkist_processing_cryonirsp.tasks.instrument_polarization.CIInstrumentPolarizationCalibration.quality_store_polcal_results",
187
+ autospec=True,
188
+ )
189
+
190
+ # Create beam border intermediate array that is consistent with a single pixel array
191
+ task.intermediate_frame_write_arrays(
192
+ arrays=np.array([0, 1, 0, 1]), task_tag=CryonirspTag.task_beam_boundaries(), beam=1
193
+ )
194
+
195
+ # Create fake bad pixel map
196
+ task.intermediate_frame_write_arrays(
197
+ arrays=np.zeros((1, 1)), task_tag=CryonirspTag.task_bad_pixel_map()
198
+ )
199
+
200
+ start_time = datetime.now()
201
+ # Create a single fake polcal raw dark array
202
+ _create_polcal_dark_or_gain_array(
203
+ task,
204
+ array_shape,
205
+ num_modstates,
206
+ exposure_conditions,
207
+ TaskName.polcal_dark.value,
208
+ start_time,
209
+ )
210
+
211
+ # Create a single fake polcal gain array
212
+ _create_polcal_dark_or_gain_array(
213
+ task,
214
+ array_shape,
215
+ num_modstates,
216
+ exposure_conditions,
217
+ TaskName.polcal_gain.value,
218
+ start_time,
219
+ )
220
+
221
+ # Create a set of full polcal frames
222
+ _create_polcal_arrays(
223
+ task,
224
+ dataset_shape,
225
+ array_shape,
226
+ exposure_conditions,
227
+ start_time,
228
+ num_modstates,
229
+ num_cs_steps,
230
+ )
231
+
232
+ yield task, quality_metric_mocker
233
+ finally:
234
+ task._purge()
235
+
236
+
237
+ @pytest.fixture(scope="function")
238
+ def ci_instrument_polarization_calibration_task_with_no_data(
239
+ tmp_path, recipe_run_id, assign_input_dataset_doc_to_task, init_cryonirsp_constants_db
240
+ ):
241
+ init_cryonirsp_constants_db(recipe_run_id, CryonirspConstantsDb())
242
+ with CIInstrumentPolarizationCalibration(
243
+ recipe_run_id=recipe_run_id,
244
+ workflow_name="ci_instrument_polarization_calibration",
245
+ workflow_version="VX.Y",
246
+ ) as task:
247
+ try: # This try... block is here to make sure the dbs get cleaned up if there's a failure in the fixture
248
+ param_class = cryonirsp_testing_parameters_factory(param_path=tmp_path)
249
+ assign_input_dataset_doc_to_task(task, param_class())
250
+ yield task
251
+ finally:
252
+ task._purge()
253
+
254
+
255
+ @pytest.fixture(scope="function")
256
+ def sp_instrument_polarization_calibration_task(
257
+ tmp_path,
258
+ recipe_run_id,
259
+ assign_input_dataset_doc_to_task,
260
+ init_cryonirsp_constants_db,
261
+ mocker,
262
+ ):
263
+ num_beams = 2
264
+ num_modstates = 2
265
+ num_cs_steps = 2
266
+ num_spatial_steps = 1
267
+ exposure_time = 0.01 # From CryoHeadersValidPolcalFrames fixture (Check this value)
268
+ exposure_conditions = ExposureConditions(
269
+ exposure_time, AllowableOpticalDensityFilterNames.OPEN.value
270
+ )
271
+ # intermediate_shape = (10, 10)
272
+ dataset_shape = (num_cs_steps, 20, 10)
273
+ array_shape = (1, 20, 10)
274
+ constants_db = CryonirspConstantsDb(
275
+ NUM_MODSTATES=num_modstates,
276
+ NUM_BEAMS=num_beams,
277
+ NUM_CS_STEPS=num_cs_steps,
278
+ POLCAL_EXPOSURE_CONDITIONS_LIST=(exposure_conditions,),
279
+ ARM_ID="SP",
280
+ NUM_SPATIAL_STEPS=num_spatial_steps,
281
+ )
282
+ init_cryonirsp_constants_db(recipe_run_id, constants_db)
283
+ with SPInstrumentPolarizationCalibration(
284
+ recipe_run_id=recipe_run_id,
285
+ workflow_name="sp_instrument_polarization_calibration",
286
+ workflow_version="VX.Y",
287
+ ) as task:
288
+ try: # This try... block is here to make sure the dbs get cleaned up if there's a failure in the fixture
289
+ task.scratch = WorkflowFileSystem(
290
+ scratch_base_path=tmp_path, recipe_run_id=recipe_run_id
291
+ )
292
+ param_class = cryonirsp_testing_parameters_factory(param_path=tmp_path)
293
+ assign_input_dataset_doc_to_task(task, param_class())
294
+ mocker.patch(
295
+ "dkist_processing_cryonirsp.tasks.instrument_polarization.PolcalFitter",
296
+ new=DummyPolcalFitter,
297
+ )
298
+
299
+ # Don't test place-holder QA stuff for now
300
+ quality_metric_mocker = mocker.patch(
301
+ "dkist_processing_cryonirsp.tasks.instrument_polarization.SPInstrumentPolarizationCalibration.quality_store_polcal_results",
302
+ autospec=True,
303
+ )
304
+
305
+ # Create beam border intermediate arrays that are consistent with a single pixel array
306
+ for beam in range(1, num_beams + 1):
307
+ task.intermediate_frame_write_arrays(
308
+ arrays=np.array([0, 1, 0, 1]),
309
+ task_tag=CryonirspTag.task_beam_boundaries(),
310
+ beam=beam,
311
+ )
312
+
313
+ # Create fake bad pixel map
314
+ task.intermediate_frame_write_arrays(
315
+ arrays=np.zeros((1, 1)), task_tag=CryonirspTag.task_bad_pixel_map()
316
+ )
317
+
318
+ start_time = datetime.now()
319
+ # Create a single fake polcal raw dark array
320
+ _create_polcal_dark_or_gain_array(
321
+ task,
322
+ array_shape,
323
+ num_modstates,
324
+ exposure_conditions,
325
+ TaskName.polcal_dark.value,
326
+ start_time,
327
+ )
328
+
329
+ # Create a single fake polcal gain array
330
+ _create_polcal_dark_or_gain_array(
331
+ task,
332
+ array_shape,
333
+ num_modstates,
334
+ exposure_conditions,
335
+ TaskName.polcal_gain.value,
336
+ start_time,
337
+ )
338
+
339
+ # Create a set of full polcal frames
340
+ _create_polcal_arrays(
341
+ task,
342
+ dataset_shape,
343
+ array_shape,
344
+ exposure_conditions,
345
+ start_time,
346
+ num_modstates,
347
+ num_cs_steps,
348
+ )
349
+
350
+ yield task, quality_metric_mocker
351
+ finally:
352
+ task._purge()
353
+
354
+
355
+ @pytest.fixture(scope="function")
356
+ def sp_instrument_polarization_calibration_task_with_no_data(
357
+ tmp_path, recipe_run_id, assign_input_dataset_doc_to_task, init_cryonirsp_constants_db
358
+ ):
359
+ init_cryonirsp_constants_db(recipe_run_id, CryonirspConstantsDb())
360
+ with SPInstrumentPolarizationCalibration(
361
+ recipe_run_id=recipe_run_id,
362
+ workflow_name="sp_instrument_polarization_calibration",
363
+ workflow_version="VX.Y",
364
+ ) as task:
365
+ try: # This try... block is here to make sure the dbs get cleaned up if there's a failure in the fixture
366
+ param_class = cryonirsp_testing_parameters_factory(param_path=tmp_path)
367
+ assign_input_dataset_doc_to_task(task, param_class())
368
+ yield task
369
+ finally:
370
+ task._purge()
371
+
372
+
373
+ @pytest.fixture()
374
+ def full_beam_shape() -> tuple[int, int]:
375
+ return (100, 256)
376
+
377
+
378
+ @pytest.fixture()
379
+ def single_demodulation_matrix() -> np.ndarray:
380
+ return np.arange(40).reshape(1, 1, 4, 10)
381
+
382
+
383
+ @pytest.fixture()
384
+ def multiple_demodulation_matrices() -> np.ndarray:
385
+ return np.arange(2 * 3 * 4 * 10).reshape(2, 3, 4, 10)
386
+
387
+
388
+ def test_ci_instrument_polarization_calibration_task(
389
+ ci_instrument_polarization_calibration_task, mocker
390
+ ):
391
+ """
392
+ Given: An InstrumentPolarizationCalibration task
393
+ When: Calling the task instance
394
+ Then: A demodulation matrix for each beam is produced and the correct call to the quality storage system was made
395
+ """
396
+
397
+ mocker.patch(
398
+ "dkist_processing_common.tasks.mixin.metadata_store.GraphQLClient", new=FakeGQLClient
399
+ )
400
+
401
+ # When
402
+ task, quality_mocker = ci_instrument_polarization_calibration_task
403
+ task()
404
+
405
+ # Then
406
+ for beam in [1]:
407
+ tags = [
408
+ CryonirspTag.intermediate(),
409
+ CryonirspTag.task_demodulation_matrices(),
410
+ CryonirspTag.beam(beam),
411
+ ]
412
+ assert len(list(task.read(tags=tags))) == 1
413
+
414
+ quality_mocker.assert_any_call(
415
+ task,
416
+ polcal_fitter=ANY,
417
+ label=f"CI Beam {beam}",
418
+ bin_nums=[
419
+ task.parameters.polcal_num_spatial_bins,
420
+ task.parameters.polcal_num_spatial_bins,
421
+ ],
422
+ bin_labels=["spatial", "spatial"],
423
+ skip_recording_constant_pars=False,
424
+ )
425
+
426
+
427
+ def test_sp_instrument_polarization_calibration_task(
428
+ sp_instrument_polarization_calibration_task, mocker
429
+ ):
430
+ """
431
+ Given: An InstrumentPolarizationCalibration task
432
+ When: Calling the task instance
433
+ Then: A demodulation matrix for each beam is produced and the correct call to the quality storage system was made
434
+ """
435
+
436
+ mocker.patch(
437
+ "dkist_processing_common.tasks.mixin.metadata_store.GraphQLClient", new=FakeGQLClient
438
+ )
439
+
440
+ # When
441
+ task, quality_mocker = sp_instrument_polarization_calibration_task
442
+ task()
443
+
444
+ # Then
445
+ for beam in [1, 2]:
446
+ tags = [
447
+ CryonirspTag.intermediate(),
448
+ CryonirspTag.task_demodulation_matrices(),
449
+ CryonirspTag.beam(beam),
450
+ ]
451
+ assert len(list(task.read(tags=tags))) == 1
452
+
453
+ quality_mocker.assert_any_call(
454
+ task,
455
+ polcal_fitter=ANY,
456
+ label=f"SP Beam {beam}",
457
+ bin_nums=[
458
+ task.parameters.polcal_num_spatial_bins,
459
+ task.parameters.polcal_num_spectral_bins,
460
+ ],
461
+ bin_labels=["spatial", "spectral"],
462
+ skip_recording_constant_pars=beam == 2,
463
+ )
464
+
465
+
466
+ def test_reshape_ci_demod_matrices(
467
+ ci_instrument_polarization_calibration_task_with_no_data,
468
+ multiple_demodulation_matrices,
469
+ full_beam_shape,
470
+ ):
471
+ """
472
+ Given: An InstrumentPolarizationCalibration task and a set of demodulation matrices sampled over the full FOV
473
+ When: Up-sampling the demodulation matrices
474
+ Then: The final set of demodulation matrices has the correct, full-FOV shape
475
+ """
476
+ ci_instrument_polarization_calibration_task_with_no_data.single_beam_shape = full_beam_shape
477
+ result = ci_instrument_polarization_calibration_task_with_no_data.reshape_demod_matrices(
478
+ multiple_demodulation_matrices
479
+ )
480
+ assert result.shape == full_beam_shape + (4, 10)
481
+
482
+
483
+ def test_reshape_sp_demod_matrices(
484
+ sp_instrument_polarization_calibration_task_with_no_data,
485
+ multiple_demodulation_matrices,
486
+ full_beam_shape,
487
+ ):
488
+ """
489
+ Given: An InstrumentPolarizationCalibration task and a set of demodulation matrices sampled over the full FOV
490
+ When: Up-sampling the demodulation matrices
491
+ Then: The final set of demodulation matrices has the correct, full-FOV shape
492
+ """
493
+ sp_instrument_polarization_calibration_task_with_no_data.single_beam_shape = full_beam_shape
494
+ result = sp_instrument_polarization_calibration_task_with_no_data.reshape_demod_matrices(
495
+ multiple_demodulation_matrices
496
+ )
497
+ assert result.shape == full_beam_shape + (4, 10)
498
+
499
+
500
+ def test_reshape_single_ci_demod_matrix(
501
+ ci_instrument_polarization_calibration_task_with_no_data,
502
+ single_demodulation_matrix,
503
+ full_beam_shape,
504
+ ):
505
+ """
506
+ Given: An InstrumentPolarizationCalibration task and a single demodulation matrix for the whole FOV
507
+ When: Up-sampling the demodulation matrices
508
+ Then: The final set of demodulation matrices still only has a single matrix
509
+ """
510
+ ci_instrument_polarization_calibration_task_with_no_data.single_beam_shape = full_beam_shape
511
+ result = ci_instrument_polarization_calibration_task_with_no_data.reshape_demod_matrices(
512
+ single_demodulation_matrix
513
+ )
514
+ assert result.shape == (4, 10)
515
+
516
+
517
+ def test_reshape_single_sp_demod_matrix(
518
+ sp_instrument_polarization_calibration_task_with_no_data,
519
+ single_demodulation_matrix,
520
+ full_beam_shape,
521
+ ):
522
+ """
523
+ Given: An InstrumentPolarizationCalibration task and a single demodulation matrix for the whole FOV
524
+ When: Up-sampling the demodulation matrices
525
+ Then: The final set of demodulation matrices still only has a single matrix
526
+ """
527
+ sp_instrument_polarization_calibration_task_with_no_data.single_beam_shape = full_beam_shape
528
+ result = sp_instrument_polarization_calibration_task_with_no_data.reshape_demod_matrices(
529
+ single_demodulation_matrix
530
+ )
531
+ assert result.shape == (4, 10)