dkist-processing-trend 0.1.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.
- changelog/.gitempty +0 -0
- dkist_processing_trend/__init__.py +10 -0
- dkist_processing_trend/config.py +11 -0
- dkist_processing_trend/models/__init__.py +1 -0
- dkist_processing_trend/models/constants.py +143 -0
- dkist_processing_trend/models/fit_options.py +15 -0
- dkist_processing_trend/models/fits_access.py +65 -0
- dkist_processing_trend/models/instrument.py +35 -0
- dkist_processing_trend/models/instrument_options.py +35 -0
- dkist_processing_trend/models/parameters.py +134 -0
- dkist_processing_trend/models/tags.py +117 -0
- dkist_processing_trend/models/task_name.py +20 -0
- dkist_processing_trend/parsers/__init__.py +1 -0
- dkist_processing_trend/parsers/arm_id.py +103 -0
- dkist_processing_trend/parsers/instrument_unique_bud.py +40 -0
- dkist_processing_trend/parsers/time.py +27 -0
- dkist_processing_trend/parsers/trend_l0_fits_access.py +123 -0
- dkist_processing_trend/tasks/__init__.py +27 -0
- dkist_processing_trend/tasks/arm_task_factory.py +57 -0
- dkist_processing_trend/tasks/dark.py +56 -0
- dkist_processing_trend/tasks/gain.py +74 -0
- dkist_processing_trend/tasks/initialize_arm_tasks.py +50 -0
- dkist_processing_trend/tasks/parse.py +169 -0
- dkist_processing_trend/tasks/prepare_fit_data_base.py +257 -0
- dkist_processing_trend/tasks/run_pac_fitter.py +392 -0
- dkist_processing_trend/tasks/trend_base.py +97 -0
- dkist_processing_trend/tasks/trend_output_data.py +167 -0
- dkist_processing_trend/tasks/visp/__init__.py +6 -0
- dkist_processing_trend/tasks/visp/visp_dmpd.py +410 -0
- dkist_processing_trend/tasks/visp/visp_extract_beam.py +14 -0
- dkist_processing_trend/tasks/visp/visp_geometric.py +260 -0
- dkist_processing_trend/tasks/visp/visp_prep_fit_data.py +162 -0
- dkist_processing_trend/tasks/visp/visp_process_demod.py +236 -0
- dkist_processing_trend/tasks/write_trend.py +663 -0
- dkist_processing_trend/tests/__init__.py +1 -0
- dkist_processing_trend/tests/conftest.py +718 -0
- dkist_processing_trend/tests/local_trial_workflows/__init__.py +0 -0
- dkist_processing_trend/tests/local_trial_workflows/l0_to_trend_visp_polcal.py +294 -0
- dkist_processing_trend/tests/local_trial_workflows/local_trial_helpers.py +488 -0
- dkist_processing_trend/tests/test_arm_task_factory.py +82 -0
- dkist_processing_trend/tests/test_base_tasks.py +86 -0
- dkist_processing_trend/tests/test_constants.py +120 -0
- dkist_processing_trend/tests/test_dark.py +97 -0
- dkist_processing_trend/tests/test_gain.py +135 -0
- dkist_processing_trend/tests/test_parameters.py +149 -0
- dkist_processing_trend/tests/test_parse.py +276 -0
- dkist_processing_trend/tests/test_prep_fit_data_base.py +233 -0
- dkist_processing_trend/tests/test_publish_catalog_messages.py +45 -0
- dkist_processing_trend/tests/test_run_pac_fitter.py +371 -0
- dkist_processing_trend/tests/test_stems.py +75 -0
- dkist_processing_trend/tests/test_transfer_output_data.py +76 -0
- dkist_processing_trend/tests/test_trend_fits_access.py +173 -0
- dkist_processing_trend/tests/test_visp.py +874 -0
- dkist_processing_trend/tests/test_workflows.py +10 -0
- dkist_processing_trend/tests/test_write_trend.py +460 -0
- dkist_processing_trend/workflows/__init__.py +3 -0
- dkist_processing_trend/workflows/visp.py +58 -0
- dkist_processing_trend-0.1.0.dist-info/METADATA +549 -0
- dkist_processing_trend-0.1.0.dist-info/RECORD +66 -0
- dkist_processing_trend-0.1.0.dist-info/WHEEL +5 -0
- dkist_processing_trend-0.1.0.dist-info/top_level.txt +3 -0
- docs/conf.py +57 -0
- docs/index.rst +10 -0
- docs/l0_to_trend_visp_polcal.rst +4 -0
- docs/landing_page.rst +11 -0
- docs/requirements_table.rst +8 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""Test integrity of workflows."""
|
|
2
|
+
|
|
3
|
+
from dkist_processing_core.build_utils import validate_workflows
|
|
4
|
+
|
|
5
|
+
from dkist_processing_trend import workflows
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_workflow_integrity():
|
|
9
|
+
"""Validate workflow to ensure acyclic-ness and export compilation"""
|
|
10
|
+
validate_workflows(workflows)
|
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pytest
|
|
5
|
+
from astropy.io import fits
|
|
6
|
+
from dkist_processing_common._util.scratch import WorkflowFileSystem
|
|
7
|
+
from dkist_processing_common.codecs.fits import fits_array_encoder
|
|
8
|
+
|
|
9
|
+
from dkist_processing_trend.models.instrument import InstrumentName
|
|
10
|
+
from dkist_processing_trend.models.parameters import TrendParameters
|
|
11
|
+
from dkist_processing_trend.models.tags import TrendTag
|
|
12
|
+
from dkist_processing_trend.tasks.write_trend import WriteTrendOutputs
|
|
13
|
+
from dkist_processing_trend.tests.conftest import ARM_ANGLE_FOR_ARM
|
|
14
|
+
from dkist_processing_trend.tests.conftest import CRYO_OD_FILTER_FOR_ARM
|
|
15
|
+
from dkist_processing_trend.tests.conftest import CRYO_SLIT_WIDTH_FOR_ARM
|
|
16
|
+
from dkist_processing_trend.tests.conftest import GRATING_ANGLE_FOR_ARM
|
|
17
|
+
from dkist_processing_trend.tests.conftest import GRATING_CONSTANT_FOR_ARM
|
|
18
|
+
from dkist_processing_trend.tests.conftest import WAVELENGTH_FOR_ARM
|
|
19
|
+
from dkist_processing_trend.tests.conftest import TrendInputDatasetParameterValues
|
|
20
|
+
from dkist_processing_trend.tests.conftest import TrendTestingConstantsDatabase
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.fixture(scope="session")
|
|
24
|
+
def num_polcal_ops() -> int:
|
|
25
|
+
return 1
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@pytest.fixture(scope="session")
|
|
29
|
+
def num_cs_steps() -> int:
|
|
30
|
+
return 3
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@pytest.fixture(scope="session")
|
|
34
|
+
def num_modstates() -> int:
|
|
35
|
+
return 4
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@pytest.fixture(scope="session")
|
|
39
|
+
def fov_shape() -> tuple[int, int]:
|
|
40
|
+
return (12, 11)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def num_beams_from_arm_id(arm_id) -> int:
|
|
44
|
+
if arm_id == "CI":
|
|
45
|
+
return 1
|
|
46
|
+
return 2
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@pytest.fixture(scope="session")
|
|
50
|
+
def fit_options_list() -> list[dict[str, str | bool]]:
|
|
51
|
+
return [
|
|
52
|
+
{
|
|
53
|
+
"name": "option1",
|
|
54
|
+
"remove_I_trend": True,
|
|
55
|
+
"fit_mode_name": "use_M12",
|
|
56
|
+
"init_set_name": "OCCal_VIS",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"name": "option2",
|
|
60
|
+
"remove_I_trend": False,
|
|
61
|
+
"fit_mode_name": "use_M12_I_sys_per_step",
|
|
62
|
+
"init_set_name": "OCCal_VIS",
|
|
63
|
+
},
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@pytest.fixture(scope="session")
|
|
68
|
+
def visp_instrument_options_list() -> list[dict]:
|
|
69
|
+
return [
|
|
70
|
+
{
|
|
71
|
+
"name": "full",
|
|
72
|
+
"num_spatial_px": 2560,
|
|
73
|
+
"num_spectral_px": 1000,
|
|
74
|
+
"spatial_smoothing_fit_order": 22,
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"name": "single",
|
|
78
|
+
"num_spatial_px": 1,
|
|
79
|
+
"num_spectral_px": 1,
|
|
80
|
+
"spatial_smoothing_fit_order": 1,
|
|
81
|
+
},
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@pytest.fixture(scope="session")
|
|
86
|
+
def cryonirsp_instrument_options_list() -> list[dict]:
|
|
87
|
+
return [
|
|
88
|
+
{"name": "full", "num_spatial_px": 1000, "num_spectral_px": 1000},
|
|
89
|
+
{"name": "single", "num_spatial_px": 1, "num_spectral_px": 1},
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@pytest.fixture(scope="session")
|
|
94
|
+
def dlnirsp_instrument_options_list() -> list[dict]:
|
|
95
|
+
return [
|
|
96
|
+
{
|
|
97
|
+
"name": "full",
|
|
98
|
+
"num_spatial_px": 1280,
|
|
99
|
+
"num_spectral_px": 1,
|
|
100
|
+
"spatial_smoothing_fit_order": 13,
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"name": "single",
|
|
104
|
+
"num_spatial_px": 1,
|
|
105
|
+
"num_spectral_px": 1,
|
|
106
|
+
"spatial_smoothing_fit_order": 1,
|
|
107
|
+
},
|
|
108
|
+
]
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@pytest.fixture
|
|
112
|
+
def write_trend_task(
|
|
113
|
+
tmp_path,
|
|
114
|
+
recipe_run_id,
|
|
115
|
+
link_constants_db,
|
|
116
|
+
assign_input_dataset_doc_to_task,
|
|
117
|
+
num_cs_steps,
|
|
118
|
+
num_modstates,
|
|
119
|
+
instrument,
|
|
120
|
+
fit_options_list,
|
|
121
|
+
visp_instrument_options_list,
|
|
122
|
+
cryonirsp_instrument_options_list,
|
|
123
|
+
dlnirsp_instrument_options_list,
|
|
124
|
+
arm_id_list,
|
|
125
|
+
):
|
|
126
|
+
link_constants_db(
|
|
127
|
+
recipe_run_id,
|
|
128
|
+
TrendTestingConstantsDatabase(
|
|
129
|
+
INSTRUMENT=instrument,
|
|
130
|
+
NUM_CS_STEPS=num_cs_steps,
|
|
131
|
+
NUM_MODSTATES=num_modstates,
|
|
132
|
+
ARM_ID_LIST=arm_id_list,
|
|
133
|
+
WAVELENGTH_FOR_ARM={json.dumps(arm): WAVELENGTH_FOR_ARM[arm] for arm in arm_id_list},
|
|
134
|
+
VISP_GRATING_ANGLE_FOR_ARM={
|
|
135
|
+
json.dumps(arm): GRATING_ANGLE_FOR_ARM[arm] for arm in arm_id_list
|
|
136
|
+
},
|
|
137
|
+
VISP_ARM_POSITION_FOR_ARM={
|
|
138
|
+
json.dumps(arm): ARM_ANGLE_FOR_ARM[arm] for arm in arm_id_list
|
|
139
|
+
},
|
|
140
|
+
VISP_GRATING_CONSTANT_FOR_ARM={
|
|
141
|
+
json.dumps(arm): GRATING_CONSTANT_FOR_ARM[arm] for arm in arm_id_list
|
|
142
|
+
},
|
|
143
|
+
CRYONIRSP_GRATING_ANGLE_FOR_ARM={
|
|
144
|
+
json.dumps(arm): GRATING_ANGLE_FOR_ARM[arm] for arm in arm_id_list
|
|
145
|
+
},
|
|
146
|
+
CRYONIRSP_GRATING_LITTROW_ANGLE_FOR_ARM={
|
|
147
|
+
json.dumps(arm): ARM_ANGLE_FOR_ARM[arm] for arm in arm_id_list
|
|
148
|
+
},
|
|
149
|
+
CRYONIRSP_GRATING_CONSTANT_FOR_ARM={
|
|
150
|
+
json.dumps(arm): GRATING_CONSTANT_FOR_ARM[arm] for arm in arm_id_list
|
|
151
|
+
},
|
|
152
|
+
CRYONIRSP_OD_FILTER_NAME_FOR_ARM={
|
|
153
|
+
json.dumps(arm): CRYO_OD_FILTER_FOR_ARM[arm] for arm in arm_id_list
|
|
154
|
+
},
|
|
155
|
+
CRYONIRSP_SLIT_WIDTH_FOR_ARM={
|
|
156
|
+
json.dumps(arm): CRYO_SLIT_WIDTH_FOR_ARM[arm] for arm in arm_id_list
|
|
157
|
+
},
|
|
158
|
+
DLNIRSP_GRATING_ANGLE_FOR_ARM={
|
|
159
|
+
json.dumps(arm): GRATING_ANGLE_FOR_ARM[arm] for arm in arm_id_list
|
|
160
|
+
},
|
|
161
|
+
DLNIRSP_ARM_POSITION_FOR_ARM={
|
|
162
|
+
json.dumps(arm): ARM_ANGLE_FOR_ARM[arm] for arm in arm_id_list
|
|
163
|
+
},
|
|
164
|
+
DLNIRSP_GRATING_CONSTANT_FOR_ARM={
|
|
165
|
+
json.dumps(arm): GRATING_CONSTANT_FOR_ARM[arm] for arm in arm_id_list
|
|
166
|
+
},
|
|
167
|
+
),
|
|
168
|
+
)
|
|
169
|
+
with WriteTrendOutputs(
|
|
170
|
+
recipe_run_id=recipe_run_id, workflow_name="name", workflow_version="version"
|
|
171
|
+
) as task:
|
|
172
|
+
task.scratch = WorkflowFileSystem(recipe_run_id=recipe_run_id, scratch_base_path=tmp_path)
|
|
173
|
+
assign_input_dataset_doc_to_task(
|
|
174
|
+
task,
|
|
175
|
+
TrendInputDatasetParameterValues(
|
|
176
|
+
trend_fit_options_list=fit_options_list,
|
|
177
|
+
trend_instrument_processing_options_visp=visp_instrument_options_list,
|
|
178
|
+
trend_instrument_processing_options_cryonirsp=cryonirsp_instrument_options_list,
|
|
179
|
+
trend_instrument_processing_options_dlnirsp=dlnirsp_instrument_options_list,
|
|
180
|
+
),
|
|
181
|
+
)
|
|
182
|
+
task.parameters = TrendParameters(
|
|
183
|
+
scratch=task.scratch,
|
|
184
|
+
obs_ip_start_time=task.constants.earliest_ip_start_time,
|
|
185
|
+
instrument=task.constants.instrument,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
yield task
|
|
189
|
+
task._purge()
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def write_pre_output_files_to_task(
|
|
193
|
+
task: WriteTrendOutputs,
|
|
194
|
+
raw_calibration_sequence_data_shape: tuple[int, int, int],
|
|
195
|
+
raw_fit_parameter_shape: tuple[int, ...],
|
|
196
|
+
raw_demod_shape: tuple[int, ...],
|
|
197
|
+
raw_flux_shape: tuple[int, ...],
|
|
198
|
+
raw_dmpd_shape: tuple[int, ...],
|
|
199
|
+
):
|
|
200
|
+
|
|
201
|
+
inst_options_list = task.parameters.instrument_processing_options
|
|
202
|
+
fit_options_list = task.parameters.fit_options_list
|
|
203
|
+
|
|
204
|
+
rng = np.random.default_rng()
|
|
205
|
+
|
|
206
|
+
for arm_id in task.constants.arm_id_list:
|
|
207
|
+
# Calibration sequence
|
|
208
|
+
data = rng.random(raw_calibration_sequence_data_shape, dtype=np.float64)
|
|
209
|
+
task.write(
|
|
210
|
+
data=data,
|
|
211
|
+
tags=[
|
|
212
|
+
TrendTag.intermediate(),
|
|
213
|
+
TrendTag.arm_id(arm_id),
|
|
214
|
+
TrendTag.task_calibration_sequence(),
|
|
215
|
+
],
|
|
216
|
+
encoder=fits_array_encoder,
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
for inst_opt in inst_options_list:
|
|
220
|
+
inst_opt_name = inst_opt.name
|
|
221
|
+
for fit_opt in fit_options_list:
|
|
222
|
+
fit_opt_name = fit_opt.name
|
|
223
|
+
|
|
224
|
+
# DMPD doesn't depend on beam
|
|
225
|
+
data = rng.random(raw_dmpd_shape, dtype=np.float64)
|
|
226
|
+
task.write(
|
|
227
|
+
data=data,
|
|
228
|
+
tags=[
|
|
229
|
+
TrendTag.intermediate(),
|
|
230
|
+
TrendTag.arm_id(arm_id),
|
|
231
|
+
TrendTag.instrument_processing_options(inst_opt_name),
|
|
232
|
+
TrendTag.pac_fit_options(fit_opt_name),
|
|
233
|
+
TrendTag.task_demodulated_polcal_data(),
|
|
234
|
+
],
|
|
235
|
+
encoder=fits_array_encoder,
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
for beam in range(1, num_beams_from_arm_id(arm_id) + 1):
|
|
239
|
+
# Fit parameters
|
|
240
|
+
data = rng.random(raw_fit_parameter_shape, dtype=np.float64)
|
|
241
|
+
task.write(
|
|
242
|
+
data=data,
|
|
243
|
+
tags=[
|
|
244
|
+
TrendTag.intermediate(),
|
|
245
|
+
TrendTag.arm_id(arm_id),
|
|
246
|
+
TrendTag.beam(beam),
|
|
247
|
+
TrendTag.instrument_processing_options(inst_opt_name),
|
|
248
|
+
TrendTag.pac_fit_options(fit_opt_name),
|
|
249
|
+
TrendTag.task_best_fit_parameters(),
|
|
250
|
+
],
|
|
251
|
+
encoder=fits_array_encoder,
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
# Demodulation matrices
|
|
255
|
+
data = rng.random(raw_demod_shape, dtype=np.float64)
|
|
256
|
+
task.write(
|
|
257
|
+
data=data,
|
|
258
|
+
tags=[
|
|
259
|
+
TrendTag.intermediate(),
|
|
260
|
+
TrendTag.arm_id(arm_id),
|
|
261
|
+
TrendTag.beam(beam),
|
|
262
|
+
TrendTag.instrument_processing_options(inst_opt_name),
|
|
263
|
+
TrendTag.pac_fit_options(fit_opt_name),
|
|
264
|
+
TrendTag.task_best_fit_demodulation_matrices(),
|
|
265
|
+
],
|
|
266
|
+
encoder=fits_array_encoder,
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
# Flux
|
|
270
|
+
data = rng.random(raw_flux_shape, dtype=np.float64)
|
|
271
|
+
task.write(
|
|
272
|
+
data=data,
|
|
273
|
+
tags=[
|
|
274
|
+
TrendTag.intermediate(),
|
|
275
|
+
TrendTag.arm_id(arm_id),
|
|
276
|
+
TrendTag.beam(beam),
|
|
277
|
+
TrendTag.instrument_processing_options(inst_opt_name),
|
|
278
|
+
TrendTag.pac_fit_options(fit_opt_name),
|
|
279
|
+
TrendTag.task_best_fit_flux(),
|
|
280
|
+
],
|
|
281
|
+
encoder=fits_array_encoder,
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
# Residuals
|
|
285
|
+
data = rng.random(raw_flux_shape, dtype=np.float64)
|
|
286
|
+
task.write(
|
|
287
|
+
data=data,
|
|
288
|
+
tags=[
|
|
289
|
+
TrendTag.intermediate(),
|
|
290
|
+
TrendTag.arm_id(arm_id),
|
|
291
|
+
TrendTag.beam(beam),
|
|
292
|
+
TrendTag.instrument_processing_options(inst_opt_name),
|
|
293
|
+
TrendTag.pac_fit_options(fit_opt_name),
|
|
294
|
+
TrendTag.task_fit_residuals(),
|
|
295
|
+
],
|
|
296
|
+
encoder=fits_array_encoder,
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
if arm_id != "CI":
|
|
300
|
+
# Single-beam DMPD
|
|
301
|
+
data = rng.random(raw_dmpd_shape, dtype=np.float64)
|
|
302
|
+
task.write(
|
|
303
|
+
data=data,
|
|
304
|
+
tags=[
|
|
305
|
+
TrendTag.intermediate(),
|
|
306
|
+
TrendTag.arm_id(arm_id),
|
|
307
|
+
TrendTag.beam(beam),
|
|
308
|
+
TrendTag.instrument_processing_options(inst_opt_name),
|
|
309
|
+
TrendTag.pac_fit_options(fit_opt_name),
|
|
310
|
+
TrendTag.task_single_beam_demodulated_polcal_data(),
|
|
311
|
+
],
|
|
312
|
+
encoder=fits_array_encoder,
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
@pytest.mark.parametrize(
|
|
317
|
+
"instrument, arm_id_list",
|
|
318
|
+
[
|
|
319
|
+
pytest.param(InstrumentName.visp, [1], id="visp"),
|
|
320
|
+
pytest.param(InstrumentName.cryonirsp, ["CI"], id="cryo-ci"),
|
|
321
|
+
pytest.param(InstrumentName.cryonirsp, ["SP"], id="cryo-sp"),
|
|
322
|
+
pytest.param(InstrumentName.dlnirsp, ["HBand"], id="dlnirsp"),
|
|
323
|
+
pytest.param(InstrumentName.visp, [1, 2, 3], id="visp-multi-arm"),
|
|
324
|
+
pytest.param(InstrumentName.cryonirsp, ["CI", "SP"], id="cryo-multi-arm"),
|
|
325
|
+
pytest.param(InstrumentName.dlnirsp, ["VIS", "JBand", "HBand"], id="dlnirsp-multi-arm"),
|
|
326
|
+
],
|
|
327
|
+
)
|
|
328
|
+
def test_write_trend_task(
|
|
329
|
+
write_trend_task,
|
|
330
|
+
instrument,
|
|
331
|
+
arm_id_list,
|
|
332
|
+
num_polcal_ops,
|
|
333
|
+
num_cs_steps,
|
|
334
|
+
num_modstates,
|
|
335
|
+
fov_shape,
|
|
336
|
+
fit_options_list,
|
|
337
|
+
visp_instrument_options_list,
|
|
338
|
+
cryonirsp_instrument_options_list,
|
|
339
|
+
dlnirsp_instrument_options_list,
|
|
340
|
+
mocker,
|
|
341
|
+
fake_gql_client,
|
|
342
|
+
):
|
|
343
|
+
"""
|
|
344
|
+
Given: A `WriteTrendOutputs` task with some pre-output data on disk
|
|
345
|
+
When: Running the task
|
|
346
|
+
Then: The correct number of outputs is written, and the structure of each is correct
|
|
347
|
+
"""
|
|
348
|
+
mocker.patch(
|
|
349
|
+
"dkist_processing_common.tasks.mixin.metadata_store.GraphQLClient", new=fake_gql_client
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
raw_cs_shape = (num_polcal_ops, num_modstates, num_cs_steps)
|
|
353
|
+
raw_fit_param_shape = (*fov_shape, num_polcal_ops, 15 + num_cs_steps, 3)
|
|
354
|
+
raw_demod_shape = (*fov_shape, 4, num_modstates)
|
|
355
|
+
raw_flux_shape = (*fov_shape, num_modstates, num_cs_steps)
|
|
356
|
+
raw_dmpd_shape = (num_polcal_ops, num_cs_steps, *fov_shape)
|
|
357
|
+
|
|
358
|
+
task = write_trend_task
|
|
359
|
+
|
|
360
|
+
write_pre_output_files_to_task(
|
|
361
|
+
task=task,
|
|
362
|
+
raw_calibration_sequence_data_shape=raw_cs_shape,
|
|
363
|
+
raw_fit_parameter_shape=raw_fit_param_shape,
|
|
364
|
+
raw_demod_shape=raw_demod_shape,
|
|
365
|
+
raw_flux_shape=raw_flux_shape,
|
|
366
|
+
raw_dmpd_shape=raw_dmpd_shape,
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
task()
|
|
370
|
+
|
|
371
|
+
match instrument:
|
|
372
|
+
case InstrumentName.visp:
|
|
373
|
+
inst_opt_list = visp_instrument_options_list
|
|
374
|
+
case InstrumentName.cryonirsp:
|
|
375
|
+
inst_opt_list = cryonirsp_instrument_options_list
|
|
376
|
+
case InstrumentName.dlnirsp:
|
|
377
|
+
inst_opt_list = dlnirsp_instrument_options_list
|
|
378
|
+
case _:
|
|
379
|
+
raise ValueError(
|
|
380
|
+
f"Do not recognize {instrument = }. Your fixtures/parametrization are jacked."
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
for arm_id in arm_id_list:
|
|
384
|
+
num_beams = num_beams_from_arm_id(arm_id)
|
|
385
|
+
expected_cs_shape = (num_polcal_ops, num_modstates, num_cs_steps)
|
|
386
|
+
expected_fit_param_shape = (num_beams, *fov_shape, num_polcal_ops, 15 + num_cs_steps, 3)
|
|
387
|
+
expected_modmat_shape = (num_beams, *fov_shape, num_modstates, 4)
|
|
388
|
+
expected_flux_shape = (num_beams, *fov_shape, num_modstates, num_cs_steps)
|
|
389
|
+
expected_dmpd_shape = (num_polcal_ops, num_cs_steps, *fov_shape)
|
|
390
|
+
expected_num_hdus = 9
|
|
391
|
+
expected_hdu_name_order = [
|
|
392
|
+
"Inventory",
|
|
393
|
+
"Calibration sequence",
|
|
394
|
+
"Fit info",
|
|
395
|
+
"Fit parameters",
|
|
396
|
+
"Modulation matrices",
|
|
397
|
+
"Best fit flux",
|
|
398
|
+
"Best fit residuals",
|
|
399
|
+
"Demodulated polcal data",
|
|
400
|
+
]
|
|
401
|
+
expected_data_shape_list = [
|
|
402
|
+
# None for HDUs that have no data
|
|
403
|
+
None,
|
|
404
|
+
expected_cs_shape,
|
|
405
|
+
None,
|
|
406
|
+
expected_fit_param_shape,
|
|
407
|
+
expected_modmat_shape,
|
|
408
|
+
expected_flux_shape,
|
|
409
|
+
expected_flux_shape,
|
|
410
|
+
expected_dmpd_shape,
|
|
411
|
+
]
|
|
412
|
+
|
|
413
|
+
if arm_id != "CI":
|
|
414
|
+
expected_num_hdus += 2
|
|
415
|
+
expected_hdu_name_order.insert(-1, "Beam 1 demodulated polcal data")
|
|
416
|
+
expected_hdu_name_order.insert(-1, "Beam 2 demodulated polcal data")
|
|
417
|
+
expected_data_shape_list += [expected_dmpd_shape, expected_dmpd_shape]
|
|
418
|
+
|
|
419
|
+
for fit_opt in fit_options_list:
|
|
420
|
+
for inst_opt in inst_opt_list:
|
|
421
|
+
tags = [
|
|
422
|
+
TrendTag.output(),
|
|
423
|
+
TrendTag.frame(),
|
|
424
|
+
TrendTag.arm_id(arm_id),
|
|
425
|
+
TrendTag.pac_fit_options(fit_opt["name"]),
|
|
426
|
+
TrendTag.instrument_processing_options(inst_opt["name"]),
|
|
427
|
+
]
|
|
428
|
+
assert task.count(tags) == 1
|
|
429
|
+
full_path = next(task.read(tags))
|
|
430
|
+
filename = full_path.name
|
|
431
|
+
assert filename.startswith("trend_polcal")
|
|
432
|
+
assert f"_{instrument}_" in filename
|
|
433
|
+
assert f"_{arm_id}_" in filename
|
|
434
|
+
assert f"_{fit_opt["name"]}-{inst_opt["name"]}_" in filename
|
|
435
|
+
assert filename.endswith(".fits")
|
|
436
|
+
|
|
437
|
+
with fits.open(full_path) as hdul:
|
|
438
|
+
assert len(hdul) == expected_num_hdus
|
|
439
|
+
assert isinstance(hdul[0], fits.PrimaryHDU)
|
|
440
|
+
assert [hdu.name for hdu in hdul[1:]] == expected_hdu_name_order
|
|
441
|
+
|
|
442
|
+
for hdu, expected_shape in zip(
|
|
443
|
+
hdul[1:],
|
|
444
|
+
expected_data_shape_list,
|
|
445
|
+
):
|
|
446
|
+
if hdu.name == "Inventory":
|
|
447
|
+
assert "FRAMEVOL" in hdu.header
|
|
448
|
+
else:
|
|
449
|
+
assert "FRAMEVOL" not in hdu.header
|
|
450
|
+
if hdu.data is not None:
|
|
451
|
+
assert isinstance(hdu, fits.CompImageHDU)
|
|
452
|
+
assert hdu.data.dtype is np.dtype(np.float32)
|
|
453
|
+
assert hdu.header["BITPIX"] == -32
|
|
454
|
+
assert (
|
|
455
|
+
hdu.data.shape == expected_shape
|
|
456
|
+
), f"Data shape {hdu.data.shape} does not match expected {expected_shape} for HDU {hdu.name}"
|
|
457
|
+
else:
|
|
458
|
+
assert isinstance(hdu, fits.ImageHDU)
|
|
459
|
+
|
|
460
|
+
# TODO: Test validation
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Trend pipelines for ViSP."""
|
|
2
|
+
|
|
3
|
+
from dkist_processing_common.tasks import Teardown
|
|
4
|
+
from dkist_processing_common.tasks import TransferL0Data
|
|
5
|
+
from dkist_processing_core import Workflow
|
|
6
|
+
|
|
7
|
+
from dkist_processing_trend.models.instrument import VispArms
|
|
8
|
+
from dkist_processing_trend.tasks import DarkCalibration
|
|
9
|
+
from dkist_processing_trend.tasks import GainCalibration
|
|
10
|
+
from dkist_processing_trend.tasks import ParseL0TrendInputData
|
|
11
|
+
from dkist_processing_trend.tasks import PublishTrendCatalogMessages
|
|
12
|
+
from dkist_processing_trend.tasks import RunPacFitter
|
|
13
|
+
from dkist_processing_trend.tasks import TransferTrendData
|
|
14
|
+
from dkist_processing_trend.tasks import WriteTrendOutputs
|
|
15
|
+
from dkist_processing_trend.tasks.arm_task_factory import retreive_arm_task
|
|
16
|
+
from dkist_processing_trend.tasks.visp import VispDemodulatedPolcalData
|
|
17
|
+
from dkist_processing_trend.tasks.visp import VispGeometricCalibration
|
|
18
|
+
from dkist_processing_trend.tasks.visp import VispPrepareFitData
|
|
19
|
+
from dkist_processing_trend.tasks.visp import VispProcessDemodulationMatrices
|
|
20
|
+
|
|
21
|
+
visp_polcal_pipeline = Workflow(
|
|
22
|
+
category="visp",
|
|
23
|
+
input_data="l0",
|
|
24
|
+
output_data="trend",
|
|
25
|
+
detail="polcal",
|
|
26
|
+
workflow_package=__package__,
|
|
27
|
+
)
|
|
28
|
+
visp_polcal_pipeline.add_node(task=TransferL0Data, upstreams=None)
|
|
29
|
+
visp_polcal_pipeline.add_node(task=ParseL0TrendInputData, upstreams=TransferL0Data)
|
|
30
|
+
|
|
31
|
+
output_upstreams = []
|
|
32
|
+
for arm_id in VispArms:
|
|
33
|
+
dark_task = retreive_arm_task(DarkCalibration, arm_id=arm_id)
|
|
34
|
+
gain_task = retreive_arm_task(GainCalibration, arm_id=arm_id)
|
|
35
|
+
geo_task = retreive_arm_task(VispGeometricCalibration, arm_id=arm_id)
|
|
36
|
+
prep_data_task = retreive_arm_task(VispPrepareFitData, arm_id=arm_id)
|
|
37
|
+
run_fit_task = retreive_arm_task(RunPacFitter, arm_id=arm_id)
|
|
38
|
+
process_demod_task = retreive_arm_task(VispProcessDemodulationMatrices, arm_id=arm_id)
|
|
39
|
+
dmpd_task = retreive_arm_task(VispDemodulatedPolcalData, arm_id=arm_id)
|
|
40
|
+
|
|
41
|
+
# Generate demod flow
|
|
42
|
+
visp_polcal_pipeline.add_node(task=dark_task, upstreams=ParseL0TrendInputData)
|
|
43
|
+
visp_polcal_pipeline.add_node(task=gain_task, upstreams=dark_task)
|
|
44
|
+
visp_polcal_pipeline.add_node(task=prep_data_task, upstreams=gain_task)
|
|
45
|
+
visp_polcal_pipeline.add_node(task=run_fit_task, upstreams=prep_data_task)
|
|
46
|
+
|
|
47
|
+
# DMPD flow
|
|
48
|
+
visp_polcal_pipeline.add_node(task=geo_task, upstreams=ParseL0TrendInputData)
|
|
49
|
+
visp_polcal_pipeline.add_node(task=process_demod_task, upstreams=run_fit_task)
|
|
50
|
+
visp_polcal_pipeline.add_node(task=dmpd_task, upstreams=[geo_task, process_demod_task])
|
|
51
|
+
|
|
52
|
+
output_upstreams += [dmpd_task]
|
|
53
|
+
|
|
54
|
+
# Output
|
|
55
|
+
visp_polcal_pipeline.add_node(task=WriteTrendOutputs, upstreams=output_upstreams)
|
|
56
|
+
visp_polcal_pipeline.add_node(task=TransferTrendData, upstreams=WriteTrendOutputs)
|
|
57
|
+
visp_polcal_pipeline.add_node(task=PublishTrendCatalogMessages, upstreams=TransferTrendData)
|
|
58
|
+
visp_polcal_pipeline.add_node(task=Teardown, upstreams=PublishTrendCatalogMessages)
|