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,319 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pytest
|
|
5
|
+
from dkist_processing_common._util.scratch import WorkflowFileSystem
|
|
6
|
+
from dkist_processing_common.codecs.fits import fits_hdulist_encoder
|
|
7
|
+
from dkist_processing_common.tests.conftest import FakeGQLClient
|
|
8
|
+
from dkist_processing_math import transform
|
|
9
|
+
|
|
10
|
+
from dkist_processing_cryonirsp.models.exposure_conditions import AllowableOpticalDensityFilterNames
|
|
11
|
+
from dkist_processing_cryonirsp.models.exposure_conditions import ExposureConditions
|
|
12
|
+
from dkist_processing_cryonirsp.models.tags import CryonirspTag
|
|
13
|
+
from dkist_processing_cryonirsp.tasks.sp_geometric import SPGeometricCalibration
|
|
14
|
+
from dkist_processing_cryonirsp.tests.conftest import cryonirsp_testing_parameters_factory
|
|
15
|
+
from dkist_processing_cryonirsp.tests.conftest import CryonirspConstantsDb
|
|
16
|
+
from dkist_processing_cryonirsp.tests.conftest import generate_214_l0_fits_frame
|
|
17
|
+
from dkist_processing_cryonirsp.tests.header_models import CryonirspHeadersValidSPSolarGainFrames
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@pytest.fixture(scope="function")
|
|
21
|
+
def geometric_calibration_task_that_completes(
|
|
22
|
+
tmp_path, recipe_run_id, assign_input_dataset_doc_to_task, init_cryonirsp_constants_db
|
|
23
|
+
):
|
|
24
|
+
# This fixture makes data that look enough like real data that all of the feature detection stuff at least runs
|
|
25
|
+
# through (mostly this is an issue for the angle calculation). It would be great to contrive data that
|
|
26
|
+
# produce a geometric calibration with real numbers that can be checked, but for now we'll rely on the grogu
|
|
27
|
+
# tests for that. In other words, this fixture just tests if the machinery of the task completes and some object
|
|
28
|
+
# (ANY object) is written correctly.
|
|
29
|
+
number_of_modstates = 1
|
|
30
|
+
number_of_beams = 2
|
|
31
|
+
exposure_time = 20.0 # From CryonirspHeadersValidSolarGainFrames fixture
|
|
32
|
+
exposure_conditions = ExposureConditions(
|
|
33
|
+
exposure_time, AllowableOpticalDensityFilterNames.OPEN.value
|
|
34
|
+
)
|
|
35
|
+
data_shape_int = 100, 98
|
|
36
|
+
data_shape_raw = 100, 196
|
|
37
|
+
dataset_shape = 1, *data_shape_raw
|
|
38
|
+
constants_db = CryonirspConstantsDb(
|
|
39
|
+
NUM_MODSTATES=number_of_modstates,
|
|
40
|
+
SOLAR_GAIN_EXPOSURE_CONDITIONS_LIST=(exposure_conditions,),
|
|
41
|
+
ARM_ID="SP",
|
|
42
|
+
)
|
|
43
|
+
init_cryonirsp_constants_db(recipe_run_id, constants_db)
|
|
44
|
+
with SPGeometricCalibration(
|
|
45
|
+
recipe_run_id=recipe_run_id,
|
|
46
|
+
workflow_name="sp_geometric_calibration",
|
|
47
|
+
workflow_version="VX.Y",
|
|
48
|
+
) as task:
|
|
49
|
+
try: # This try... block is here to make sure the dbs get cleaned up if there's a failure in the fixture
|
|
50
|
+
task.scratch = WorkflowFileSystem(
|
|
51
|
+
scratch_base_path=tmp_path, recipe_run_id=recipe_run_id
|
|
52
|
+
)
|
|
53
|
+
param_class = cryonirsp_testing_parameters_factory(param_path=tmp_path)
|
|
54
|
+
assign_input_dataset_doc_to_task(task, param_class())
|
|
55
|
+
|
|
56
|
+
task.angles = [0.0, 0.0]
|
|
57
|
+
task.offsets = np.zeros((number_of_beams, 2))
|
|
58
|
+
task.shifts = np.zeros(data_shape_int[0])
|
|
59
|
+
|
|
60
|
+
# Create the intermediate frames needed
|
|
61
|
+
# Create fake bad pixel map
|
|
62
|
+
task.intermediate_frame_write_arrays(
|
|
63
|
+
arrays=np.zeros(data_shape_raw),
|
|
64
|
+
task_tag=CryonirspTag.task_bad_pixel_map(),
|
|
65
|
+
)
|
|
66
|
+
for beam in range(1, number_of_beams + 1):
|
|
67
|
+
dark_cal = np.ones(data_shape_int) * 3.0
|
|
68
|
+
task.intermediate_frame_write_arrays(
|
|
69
|
+
arrays=dark_cal,
|
|
70
|
+
beam=beam,
|
|
71
|
+
task_tag=CryonirspTag.task_dark(),
|
|
72
|
+
exposure_conditions=exposure_conditions,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Need a lamp for each beam
|
|
76
|
+
lamp_gain = np.ones(data_shape_int)
|
|
77
|
+
task.intermediate_frame_write_arrays(
|
|
78
|
+
arrays=lamp_gain,
|
|
79
|
+
beam=beam,
|
|
80
|
+
task_tag=CryonirspTag.task_lamp_gain(),
|
|
81
|
+
exposure_conditions=exposure_conditions,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# And a beam border intermediate array
|
|
85
|
+
border = data_shape_raw[1] // 2
|
|
86
|
+
task.intermediate_frame_write_arrays(
|
|
87
|
+
arrays=np.array(
|
|
88
|
+
[
|
|
89
|
+
0,
|
|
90
|
+
data_shape_int[0],
|
|
91
|
+
((beam - 1) * border),
|
|
92
|
+
(border + (beam - 1) * border),
|
|
93
|
+
]
|
|
94
|
+
),
|
|
95
|
+
task_tag=CryonirspTag.task_beam_boundaries(),
|
|
96
|
+
beam=beam,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Create the raw data, which is based on two beams per frame
|
|
100
|
+
beam1 = 1
|
|
101
|
+
beam2 = 2
|
|
102
|
+
ds = CryonirspHeadersValidSPSolarGainFrames(
|
|
103
|
+
dataset_shape=dataset_shape,
|
|
104
|
+
array_shape=dataset_shape,
|
|
105
|
+
time_delta=10,
|
|
106
|
+
)
|
|
107
|
+
header = ds.header()
|
|
108
|
+
# Create each beam of the solar gain image, rotate and translate them, and then combine them into one
|
|
109
|
+
true_solar_1 = np.ones(data_shape_int)
|
|
110
|
+
true_solar_1[data_shape_int[0] // 2, :] += 5
|
|
111
|
+
true_solar_1[:, data_shape_int[1] // 2] += 5
|
|
112
|
+
true_solar_2 = np.copy(true_solar_1)
|
|
113
|
+
# Now add the beam number to each beam in the array
|
|
114
|
+
true_solar_1 += beam1
|
|
115
|
+
true_solar_2 += beam2
|
|
116
|
+
translated_solar_1 = next(
|
|
117
|
+
transform.translate_arrays(arrays=true_solar_1, translation=task.offsets[0])
|
|
118
|
+
)
|
|
119
|
+
translated_solar_2 = next(
|
|
120
|
+
transform.translate_arrays(arrays=true_solar_2, translation=task.offsets[1])
|
|
121
|
+
)
|
|
122
|
+
distorted_solar_1 = next(
|
|
123
|
+
transform.rotate_arrays_about_point(arrays=translated_solar_1, angle=task.angles[0])
|
|
124
|
+
)
|
|
125
|
+
distorted_solar_2 = next(
|
|
126
|
+
transform.rotate_arrays_about_point(arrays=translated_solar_2, angle=task.angles[1])
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
raw_solar_1 = distorted_solar_1 + dark_cal
|
|
130
|
+
raw_solar_2 = distorted_solar_2 + dark_cal
|
|
131
|
+
raw_solar = np.concatenate((raw_solar_1, raw_solar_2), axis=1)
|
|
132
|
+
solar_hdul = generate_214_l0_fits_frame(data=raw_solar, s122_header=header)
|
|
133
|
+
task.write(
|
|
134
|
+
data=solar_hdul,
|
|
135
|
+
tags=[
|
|
136
|
+
CryonirspTag.linearized(),
|
|
137
|
+
CryonirspTag.task_solar_gain(),
|
|
138
|
+
CryonirspTag.frame(),
|
|
139
|
+
CryonirspTag.beam(beam),
|
|
140
|
+
CryonirspTag.exposure_conditions(exposure_conditions),
|
|
141
|
+
],
|
|
142
|
+
encoder=fits_hdulist_encoder,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
yield task, data_shape_int
|
|
146
|
+
finally:
|
|
147
|
+
task._purge()
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@pytest.fixture(scope="function")
|
|
151
|
+
def geometric_calibration_task_with_simple_raw_data(
|
|
152
|
+
tmp_path, recipe_run_id, assign_input_dataset_doc_to_task, init_cryonirsp_constants_db
|
|
153
|
+
):
|
|
154
|
+
number_of_modstates = 1
|
|
155
|
+
number_of_beams = 2
|
|
156
|
+
exposure_time = 20.0 # From CryonirspHeadersValidSolarGainFrames fixture
|
|
157
|
+
exposure_conditions = ExposureConditions(
|
|
158
|
+
exposure_time, AllowableOpticalDensityFilterNames.OPEN.value
|
|
159
|
+
)
|
|
160
|
+
data_shape_int = 100, 98
|
|
161
|
+
data_shape_raw = 100, 196
|
|
162
|
+
constants_db = CryonirspConstantsDb(
|
|
163
|
+
NUM_MODSTATES=number_of_modstates,
|
|
164
|
+
SOLAR_GAIN_EXPOSURE_CONDITIONS_LIST=(exposure_conditions,),
|
|
165
|
+
ARM_ID="SP",
|
|
166
|
+
)
|
|
167
|
+
init_cryonirsp_constants_db(recipe_run_id, constants_db)
|
|
168
|
+
with SPGeometricCalibration(
|
|
169
|
+
recipe_run_id=recipe_run_id,
|
|
170
|
+
workflow_name="sp_geometric_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
|
+
|
|
180
|
+
# Create the intermediate frames needed
|
|
181
|
+
# Create a fake bad pixel map
|
|
182
|
+
task.intermediate_frame_write_arrays(
|
|
183
|
+
arrays=np.zeros(data_shape_raw),
|
|
184
|
+
task_tag=CryonirspTag.task_bad_pixel_map(),
|
|
185
|
+
)
|
|
186
|
+
for beam in range(1, number_of_beams + 1):
|
|
187
|
+
dark_cal = np.ones(data_shape_int) * 3.0
|
|
188
|
+
task.intermediate_frame_write_arrays(
|
|
189
|
+
arrays=dark_cal,
|
|
190
|
+
beam=beam,
|
|
191
|
+
task_tag=CryonirspTag.task_dark(),
|
|
192
|
+
exposure_conditions=exposure_conditions,
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
# Need a lamp for each beam
|
|
196
|
+
lamp_gain = np.ones(data_shape_int)
|
|
197
|
+
task.intermediate_frame_write_arrays(
|
|
198
|
+
arrays=lamp_gain,
|
|
199
|
+
beam=beam,
|
|
200
|
+
task_tag=CryonirspTag.task_lamp_gain(),
|
|
201
|
+
exposure_conditions=exposure_conditions,
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
# And a beam border intermediate array
|
|
205
|
+
border = data_shape_raw[1] // 2
|
|
206
|
+
task.intermediate_frame_write_arrays(
|
|
207
|
+
arrays=np.array(
|
|
208
|
+
[
|
|
209
|
+
0,
|
|
210
|
+
data_shape_int[0],
|
|
211
|
+
((beam - 1) * border),
|
|
212
|
+
(border + (beam - 1) * border),
|
|
213
|
+
]
|
|
214
|
+
),
|
|
215
|
+
task_tag=CryonirspTag.task_beam_boundaries(),
|
|
216
|
+
beam=beam,
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
# Let's write a dark with the wrong exposure time, just to make sure it doesn't get used
|
|
220
|
+
task.intermediate_frame_write_arrays(
|
|
221
|
+
arrays=np.ones(data_shape_int) * 1e6,
|
|
222
|
+
beam=beam,
|
|
223
|
+
task_tag=CryonirspTag.task_dark(),
|
|
224
|
+
exposure_conditions=ExposureConditions(
|
|
225
|
+
exposure_time**2, AllowableOpticalDensityFilterNames.OPEN.value
|
|
226
|
+
),
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
# Create the raw data, which is based on two beams per frame
|
|
230
|
+
beam1 = 1
|
|
231
|
+
beam2 = 2
|
|
232
|
+
dark_cal_two_beams = np.concatenate((dark_cal, dark_cal), axis=1)
|
|
233
|
+
ds = CryonirspHeadersValidSPSolarGainFrames(
|
|
234
|
+
dataset_shape=(1,) + data_shape_raw,
|
|
235
|
+
array_shape=(1,) + data_shape_raw,
|
|
236
|
+
time_delta=10,
|
|
237
|
+
)
|
|
238
|
+
header = ds.header()
|
|
239
|
+
true_solar = np.ones(data_shape_raw)
|
|
240
|
+
# Now add the beam number to each beam in the array
|
|
241
|
+
true_solar[:, : data_shape_int[1]] += beam1
|
|
242
|
+
true_solar[:, data_shape_int[1] :] += beam2
|
|
243
|
+
raw_solar = true_solar + dark_cal_two_beams
|
|
244
|
+
solar_hdul = generate_214_l0_fits_frame(data=raw_solar, s122_header=header)
|
|
245
|
+
task.write(
|
|
246
|
+
data=solar_hdul,
|
|
247
|
+
tags=[
|
|
248
|
+
CryonirspTag.linearized(),
|
|
249
|
+
CryonirspTag.task_solar_gain(),
|
|
250
|
+
CryonirspTag.frame(),
|
|
251
|
+
CryonirspTag.exposure_conditions(exposure_conditions),
|
|
252
|
+
],
|
|
253
|
+
encoder=fits_hdulist_encoder,
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
yield task, data_shape_int, true_solar
|
|
257
|
+
finally:
|
|
258
|
+
task._purge()
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def test_geometric_task(geometric_calibration_task_that_completes, mocker):
|
|
262
|
+
"""
|
|
263
|
+
Given: A set of raw solar gain images and necessary intermediate calibrations
|
|
264
|
+
When: Running the geometric task
|
|
265
|
+
Then: The damn thing runs and makes outputs that at least are the right type
|
|
266
|
+
"""
|
|
267
|
+
# See the note in the fixture above: this test does NOT test for accuracy of the calibration
|
|
268
|
+
mocker.patch(
|
|
269
|
+
"dkist_processing_common.tasks.mixin.metadata_store.GraphQLClient", new=FakeGQLClient
|
|
270
|
+
)
|
|
271
|
+
task, beam_shape = geometric_calibration_task_that_completes
|
|
272
|
+
task()
|
|
273
|
+
for beam in range(1, task.constants.num_beams + 1):
|
|
274
|
+
assert type(task.intermediate_frame_load_angle(beam=beam)) is float
|
|
275
|
+
assert task.intermediate_frame_load_spec_shift(beam=beam).shape[0] == beam_shape[0]
|
|
276
|
+
assert task.intermediate_frame_load_state_offset(beam=beam).shape == (2,)
|
|
277
|
+
|
|
278
|
+
quality_files = task.read(tags=[CryonirspTag.quality("TASK_TYPES")])
|
|
279
|
+
for file in quality_files:
|
|
280
|
+
with file.open() as f:
|
|
281
|
+
data = json.load(f)
|
|
282
|
+
assert isinstance(data, dict)
|
|
283
|
+
assert data["total_frames"] == task.scratch.count_all(
|
|
284
|
+
tags=[
|
|
285
|
+
CryonirspTag.linearized(),
|
|
286
|
+
CryonirspTag.frame(),
|
|
287
|
+
CryonirspTag.task_solar_gain(),
|
|
288
|
+
]
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def test_basic_corrections(geometric_calibration_task_with_simple_raw_data):
|
|
293
|
+
"""
|
|
294
|
+
Given: A set of raw solar gain images and necessary intermediate calibrations
|
|
295
|
+
When: Doing basic dark and lamp gain corrections
|
|
296
|
+
Then: The corrections are applied correctly
|
|
297
|
+
"""
|
|
298
|
+
task, beam_shape, true_solar = geometric_calibration_task_with_simple_raw_data
|
|
299
|
+
flipped_true_solar = np.flip(true_solar, axis=1)
|
|
300
|
+
task.do_basic_corrections()
|
|
301
|
+
|
|
302
|
+
# Positive test on the flipped true solar image
|
|
303
|
+
for beam in range(1, task.constants.num_beams + 1):
|
|
304
|
+
if beam == 1:
|
|
305
|
+
expected = flipped_true_solar[:, 0 : beam_shape[1]]
|
|
306
|
+
else:
|
|
307
|
+
expected = flipped_true_solar[:, beam_shape[1] :]
|
|
308
|
+
array = task.basic_dark_bp_corrected_data(beam=beam)
|
|
309
|
+
np.testing.assert_equal(expected, array)
|
|
310
|
+
|
|
311
|
+
# Negative test on the original true solar image
|
|
312
|
+
for beam in range(1, task.constants.num_beams + 1):
|
|
313
|
+
if beam == 1:
|
|
314
|
+
expected = true_solar[:, 0 : beam_shape[1]]
|
|
315
|
+
else:
|
|
316
|
+
expected = true_solar[:, beam_shape[1] :]
|
|
317
|
+
array = task.basic_dark_bp_corrected_data(beam=beam)
|
|
318
|
+
with pytest.raises(AssertionError):
|
|
319
|
+
np.testing.assert_equal(expected, array)
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pytest
|
|
5
|
+
from astropy.io import fits
|
|
6
|
+
from dkist_header_validator import spec122_validator
|
|
7
|
+
from dkist_processing_common._util.scratch import WorkflowFileSystem
|
|
8
|
+
from dkist_processing_common.codecs.fits import fits_hdulist_encoder
|
|
9
|
+
from dkist_processing_common.tests.conftest import FakeGQLClient
|
|
10
|
+
|
|
11
|
+
from dkist_processing_cryonirsp.models.tags import CryonirspTag
|
|
12
|
+
from dkist_processing_cryonirsp.tasks.make_movie_frames import SPMakeCryonirspMovieFrames
|
|
13
|
+
from dkist_processing_cryonirsp.tests.conftest import CryonirspConstantsDb
|
|
14
|
+
from dkist_processing_cryonirsp.tests.conftest import generate_fits_frame
|
|
15
|
+
from dkist_processing_cryonirsp.tests.header_models import CryonirspHeadersValidObserveFrames
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@pytest.fixture(
|
|
19
|
+
scope="function",
|
|
20
|
+
params=[
|
|
21
|
+
pytest.param((True, 2), id="polarimetric"),
|
|
22
|
+
pytest.param((False, 2), id="intensity-only"),
|
|
23
|
+
pytest.param((False, 1), id="single_scan_step"),
|
|
24
|
+
],
|
|
25
|
+
)
|
|
26
|
+
def sp_movie_frames_task(tmp_path, recipe_run_id, init_cryonirsp_constants_db, request):
|
|
27
|
+
is_polarimetric, scan_steps = request.param
|
|
28
|
+
map_scans = 3
|
|
29
|
+
array_shape = (3, 4)
|
|
30
|
+
if is_polarimetric:
|
|
31
|
+
num_mod = 8
|
|
32
|
+
spin_mode = "Continuous"
|
|
33
|
+
else:
|
|
34
|
+
num_mod = 1
|
|
35
|
+
spin_mode = "None"
|
|
36
|
+
constants_db = CryonirspConstantsDb(
|
|
37
|
+
NUM_MODSTATES=num_mod,
|
|
38
|
+
MODULATOR_SPIN_MODE=spin_mode,
|
|
39
|
+
NUM_SCAN_STEPS=scan_steps,
|
|
40
|
+
NUM_MAP_SCANS=map_scans,
|
|
41
|
+
TIME_OBS_LIST=(datetime.now().isoformat("T"),),
|
|
42
|
+
)
|
|
43
|
+
init_cryonirsp_constants_db(recipe_run_id, constants_db)
|
|
44
|
+
with SPMakeCryonirspMovieFrames(
|
|
45
|
+
recipe_run_id=recipe_run_id, workflow_name="make_movie_frames", workflow_version="VX.Y"
|
|
46
|
+
) as task:
|
|
47
|
+
try: # This try... block is here to make sure the dbs get cleaned up if there's a failure in the fixture
|
|
48
|
+
meas_num = 1 # Use only the first measurement if there are multiple measurements.
|
|
49
|
+
task.scratch = WorkflowFileSystem(
|
|
50
|
+
scratch_base_path=tmp_path, recipe_run_id=recipe_run_id
|
|
51
|
+
)
|
|
52
|
+
start_time = datetime.now()
|
|
53
|
+
for stokes_state in ["I", "Q", "U", "V"]:
|
|
54
|
+
for map_scan in range(1, map_scans + 1):
|
|
55
|
+
for scan_step in range(0, scan_steps + 1):
|
|
56
|
+
ds = CryonirspHeadersValidObserveFrames(
|
|
57
|
+
dataset_shape=(2, *array_shape),
|
|
58
|
+
array_shape=(1, *array_shape),
|
|
59
|
+
time_delta=10,
|
|
60
|
+
num_map_scans=map_scans,
|
|
61
|
+
map_scan=map_scan,
|
|
62
|
+
num_scan_steps=scan_steps,
|
|
63
|
+
scan_step=scan_step,
|
|
64
|
+
num_modstates=1,
|
|
65
|
+
modstate=1,
|
|
66
|
+
start_time=start_time,
|
|
67
|
+
num_meas=1,
|
|
68
|
+
meas_num=1,
|
|
69
|
+
arm_id="SP",
|
|
70
|
+
)
|
|
71
|
+
header_generator = (
|
|
72
|
+
spec122_validator.validate_and_translate_to_214_l0(
|
|
73
|
+
d.header(), return_type=fits.HDUList
|
|
74
|
+
)[0].header
|
|
75
|
+
for d in ds
|
|
76
|
+
)
|
|
77
|
+
hdul = generate_fits_frame(
|
|
78
|
+
header_generator=header_generator,
|
|
79
|
+
shape=(1, *array_shape),
|
|
80
|
+
)
|
|
81
|
+
task.write(
|
|
82
|
+
data=hdul,
|
|
83
|
+
tags=[
|
|
84
|
+
CryonirspTag.calibrated(),
|
|
85
|
+
CryonirspTag.frame(),
|
|
86
|
+
CryonirspTag.map_scan(map_scan),
|
|
87
|
+
CryonirspTag.scan_step(scan_step),
|
|
88
|
+
CryonirspTag.stokes(stokes_state),
|
|
89
|
+
CryonirspTag.meas_num(meas_num),
|
|
90
|
+
],
|
|
91
|
+
encoder=fits_hdulist_encoder,
|
|
92
|
+
)
|
|
93
|
+
yield task, map_scans, scan_steps, array_shape, is_polarimetric
|
|
94
|
+
finally:
|
|
95
|
+
task._purge()
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_sp_make_movie_frames(sp_movie_frames_task, mocker):
|
|
99
|
+
"""
|
|
100
|
+
Given: A SPMakeCryonirspMovieFrames task
|
|
101
|
+
When: Calling the task instance
|
|
102
|
+
Then: a fits file is made for each scan containing the movie frame for that scan
|
|
103
|
+
"""
|
|
104
|
+
mocker.patch(
|
|
105
|
+
"dkist_processing_common.tasks.mixin.metadata_store.GraphQLClient", new=FakeGQLClient
|
|
106
|
+
)
|
|
107
|
+
task, map_scans, scan_steps, array_shape, is_polarimetric = sp_movie_frames_task
|
|
108
|
+
if scan_steps > 1:
|
|
109
|
+
expected_shape = (scan_steps, array_shape[0])
|
|
110
|
+
else:
|
|
111
|
+
expected_shape = array_shape
|
|
112
|
+
if is_polarimetric:
|
|
113
|
+
expected_shape = tuple(np.array(expected_shape) * 2)
|
|
114
|
+
task()
|
|
115
|
+
assert len(list(task.read(tags=[CryonirspTag.movie_frame()]))) == map_scans
|
|
116
|
+
for filepath in task.read(tags=[CryonirspTag.movie_frame()]):
|
|
117
|
+
assert filepath.exists()
|
|
118
|
+
hdul = fits.open(filepath)
|
|
119
|
+
assert hdul[0].header["INSTRUME"] == "CRYO-NIRSP"
|
|
120
|
+
# Multiply by 2 because a single map is (axis_length, steps) but there are 4 stokes in a 2x2 array
|
|
121
|
+
assert hdul[0].data.shape == expected_shape
|