dkist-processing-dlnirsp 0.32.9__py3-none-any.whl → 0.33.0__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.
- dkist_processing_dlnirsp/models/constants.py +6 -0
- dkist_processing_dlnirsp/models/parameters.py +33 -3
- dkist_processing_dlnirsp/parsers/task.py +2 -25
- dkist_processing_dlnirsp/parsers/time.py +2 -2
- dkist_processing_dlnirsp/tasks/__init__.py +1 -2
- dkist_processing_dlnirsp/tasks/movie.py +1121 -0
- dkist_processing_dlnirsp/tasks/parse.py +13 -8
- dkist_processing_dlnirsp/tasks/solar.py +129 -30
- dkist_processing_dlnirsp/tests/conftest.py +45 -6
- dkist_processing_dlnirsp/tests/local_trial_workflows/l0_polcals_as_science.py +21 -18
- dkist_processing_dlnirsp/tests/local_trial_workflows/l0_solar_gain_as_science.py +21 -18
- dkist_processing_dlnirsp/tests/local_trial_workflows/l0_to_l1.py +21 -18
- dkist_processing_dlnirsp/tests/local_trial_workflows/local_trial_dev_mockers.py +1 -1
- dkist_processing_dlnirsp/tests/test_dlnirsp_constants.py +2 -0
- dkist_processing_dlnirsp/tests/test_movie.py +141 -0
- dkist_processing_dlnirsp/tests/test_parameters.py +8 -0
- dkist_processing_dlnirsp/tests/test_parse.py +10 -0
- dkist_processing_dlnirsp/tests/test_science.py +0 -9
- dkist_processing_dlnirsp/tests/test_solar.py +114 -17
- dkist_processing_dlnirsp/workflows/l0_processing.py +6 -8
- dkist_processing_dlnirsp/workflows/trial_workflow.py +7 -7
- {dkist_processing_dlnirsp-0.32.9.dist-info → dkist_processing_dlnirsp-0.33.0.dist-info}/METADATA +41 -24
- {dkist_processing_dlnirsp-0.32.9.dist-info → dkist_processing_dlnirsp-0.33.0.dist-info}/RECORD +27 -29
- docs/gain.rst +7 -3
- dkist_processing_dlnirsp/tasks/assemble_movie.py +0 -150
- dkist_processing_dlnirsp/tasks/make_movie_frames.py +0 -156
- dkist_processing_dlnirsp/tests/test_assemble_movie.py +0 -169
- dkist_processing_dlnirsp/tests/test_make_movie_frames.py +0 -98
- {dkist_processing_dlnirsp-0.32.9.dist-info → dkist_processing_dlnirsp-0.33.0.dist-info}/WHEEL +0 -0
- {dkist_processing_dlnirsp-0.32.9.dist-info → dkist_processing_dlnirsp-0.33.0.dist-info}/entry_points.txt +0 -0
- {dkist_processing_dlnirsp-0.32.9.dist-info → dkist_processing_dlnirsp-0.33.0.dist-info}/top_level.txt +0 -0
|
@@ -13,7 +13,6 @@ from dkist_processing_common.tasks import WorkflowTaskBase
|
|
|
13
13
|
from dkist_service_configuration.logging import logger
|
|
14
14
|
|
|
15
15
|
from dkist_processing_dlnirsp.models.tags import DlnirspTag
|
|
16
|
-
from dkist_processing_dlnirsp.tasks.assemble_movie import AssembleDlnirspMovie
|
|
17
16
|
from dkist_processing_dlnirsp.tasks.bad_pixel_map import BadPixelCalibration
|
|
18
17
|
from dkist_processing_dlnirsp.tasks.dark import DarkCalibration
|
|
19
18
|
from dkist_processing_dlnirsp.tasks.geometric import GeometricCalibration
|
|
@@ -22,7 +21,7 @@ from dkist_processing_dlnirsp.tasks.instrument_polarization import InstrumentPol
|
|
|
22
21
|
from dkist_processing_dlnirsp.tasks.l1_output_data import DlnirspAssembleQualityData
|
|
23
22
|
from dkist_processing_dlnirsp.tasks.lamp import LampCalibration
|
|
24
23
|
from dkist_processing_dlnirsp.tasks.linearity_correction import LinearityCorrection
|
|
25
|
-
from dkist_processing_dlnirsp.tasks.
|
|
24
|
+
from dkist_processing_dlnirsp.tasks.movie import MakeDlnirspMovie
|
|
26
25
|
from dkist_processing_dlnirsp.tasks.parse import ParseL0DlnirspLinearizedData
|
|
27
26
|
from dkist_processing_dlnirsp.tasks.parse import ParseL0DlnirspRampData
|
|
28
27
|
from dkist_processing_dlnirsp.tasks.quality_metrics import DlnirspL0QualityMetrics
|
|
@@ -171,9 +170,10 @@ def main(
|
|
|
171
170
|
load_bad_pixel: bool = False,
|
|
172
171
|
load_geometric: bool = False,
|
|
173
172
|
load_wavelength_calibration: bool = False,
|
|
174
|
-
load_solar: bool = False,
|
|
175
173
|
load_inst_polcal: bool = False,
|
|
174
|
+
load_solar: bool = False,
|
|
176
175
|
load_calibrated_data: bool = False,
|
|
176
|
+
skip_movie: bool = False,
|
|
177
177
|
transfer_trial_data: str | None = None,
|
|
178
178
|
):
|
|
179
179
|
with ManualProcessing(
|
|
@@ -261,18 +261,18 @@ def main(
|
|
|
261
261
|
manual_processing_run.run_task(task=WavelengthCalibration)
|
|
262
262
|
manual_processing_run.run_task(task=SaveWavelengthCal)
|
|
263
263
|
|
|
264
|
-
if load_solar:
|
|
265
|
-
manual_processing_run.run_task(task=LoadSolarCal)
|
|
266
|
-
else:
|
|
267
|
-
manual_processing_run.run_task(task=SolarCalibration)
|
|
268
|
-
manual_processing_run.run_task(task=SaveSolarCal)
|
|
269
|
-
|
|
270
264
|
if load_inst_polcal:
|
|
271
265
|
manual_processing_run.run_task(task=LoadInstPolCal)
|
|
272
266
|
else:
|
|
273
267
|
manual_processing_run.run_task(task=InstrumentPolarizationCalibration)
|
|
274
268
|
manual_processing_run.run_task(task=SaveInstPolCal)
|
|
275
269
|
|
|
270
|
+
if load_solar:
|
|
271
|
+
manual_processing_run.run_task(task=LoadSolarCal)
|
|
272
|
+
else:
|
|
273
|
+
manual_processing_run.run_task(task=SolarCalibration)
|
|
274
|
+
manual_processing_run.run_task(task=SaveSolarCal)
|
|
275
|
+
|
|
276
276
|
if load_calibrated_data:
|
|
277
277
|
manual_processing_run.run_task(task=LoadCalibratedData)
|
|
278
278
|
else:
|
|
@@ -281,8 +281,6 @@ def main(
|
|
|
281
281
|
|
|
282
282
|
manual_processing_run.run_task(task=DlnirspWriteL1Frame)
|
|
283
283
|
manual_processing_run.run_task(task=ValidateL1Output)
|
|
284
|
-
manual_processing_run.run_task(task=MakeDlnirspMovieFrames)
|
|
285
|
-
manual_processing_run.run_task(task=AssembleDlnirspMovie)
|
|
286
284
|
|
|
287
285
|
manual_processing_run.run_task(task=DlnirspL0QualityMetrics)
|
|
288
286
|
manual_processing_run.run_task(task=DlnirspL1QualityMetrics)
|
|
@@ -293,6 +291,9 @@ def main(
|
|
|
293
291
|
|
|
294
292
|
manual_processing_run.run_task(task=CreateTrialAsdf)
|
|
295
293
|
|
|
294
|
+
if not skip_movie:
|
|
295
|
+
manual_processing_run.run_task(task=MakeDlnirspMovie)
|
|
296
|
+
|
|
296
297
|
if transfer_trial_data:
|
|
297
298
|
transfer_trial_data_locally(
|
|
298
299
|
trial_output_location=transfer_trial_data, processing_run=manual_processing_run
|
|
@@ -391,21 +392,22 @@ if __name__ == "__main__":
|
|
|
391
392
|
help="Load wavelength calibration solution from previously saved run",
|
|
392
393
|
action="store_true",
|
|
393
394
|
)
|
|
394
|
-
parser.add_argument(
|
|
395
|
-
"-S",
|
|
396
|
-
"--load-solar",
|
|
397
|
-
help="Load solar calibration from previously saved run",
|
|
398
|
-
action="store_true",
|
|
399
|
-
)
|
|
400
395
|
parser.add_argument(
|
|
401
396
|
"-P",
|
|
402
397
|
"--load-inst-polcal",
|
|
403
398
|
help="Load instrument polarization calibration from previously saved run",
|
|
404
399
|
action="store_true",
|
|
405
400
|
)
|
|
401
|
+
parser.add_argument(
|
|
402
|
+
"-S",
|
|
403
|
+
"--load-solar",
|
|
404
|
+
help="Load solar calibration from previously saved run",
|
|
405
|
+
action="store_true",
|
|
406
|
+
)
|
|
406
407
|
parser.add_argument(
|
|
407
408
|
"-C", "--load-calibrated-data", help="Load CALIBRATED science frames", action="store_true"
|
|
408
409
|
)
|
|
410
|
+
parser.add_argument("-V", "--skip-movie", help="Don't make a browse movie", action="store_true")
|
|
409
411
|
parser.add_argument(
|
|
410
412
|
"-p",
|
|
411
413
|
"--param-dir",
|
|
@@ -432,9 +434,10 @@ if __name__ == "__main__":
|
|
|
432
434
|
load_bad_pixel=args.load_bad_pixel,
|
|
433
435
|
load_geometric=args.load_geometric,
|
|
434
436
|
load_wavelength_calibration=args.load_wavelength_calibration,
|
|
435
|
-
load_solar=args.load_solar,
|
|
436
437
|
load_inst_polcal=args.load_inst_polcal,
|
|
438
|
+
load_solar=args.load_solar,
|
|
437
439
|
load_calibrated_data=args.load_calibrated_data,
|
|
440
|
+
skip_movie=args.skip_movie,
|
|
438
441
|
transfer_trial_data=args.transfer_trial_data,
|
|
439
442
|
)
|
|
440
443
|
)
|
|
@@ -89,7 +89,7 @@ def permissive_write_l1_task(force_intensity_only: bool):
|
|
|
89
89
|
self, header: fits.Header, stokes: Literal["I", "Q", "U", "V"]
|
|
90
90
|
) -> fits.Header:
|
|
91
91
|
if force_intensity_only:
|
|
92
|
-
header[
|
|
92
|
+
header[DlnirspMetadataKey.polarimeter_mode] = "Stokes I"
|
|
93
93
|
|
|
94
94
|
return super().add_dataset_headers(header=header, stokes=stokes)
|
|
95
95
|
|
|
@@ -28,6 +28,7 @@ class test_constants:
|
|
|
28
28
|
arm_position_mm: float = 39.2
|
|
29
29
|
grating_constant_inverse_mm: float = 19.0
|
|
30
30
|
grating_position_deg: float = 87.4
|
|
31
|
+
obs_ip_end_time: str = "2000-01-01T00:01:00"
|
|
31
32
|
# We don't need all the common ones, but let's put one just to check
|
|
32
33
|
instrument: str = "CHECK_OUT_THIS_INSTRUMENT"
|
|
33
34
|
|
|
@@ -47,6 +48,7 @@ def simple_constant_names():
|
|
|
47
48
|
"observe_exposure_times",
|
|
48
49
|
"num_modstates",
|
|
49
50
|
"solar_gain_ip_start_time",
|
|
51
|
+
"obs_ip_end_time",
|
|
50
52
|
"instrument",
|
|
51
53
|
]
|
|
52
54
|
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import shutil
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pytest
|
|
5
|
+
from dkist_processing_common._util.scratch import WorkflowFileSystem
|
|
6
|
+
from dkist_processing_common.codecs.json import json_encoder
|
|
7
|
+
|
|
8
|
+
from dkist_processing_dlnirsp.models.tags import DlnirspTag
|
|
9
|
+
from dkist_processing_dlnirsp.tasks.movie import MakeDlnirspMovie
|
|
10
|
+
from dkist_processing_dlnirsp.tests.conftest import CalibratedHeaders
|
|
11
|
+
from dkist_processing_dlnirsp.tests.conftest import DlnirspTestingConstants
|
|
12
|
+
from dkist_processing_dlnirsp.tests.conftest import DlnirspTestingParameters
|
|
13
|
+
from dkist_processing_dlnirsp.tests.conftest import write_calibrated_frames_to_task
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@pytest.fixture
|
|
17
|
+
def movie_task(recipe_run_id, tmp_path, link_constants_db, assign_input_dataset_doc_to_task):
|
|
18
|
+
link_constants_db(
|
|
19
|
+
recipe_run_id=recipe_run_id,
|
|
20
|
+
constants_obj=DlnirspTestingConstants(),
|
|
21
|
+
)
|
|
22
|
+
with MakeDlnirspMovie(
|
|
23
|
+
recipe_run_id=recipe_run_id,
|
|
24
|
+
workflow_name="workflow_name",
|
|
25
|
+
workflow_version="workflow_version",
|
|
26
|
+
) as task:
|
|
27
|
+
assign_input_dataset_doc_to_task(
|
|
28
|
+
task,
|
|
29
|
+
DlnirspTestingParameters(),
|
|
30
|
+
arm_id="VIS",
|
|
31
|
+
)
|
|
32
|
+
task.scratch = WorkflowFileSystem(scratch_base_path=tmp_path, recipe_run_id=recipe_run_id)
|
|
33
|
+
|
|
34
|
+
yield task
|
|
35
|
+
task._purge()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@pytest.fixture(scope="session")
|
|
39
|
+
def num_spectral_pix() -> int:
|
|
40
|
+
return 396
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@pytest.fixture(scope="session")
|
|
44
|
+
def valid_wavelength_solution_header(num_spectral_pix) -> dict[str, str | int | float]:
|
|
45
|
+
"""A valid wavelength solution header that depends on `arm_id='vis'` in `movie_task`."""
|
|
46
|
+
return {
|
|
47
|
+
"CTYPE3": "AWAV-GRA",
|
|
48
|
+
"CUNIT3": "nm",
|
|
49
|
+
"CRPIX3": num_spectral_pix // 2,
|
|
50
|
+
"CRVAL3": 854.17,
|
|
51
|
+
"CDELT3": 0.00229,
|
|
52
|
+
"PV3_0": 23000,
|
|
53
|
+
"PV3_1": 90,
|
|
54
|
+
"PV3_2": 65.69,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def mindices_data_func(frame: CalibratedHeaders) -> np.ndarray:
|
|
59
|
+
shape = frame.array_shape
|
|
60
|
+
|
|
61
|
+
return np.ones(shape) * (1000 * frame.current_MINDEX1_value + 100 * frame.current_MINDEX2_value)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@pytest.mark.parametrize(
|
|
65
|
+
"has_multiple_mosaics",
|
|
66
|
+
[pytest.param(True, id="multi_mosaic"), pytest.param(False, id="single_mosaic")],
|
|
67
|
+
)
|
|
68
|
+
@pytest.mark.parametrize(
|
|
69
|
+
"is_polarimetric",
|
|
70
|
+
[pytest.param(True, id="polarimetric"), pytest.param(False, id="spectrographic")],
|
|
71
|
+
)
|
|
72
|
+
@pytest.mark.parametrize(
|
|
73
|
+
"is_mosaiced", [pytest.param(True, id="mosaiced"), pytest.param(False, id="no_mosaic")]
|
|
74
|
+
)
|
|
75
|
+
def test_movie_task(
|
|
76
|
+
movie_task,
|
|
77
|
+
has_multiple_mosaics,
|
|
78
|
+
is_polarimetric,
|
|
79
|
+
is_mosaiced,
|
|
80
|
+
num_spectral_pix,
|
|
81
|
+
valid_wavelength_solution_header,
|
|
82
|
+
link_constants_db,
|
|
83
|
+
mocker,
|
|
84
|
+
fake_gql_client,
|
|
85
|
+
):
|
|
86
|
+
"""
|
|
87
|
+
Given: A `MakeDlnirspMovie` task with CALIBRATED frames
|
|
88
|
+
When: Running the task
|
|
89
|
+
Then: The dang thing runs and produces the expected movie file
|
|
90
|
+
"""
|
|
91
|
+
mocker.patch(
|
|
92
|
+
"dkist_processing_common.tasks.mixin.metadata_store.GraphQLClient", new=fake_gql_client
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
num_dither = 1
|
|
96
|
+
num_mosaics = 2 if has_multiple_mosaics else 1
|
|
97
|
+
num_X_tiles = 2 if is_mosaiced else 1
|
|
98
|
+
num_Y_tiles = 2 if is_mosaiced else 1
|
|
99
|
+
stokes_params = ["I"]
|
|
100
|
+
if is_polarimetric:
|
|
101
|
+
stokes_params += ["Q", "U", "V"]
|
|
102
|
+
num_modstates = 8
|
|
103
|
+
pol_mode = "Full Stokes"
|
|
104
|
+
else:
|
|
105
|
+
num_modstates = 1
|
|
106
|
+
pol_mode = "Stokes I"
|
|
107
|
+
|
|
108
|
+
task = movie_task
|
|
109
|
+
|
|
110
|
+
constants_db = DlnirspTestingConstants(
|
|
111
|
+
POLARIMETER_MODE=pol_mode,
|
|
112
|
+
NUM_DITHER_STEPS=num_dither,
|
|
113
|
+
NUM_MOSAIC_REPEATS=num_mosaics,
|
|
114
|
+
NUM_MOSAIC_TILES_X=num_X_tiles,
|
|
115
|
+
NUM_MOSAIC_TILES_Y=num_Y_tiles,
|
|
116
|
+
NUM_MODSTATES=num_modstates,
|
|
117
|
+
)
|
|
118
|
+
link_constants_db(task.recipe_run_id, constants_db)
|
|
119
|
+
|
|
120
|
+
write_calibrated_frames_to_task(
|
|
121
|
+
task,
|
|
122
|
+
num_mosaics=num_mosaics,
|
|
123
|
+
num_X_tiles=num_X_tiles,
|
|
124
|
+
num_Y_tiles=num_Y_tiles,
|
|
125
|
+
dither_mode_on=False,
|
|
126
|
+
is_polarimetric=is_polarimetric,
|
|
127
|
+
array_shape=(num_spectral_pix, 20, 30),
|
|
128
|
+
data_func=mindices_data_func,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
task.write(
|
|
132
|
+
data=valid_wavelength_solution_header,
|
|
133
|
+
tags=[DlnirspTag.intermediate(), DlnirspTag.task_wavelength_solution()],
|
|
134
|
+
encoder=json_encoder,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
task()
|
|
138
|
+
|
|
139
|
+
movie_file_list = list(task.read(tags=[DlnirspTag.output(), DlnirspTag.movie()]))
|
|
140
|
+
|
|
141
|
+
assert len(movie_file_list) == 1
|
|
@@ -53,6 +53,12 @@ def arm_parameter_names() -> list[str]:
|
|
|
53
53
|
"ifu_y_pos_file_vis",
|
|
54
54
|
"ifu_y_pos_file_jband",
|
|
55
55
|
"ifu_y_pos_file_hband",
|
|
56
|
+
"movie_core_wave_value_nm_vis",
|
|
57
|
+
"movie_core_wave_value_nm_jband",
|
|
58
|
+
"movie_core_wave_value_nm_hband",
|
|
59
|
+
"movie_cont_wave_value_nm_vis",
|
|
60
|
+
"movie_cont_wave_value_nm_jband",
|
|
61
|
+
"movie_cont_wave_value_nm_hband",
|
|
56
62
|
]
|
|
57
63
|
|
|
58
64
|
|
|
@@ -170,6 +176,8 @@ def test_standard_parameters(
|
|
|
170
176
|
np.testing.assert_array_equal(param_obj_value, pv)
|
|
171
177
|
elif isinstance(param_obj_value, BaseModel):
|
|
172
178
|
assert param_obj_value.model_dump() == pv
|
|
179
|
+
elif property_name == "movie_vertical_nan_slices":
|
|
180
|
+
assert [slice(*i) for i in pv] == param_obj_value
|
|
173
181
|
else:
|
|
174
182
|
assert param_obj_value == pv
|
|
175
183
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
|
|
1
3
|
import numpy as np
|
|
2
4
|
import pytest
|
|
3
5
|
from astropy.io import fits
|
|
@@ -255,6 +257,8 @@ def test_parse_linearized_data(
|
|
|
255
257
|
grating_constant=grating_constant * 300,
|
|
256
258
|
grating_angle=grating_angle * -6,
|
|
257
259
|
)
|
|
260
|
+
obs_start_time = "2020-01-01T01:23:45"
|
|
261
|
+
modstate_length_sec = 0.5
|
|
258
262
|
num_obs = write_observe_frames_to_task(
|
|
259
263
|
task,
|
|
260
264
|
num_modstates=num_mod,
|
|
@@ -267,8 +271,13 @@ def test_parse_linearized_data(
|
|
|
267
271
|
grating_constant=grating_constant,
|
|
268
272
|
grating_angle=grating_angle,
|
|
269
273
|
dither_mode_on=dither_mode_on,
|
|
274
|
+
start_date=obs_start_time,
|
|
275
|
+
modstate_length_sec=modstate_length_sec,
|
|
270
276
|
swap_crpix_values="swap" in task.parameters.wcs_crpix_correction_method,
|
|
271
277
|
)
|
|
278
|
+
obs_end_time = datetime.datetime.fromisoformat(obs_start_time) + datetime.timedelta(
|
|
279
|
+
seconds=num_obs * modstate_length_sec
|
|
280
|
+
)
|
|
272
281
|
|
|
273
282
|
task()
|
|
274
283
|
|
|
@@ -350,6 +359,7 @@ def test_parse_linearized_data(
|
|
|
350
359
|
assert task.constants._db_dict[BudName.solar_gain_gos_level3_status] == "clear"
|
|
351
360
|
assert task.constants._db_dict[BudName.solar_gain_num_raw_frames_per_fpa] == 30
|
|
352
361
|
assert task.constants._db_dict[BudName.polcal_num_raw_frames_per_fpa] == 10
|
|
362
|
+
assert task.constants._db_dict[DlnirspBudName.obs_ip_end_time] == obs_end_time.isoformat("T")
|
|
353
363
|
|
|
354
364
|
|
|
355
365
|
def test_crpix_and_spatial_step_association_swapped(
|
|
@@ -70,15 +70,6 @@ def make_solar_data(solar_signal):
|
|
|
70
70
|
return make_array
|
|
71
71
|
|
|
72
72
|
|
|
73
|
-
@pytest.fixture
|
|
74
|
-
def make_full_demodulation_matrix(demodulation_matrix):
|
|
75
|
-
def make_array(frame: Spec122Dataset):
|
|
76
|
-
array_shape = frame.array_shape[1:]
|
|
77
|
-
return np.ones(array_shape + demodulation_matrix.shape) * demodulation_matrix
|
|
78
|
-
|
|
79
|
-
return make_array
|
|
80
|
-
|
|
81
|
-
|
|
82
73
|
@pytest.fixture
|
|
83
74
|
def make_linearized_science_data(
|
|
84
75
|
dark_signal,
|
|
@@ -12,9 +12,11 @@ from dkist_processing_dlnirsp.models.tags import DlnirspTag
|
|
|
12
12
|
from dkist_processing_dlnirsp.tasks.solar import SolarCalibration
|
|
13
13
|
from dkist_processing_dlnirsp.tests.conftest import DlnirspTestingConstants
|
|
14
14
|
from dkist_processing_dlnirsp.tests.conftest import DlnirspTestingParameters
|
|
15
|
+
from dkist_processing_dlnirsp.tests.conftest import tag_on_modstate
|
|
15
16
|
from dkist_processing_dlnirsp.tests.conftest import write_dark_frames_to_task
|
|
16
17
|
from dkist_processing_dlnirsp.tests.conftest import write_geometric_calibration_to_task
|
|
17
18
|
from dkist_processing_dlnirsp.tests.conftest import write_lamp_gain_frames_to_task
|
|
19
|
+
from dkist_processing_dlnirsp.tests.conftest import write_simple_frames_to_task
|
|
18
20
|
from dkist_processing_dlnirsp.tests.conftest import write_solar_gain_frames_to_task
|
|
19
21
|
|
|
20
22
|
|
|
@@ -30,7 +32,7 @@ def lamp_signal() -> float:
|
|
|
30
32
|
|
|
31
33
|
@pytest.fixture
|
|
32
34
|
def solar_signal() -> float:
|
|
33
|
-
return
|
|
35
|
+
return 62831.85
|
|
34
36
|
|
|
35
37
|
|
|
36
38
|
@pytest.fixture
|
|
@@ -52,11 +54,29 @@ def make_lamp_data(lamp_signal):
|
|
|
52
54
|
|
|
53
55
|
|
|
54
56
|
@pytest.fixture
|
|
55
|
-
def
|
|
57
|
+
def solar_stokes_stack(solar_signal) -> np.ndarray:
|
|
58
|
+
I = solar_signal
|
|
59
|
+
Q = np.random.random() - 0.5
|
|
60
|
+
U = np.random.random() - 0.5
|
|
61
|
+
V = np.random.random() - 0.5
|
|
62
|
+
|
|
63
|
+
return np.array([I, Q, U, V])
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@pytest.fixture
|
|
67
|
+
def modulated_solar_signal(solar_stokes_stack, modulation_matrix) -> np.ndarray:
|
|
68
|
+
return modulation_matrix @ solar_stokes_stack
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@pytest.fixture
|
|
72
|
+
def make_solar_data(
|
|
73
|
+
modulated_solar_signal, solar_signal, dark_signal, lamp_signal, is_polarimetric
|
|
74
|
+
):
|
|
56
75
|
def make_array(frame: Spec122Dataset):
|
|
57
76
|
shape = frame.array_shape[1:]
|
|
58
77
|
modstate = frame.header()["DLN__015"]
|
|
59
|
-
|
|
78
|
+
true_signal = modulated_solar_signal[modstate - 1] if is_polarimetric else solar_signal
|
|
79
|
+
return (np.ones(shape) * true_signal) * lamp_signal + dark_signal
|
|
60
80
|
|
|
61
81
|
return make_array
|
|
62
82
|
|
|
@@ -121,13 +141,20 @@ def solar_task_for_basic_corrections(
|
|
|
121
141
|
shifts_and_scales,
|
|
122
142
|
constants_class_with_different_num_slits,
|
|
123
143
|
reference_wave_axis,
|
|
144
|
+
modulation_matrix,
|
|
145
|
+
make_full_demodulation_matrix,
|
|
146
|
+
is_polarimetric,
|
|
124
147
|
):
|
|
125
148
|
solar_exp_time = 1.0
|
|
126
|
-
num_modstates =
|
|
149
|
+
num_modstates = modulation_matrix.shape[0] if is_polarimetric else 1
|
|
127
150
|
array_shape = jband_group_id_array.shape
|
|
128
151
|
link_constants_db(
|
|
129
152
|
recipe_run_id=recipe_run_id,
|
|
130
|
-
constants_obj=DlnirspTestingConstants(
|
|
153
|
+
constants_obj=DlnirspTestingConstants(
|
|
154
|
+
SOLAR_GAIN_EXPOSURE_TIMES=(solar_exp_time,),
|
|
155
|
+
NUM_MODSTATES=num_modstates,
|
|
156
|
+
POLARIMETER_MODE="Full Stokes" if is_polarimetric else "Stokes I",
|
|
157
|
+
),
|
|
131
158
|
)
|
|
132
159
|
|
|
133
160
|
with SolarCalibration(
|
|
@@ -172,6 +199,7 @@ def solar_task_for_basic_corrections(
|
|
|
172
199
|
DlnirspTag.task_solar_gain(),
|
|
173
200
|
DlnirspTag.exposure_time(solar_exp_time),
|
|
174
201
|
],
|
|
202
|
+
tag_func=tag_on_modstate,
|
|
175
203
|
data_func=make_solar_data,
|
|
176
204
|
)
|
|
177
205
|
shift_dict, scale_dict = shifts_and_scales
|
|
@@ -179,6 +207,16 @@ def solar_task_for_basic_corrections(
|
|
|
179
207
|
task, shift_dict=shift_dict, scale_dict=scale_dict, wave_axis=reference_wave_axis
|
|
180
208
|
)
|
|
181
209
|
|
|
210
|
+
if is_polarimetric:
|
|
211
|
+
write_simple_frames_to_task(
|
|
212
|
+
task,
|
|
213
|
+
task_type=TaskName.polcal.value,
|
|
214
|
+
array_shape=array_shape,
|
|
215
|
+
num_modstates=1,
|
|
216
|
+
tags=[DlnirspTag.intermediate(), DlnirspTag.task_demodulation_matrices()],
|
|
217
|
+
data_func=make_full_demodulation_matrix,
|
|
218
|
+
)
|
|
219
|
+
|
|
182
220
|
yield task, num_modstates, num_solar_frames
|
|
183
221
|
task._purge()
|
|
184
222
|
|
|
@@ -272,8 +310,15 @@ def solar_task_with_no_data(
|
|
|
272
310
|
task._purge()
|
|
273
311
|
|
|
274
312
|
|
|
313
|
+
@pytest.mark.parametrize(
|
|
314
|
+
"is_polarimetric", [pytest.param(True, id="polarimetric"), pytest.param(False, id="intensity")]
|
|
315
|
+
)
|
|
275
316
|
def test_compute_average_gain(
|
|
276
|
-
solar_task_for_basic_corrections,
|
|
317
|
+
solar_task_for_basic_corrections,
|
|
318
|
+
is_polarimetric,
|
|
319
|
+
solar_signal,
|
|
320
|
+
lamp_signal,
|
|
321
|
+
write_drifted_group_ids_to_task,
|
|
277
322
|
):
|
|
278
323
|
"""
|
|
279
324
|
Given: A SolarCalibration task with associated solar gain, lamp, geometric, and dark frames
|
|
@@ -284,22 +329,36 @@ def test_compute_average_gain(
|
|
|
284
329
|
|
|
285
330
|
write_drifted_group_ids_to_task(task)
|
|
286
331
|
|
|
287
|
-
|
|
332
|
+
pol_tag = []
|
|
333
|
+
if is_polarimetric:
|
|
334
|
+
task.compute_demodulated_I_gains()
|
|
335
|
+
pol_tag.append(DlnirspTag.stokes("I"))
|
|
336
|
+
else:
|
|
337
|
+
task.compute_intensity_only_avg_gains()
|
|
288
338
|
|
|
289
|
-
tags = [DlnirspTag.intermediate_frame(), DlnirspTag.task("SC_FULL_CORR")]
|
|
339
|
+
tags = [DlnirspTag.intermediate_frame(), DlnirspTag.task("SC_FULL_CORR")] + pol_tag
|
|
290
340
|
arrays = list(task.read(tags=tags, decoder=fits_array_decoder))
|
|
291
341
|
|
|
342
|
+
if not is_polarimetric:
|
|
343
|
+
assert task.count(tags=tags + [DlnirspTag.stokes("I")]) == 0
|
|
344
|
+
|
|
292
345
|
assert len(arrays) == 1
|
|
293
346
|
avg_array = arrays[0]
|
|
294
347
|
assert avg_array.shape == task.rectified_array_shape
|
|
295
|
-
expected_value =
|
|
296
|
-
np.testing.
|
|
348
|
+
expected_value = solar_signal
|
|
349
|
+
np.testing.assert_array_almost_equal(avg_array[~np.isnan(avg_array)], expected_value)
|
|
297
350
|
|
|
298
351
|
dark_only_list = list(
|
|
299
|
-
task.read(
|
|
352
|
+
task.read(
|
|
353
|
+
tags=[DlnirspTag.intermediate_frame(), DlnirspTag.task("SC_DARK_ONLY")],
|
|
354
|
+
decoder=fits_array_decoder,
|
|
355
|
+
)
|
|
300
356
|
)
|
|
301
357
|
assert len(dark_only_list) == 1
|
|
302
|
-
|
|
358
|
+
expected_dark_only_value = solar_signal * lamp_signal
|
|
359
|
+
np.testing.assert_array_almost_equal(
|
|
360
|
+
dark_only_list[0][~np.isnan(dark_only_list[0])], expected_dark_only_value
|
|
361
|
+
)
|
|
303
362
|
|
|
304
363
|
|
|
305
364
|
def test_compute_characteristic_spectra(solar_task_with_full_corr, groups_by_slitbeam):
|
|
@@ -326,8 +385,16 @@ def test_compute_characteristic_spectra(solar_task_with_full_corr, groups_by_sli
|
|
|
326
385
|
np.testing.assert_equal(rectified_group[~np.isnan(rectified_group)], slitbeam_median)
|
|
327
386
|
|
|
328
387
|
|
|
388
|
+
@pytest.mark.parametrize(
|
|
389
|
+
"is_polarimetric", [pytest.param(True, id="polarimetric"), pytest.param(False, id="intensity")]
|
|
390
|
+
)
|
|
329
391
|
def test_solar_task_completes(
|
|
330
|
-
solar_task_for_basic_corrections,
|
|
392
|
+
solar_task_for_basic_corrections,
|
|
393
|
+
write_drifted_group_ids_to_task,
|
|
394
|
+
is_polarimetric,
|
|
395
|
+
modulation_matrix,
|
|
396
|
+
mocker,
|
|
397
|
+
fake_gql_client,
|
|
331
398
|
):
|
|
332
399
|
"""
|
|
333
400
|
Given: A SolarCalibration task with necessary starting data
|
|
@@ -341,16 +408,46 @@ def test_solar_task_completes(
|
|
|
341
408
|
|
|
342
409
|
write_drifted_group_ids_to_task(task)
|
|
343
410
|
|
|
411
|
+
# Just make sure we set up the test correctly
|
|
412
|
+
assert num_solar_frames == modulation_matrix.shape[0] if is_polarimetric else 1
|
|
413
|
+
|
|
344
414
|
task()
|
|
345
415
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
416
|
+
# Make sure the correct code paths were taken; intensity-only data intermediates don't get the `stokes("I")` tag
|
|
417
|
+
assert task.count(tags=[DlnirspTag.intermediate_frame(), DlnirspTag.task("SC_DARK_ONLY")]) == 1
|
|
418
|
+
assert task.count(tags=[DlnirspTag.intermediate_frame(), DlnirspTag.task("SC_FULL_CORR")]) == 1
|
|
419
|
+
if not is_polarimetric:
|
|
420
|
+
assert (
|
|
421
|
+
task.count(
|
|
422
|
+
tags=[
|
|
423
|
+
DlnirspTag.intermediate_frame(),
|
|
424
|
+
DlnirspTag.task("SC_DARK_ONLY"),
|
|
425
|
+
DlnirspTag.stokes("I"),
|
|
426
|
+
]
|
|
427
|
+
)
|
|
428
|
+
== 0
|
|
349
429
|
)
|
|
350
|
-
|
|
430
|
+
assert (
|
|
431
|
+
task.count(
|
|
432
|
+
tags=[
|
|
433
|
+
DlnirspTag.intermediate_frame(),
|
|
434
|
+
DlnirspTag.task("SC_FULL_CORR"),
|
|
435
|
+
DlnirspTag.stokes("I"),
|
|
436
|
+
]
|
|
437
|
+
)
|
|
438
|
+
== 0
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
tags = [DlnirspTag.intermediate_frame(), DlnirspTag.task_solar_gain()]
|
|
442
|
+
if is_polarimetric:
|
|
443
|
+
tags.append(DlnirspTag.stokes("I"))
|
|
444
|
+
solar_cal_list = list(task.read(tags=tags))
|
|
351
445
|
assert len(solar_cal_list) == 1
|
|
352
446
|
assert solar_cal_list[0].exists()
|
|
353
447
|
|
|
448
|
+
if not is_polarimetric:
|
|
449
|
+
assert task.count(tags=tags + [DlnirspTag.stokes("I")]) == 0
|
|
450
|
+
|
|
354
451
|
quality_files = list(task.read(tags=[DlnirspTag.quality("TASK_TYPES")]))
|
|
355
452
|
assert len(quality_files) == 1
|
|
356
453
|
file = quality_files[0]
|
|
@@ -9,7 +9,6 @@ from dkist_processing_common.tasks import TransferL1Data
|
|
|
9
9
|
from dkist_processing_core import ResourceQueue
|
|
10
10
|
from dkist_processing_core import Workflow
|
|
11
11
|
|
|
12
|
-
from dkist_processing_dlnirsp.tasks import AssembleDlnirspMovie
|
|
13
12
|
from dkist_processing_dlnirsp.tasks import BadPixelCalibration
|
|
14
13
|
from dkist_processing_dlnirsp.tasks import DarkCalibration
|
|
15
14
|
from dkist_processing_dlnirsp.tasks import DlnirspAssembleQualityData
|
|
@@ -21,7 +20,7 @@ from dkist_processing_dlnirsp.tasks import IfuDriftCalibration
|
|
|
21
20
|
from dkist_processing_dlnirsp.tasks import InstrumentPolarizationCalibration
|
|
22
21
|
from dkist_processing_dlnirsp.tasks import LampCalibration
|
|
23
22
|
from dkist_processing_dlnirsp.tasks import LinearityCorrection
|
|
24
|
-
from dkist_processing_dlnirsp.tasks import
|
|
23
|
+
from dkist_processing_dlnirsp.tasks import MakeDlnirspMovie
|
|
25
24
|
from dkist_processing_dlnirsp.tasks import ParseL0DlnirspLinearizedData
|
|
26
25
|
from dkist_processing_dlnirsp.tasks import ParseL0DlnirspRampData
|
|
27
26
|
from dkist_processing_dlnirsp.tasks import ScienceCalibration
|
|
@@ -47,22 +46,21 @@ l0_pipeline.add_node(task=LampCalibration, upstreams=DarkCalibration)
|
|
|
47
46
|
l0_pipeline.add_node(task=BadPixelCalibration, upstreams=[LampCalibration, IfuDriftCalibration])
|
|
48
47
|
l0_pipeline.add_node(task=GeometricCalibration, upstreams=[IfuDriftCalibration, LampCalibration])
|
|
49
48
|
l0_pipeline.add_node(task=WavelengthCalibration, upstreams=GeometricCalibration)
|
|
50
|
-
l0_pipeline.add_node(task=SolarCalibration, upstreams=GeometricCalibration)
|
|
51
49
|
l0_pipeline.add_node(
|
|
52
50
|
task=InstrumentPolarizationCalibration,
|
|
53
51
|
resource_queue=ResourceQueue.HIGH_MEMORY,
|
|
54
52
|
upstreams=BadPixelCalibration,
|
|
55
53
|
)
|
|
56
54
|
l0_pipeline.add_node(
|
|
57
|
-
task=
|
|
55
|
+
task=SolarCalibration, upstreams=[GeometricCalibration, InstrumentPolarizationCalibration]
|
|
58
56
|
)
|
|
57
|
+
l0_pipeline.add_node(task=ScienceCalibration, upstreams=SolarCalibration)
|
|
59
58
|
l0_pipeline.add_node(
|
|
60
59
|
task=DlnirspWriteL1Frame, upstreams=[WavelengthCalibration, ScienceCalibration]
|
|
61
60
|
)
|
|
62
61
|
|
|
63
62
|
# Movie flow
|
|
64
|
-
l0_pipeline.add_node(task=
|
|
65
|
-
l0_pipeline.add_node(task=AssembleDlnirspMovie, upstreams=MakeDlnirspMovieFrames)
|
|
63
|
+
l0_pipeline.add_node(task=MakeDlnirspMovie, upstreams=[ScienceCalibration, WavelengthCalibration])
|
|
66
64
|
|
|
67
65
|
# Quality flow
|
|
68
66
|
l0_pipeline.add_node(task=DlnirspL0QualityMetrics, upstreams=ParseL0DlnirspLinearizedData)
|
|
@@ -76,11 +74,11 @@ l0_pipeline.add_node(
|
|
|
76
74
|
# Output flow
|
|
77
75
|
l0_pipeline.add_node(
|
|
78
76
|
task=TransferL1Data,
|
|
79
|
-
upstreams=[DlnirspWriteL1Frame,
|
|
77
|
+
upstreams=[DlnirspWriteL1Frame, MakeDlnirspMovie, DlnirspAssembleQualityData],
|
|
80
78
|
)
|
|
81
79
|
l0_pipeline.add_node(
|
|
82
80
|
task=SubmitDatasetMetadata,
|
|
83
|
-
upstreams=[DlnirspWriteL1Frame,
|
|
81
|
+
upstreams=[DlnirspWriteL1Frame, MakeDlnirspMovie],
|
|
84
82
|
)
|
|
85
83
|
l0_pipeline.add_node(
|
|
86
84
|
task=PublishCatalogAndQualityMessages, upstreams=[TransferL1Data, SubmitDatasetMetadata]
|