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 @@
|
|
|
1
|
+
"""Init."""
|
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
from collections.abc import Iterable
|
|
4
|
+
from dataclasses import asdict
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from dataclasses import field
|
|
7
|
+
from dataclasses import is_dataclass
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from random import randint
|
|
10
|
+
from typing import Any
|
|
11
|
+
from typing import Callable
|
|
12
|
+
from typing import Type
|
|
13
|
+
from typing import TypeVar
|
|
14
|
+
|
|
15
|
+
import numpy as np
|
|
16
|
+
import pytest
|
|
17
|
+
from astropy.io import fits
|
|
18
|
+
from astropy.time import Time
|
|
19
|
+
from astropy.time import TimeDelta
|
|
20
|
+
from dkist_data_simulator.spec122 import Spec122Dataset
|
|
21
|
+
from dkist_header_validator import spec122_validator
|
|
22
|
+
from dkist_header_validator.translator import sanitize_to_spec214_level1
|
|
23
|
+
from dkist_header_validator.translator import translate_spec122_to_spec214_l0
|
|
24
|
+
from dkist_processing_common.codecs.fits import fits_array_encoder
|
|
25
|
+
from dkist_processing_common.tasks import WorkflowTaskBase
|
|
26
|
+
|
|
27
|
+
from dkist_processing_cryonirsp.models.constants import CryonirspConstants
|
|
28
|
+
from dkist_processing_cryonirsp.models.exposure_conditions import AllowableOpticalDensityFilterNames
|
|
29
|
+
from dkist_processing_cryonirsp.models.exposure_conditions import ExposureConditions
|
|
30
|
+
from dkist_processing_cryonirsp.models.parameters import CryonirspParameters
|
|
31
|
+
from dkist_processing_cryonirsp.models.tags import CryonirspTag
|
|
32
|
+
from dkist_processing_cryonirsp.tests.header_models import CryonirspCIHeaders
|
|
33
|
+
from dkist_processing_cryonirsp.tests.header_models import CryonirspHeaders
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def generate_fits_frame(header_generator: Iterable, shape=None) -> fits.HDUList:
|
|
37
|
+
shape = shape or (1, 10, 10)
|
|
38
|
+
generated_header = next(header_generator)
|
|
39
|
+
translated_header = translate_spec122_to_spec214_l0(generated_header)
|
|
40
|
+
del translated_header["COMMENT"]
|
|
41
|
+
hdu = fits.PrimaryHDU(data=np.ones(shape=shape) * 150, header=fits.Header(translated_header))
|
|
42
|
+
return fits.HDUList([hdu])
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def generate_full_cryonirsp_fits_frame(
|
|
46
|
+
header_generator: Iterable, data: np.ndarray | None = None
|
|
47
|
+
) -> fits.HDUList:
|
|
48
|
+
if data is None:
|
|
49
|
+
data = np.ones(shape=(1, 2000, 2560))
|
|
50
|
+
data[0, 1000:, :] *= np.arange(1000)[:, None][::-1, :] # Make beam 2 different and flip it
|
|
51
|
+
generated_header = next(header_generator)
|
|
52
|
+
translated_header = translate_spec122_to_spec214_l0(generated_header)
|
|
53
|
+
del translated_header["COMMENT"]
|
|
54
|
+
hdu = fits.PrimaryHDU(data=data, header=fits.Header(translated_header))
|
|
55
|
+
return fits.HDUList([hdu])
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def generate_214_l0_fits_frame(
|
|
59
|
+
s122_header: fits.Header | dict[str, Any], data: np.ndarray | None = None
|
|
60
|
+
) -> fits.HDUList:
|
|
61
|
+
"""Convert S122 header into 214 L0"""
|
|
62
|
+
if data is None:
|
|
63
|
+
data = np.ones((1, 10, 10))
|
|
64
|
+
translated_header = translate_spec122_to_spec214_l0(s122_header)
|
|
65
|
+
del translated_header["COMMENT"]
|
|
66
|
+
hdu = fits.PrimaryHDU(data=data, header=fits.Header(translated_header))
|
|
67
|
+
return fits.HDUList([hdu])
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def generate_214_l1_fits_frame(
|
|
71
|
+
s122_header: fits.Header, data: np.ndarray | None = None
|
|
72
|
+
) -> fits.HDUList:
|
|
73
|
+
"""Convert S122 header into 214 L1 only.
|
|
74
|
+
|
|
75
|
+
This does NOT include populating all L1 headers, just removing 214 L0 only headers
|
|
76
|
+
|
|
77
|
+
NOTE: The stuff you care about will be in hdulist[1]
|
|
78
|
+
"""
|
|
79
|
+
l0_s214_hdul = generate_214_l0_fits_frame(s122_header, data)
|
|
80
|
+
l0_header = l0_s214_hdul[0].header
|
|
81
|
+
l0_header["DNAXIS"] = 5
|
|
82
|
+
l0_header["DAAXES"] = 2
|
|
83
|
+
l0_header["DEAXES"] = 3
|
|
84
|
+
l1_header = sanitize_to_spec214_level1(input_headers=l0_header)
|
|
85
|
+
hdu = fits.CompImageHDU(header=l1_header, data=l0_s214_hdul[0].data)
|
|
86
|
+
|
|
87
|
+
return fits.HDUList([fits.PrimaryHDU(), hdu])
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@pytest.fixture()
|
|
91
|
+
def init_cryonirsp_constants_db():
|
|
92
|
+
def constants_maker(recipe_run_id: int, constants_obj):
|
|
93
|
+
if is_dataclass(constants_obj):
|
|
94
|
+
constants_obj = asdict(constants_obj)
|
|
95
|
+
constants = CryonirspConstants(recipe_run_id=recipe_run_id, task_name="test")
|
|
96
|
+
constants._purge()
|
|
97
|
+
constants._update(constants_obj)
|
|
98
|
+
return
|
|
99
|
+
|
|
100
|
+
return constants_maker
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@dataclass
|
|
104
|
+
class CryonirspConstantsDb:
|
|
105
|
+
OBS_IP_START_TIME: str = "1999-12-31T23:59:59"
|
|
106
|
+
ARM_ID: str = "SP"
|
|
107
|
+
NUM_MODSTATES: int = 10
|
|
108
|
+
NUM_MAP_SCANS: int = 2
|
|
109
|
+
NUM_BEAMS: int = 2
|
|
110
|
+
NUM_CS_STEPS: int = 18
|
|
111
|
+
NUM_SPECTRAL_BINS: int = 1
|
|
112
|
+
NUM_SPATIAL_BINS: int = 1
|
|
113
|
+
NUM_SCAN_STEPS: int = 1
|
|
114
|
+
NUM_SPATIAL_STEPS: int = 1
|
|
115
|
+
NUM_MEAS: int = 1
|
|
116
|
+
INSTRUMENT: str = "CRYO-NIRSP"
|
|
117
|
+
AVERAGE_CADENCE: float = 10.0
|
|
118
|
+
MINIMUM_CADENCE: float = 10.0
|
|
119
|
+
MAXIMUM_CADENCE: float = 10.0
|
|
120
|
+
VARIANCE_CADENCE: float = 0.0
|
|
121
|
+
WAVELENGTH: float = 1082.0
|
|
122
|
+
LAMP_GAIN_EXPOSURE_CONDITIONS_LIST: tuple[ExposureConditions, ...] = (
|
|
123
|
+
ExposureConditions(100.0, AllowableOpticalDensityFilterNames.OPEN.value),
|
|
124
|
+
)
|
|
125
|
+
SOLAR_GAIN_EXPOSURE_CONDITIONS_LIST: tuple[ExposureConditions, ...] = (
|
|
126
|
+
ExposureConditions(1.0, AllowableOpticalDensityFilterNames.OPEN.value),
|
|
127
|
+
)
|
|
128
|
+
OBSERVE_EXPOSURE_CONDITIONS_LIST: tuple[ExposureConditions, ...] = (
|
|
129
|
+
ExposureConditions(0.01, AllowableOpticalDensityFilterNames.OPEN.value),
|
|
130
|
+
)
|
|
131
|
+
POLCAL_EXPOSURE_CONDITIONS_LIST: tuple[ExposureConditions] | tuple = ()
|
|
132
|
+
NON_DARK_AND_NON_POLCAL_TASK_EXPOSURE_CONDITIONS_LIST: tuple[ExposureConditions, ...] = (
|
|
133
|
+
ExposureConditions(100.0, AllowableOpticalDensityFilterNames.OPEN.value),
|
|
134
|
+
ExposureConditions(1.0, AllowableOpticalDensityFilterNames.OPEN.value),
|
|
135
|
+
ExposureConditions(0.01, AllowableOpticalDensityFilterNames.OPEN.value),
|
|
136
|
+
)
|
|
137
|
+
SPECTRAL_LINE: str = "CRSP Ca II H"
|
|
138
|
+
MODULATOR_SPIN_MODE: str = "Continuous"
|
|
139
|
+
STOKES_PARAMS: tuple[str] = (
|
|
140
|
+
"I",
|
|
141
|
+
"Q",
|
|
142
|
+
"U",
|
|
143
|
+
"V",
|
|
144
|
+
)
|
|
145
|
+
TIME_OBS_LIST: tuple[str] = ()
|
|
146
|
+
CONTRIBUTING_PROPOSAL_IDS: tuple[str] = (
|
|
147
|
+
"PROPID1",
|
|
148
|
+
"PROPID2",
|
|
149
|
+
)
|
|
150
|
+
CONTRIBUTING_EXPERIMENT_IDS: tuple[str] = (
|
|
151
|
+
"EXPERID1",
|
|
152
|
+
"EXPERID2",
|
|
153
|
+
"EXPERID3",
|
|
154
|
+
)
|
|
155
|
+
# These are SP defaults...
|
|
156
|
+
AXIS_1_TYPE: str = "AWAV"
|
|
157
|
+
AXIS_2_TYPE: str = "HPLT-TAN"
|
|
158
|
+
AXIS_3_TYPE: str = "HPLN-TAN"
|
|
159
|
+
ROI_1_ORIGIN_X: int = 0
|
|
160
|
+
ROI_1_ORIGIN_Y: int = 0
|
|
161
|
+
ROI_1_SIZE_X: int = 2048
|
|
162
|
+
ROI_1_SIZE_Y: int = 2048
|
|
163
|
+
GRATING_POSITION_DEG: float = 62.505829779431224
|
|
164
|
+
GRATING_LITTROW_ANGLE_DEG: float = -5.5
|
|
165
|
+
GRATING_CONSTANT: float = 31.6
|
|
166
|
+
SOLAR_GAIN_IP_START_TIME: str = "2021-01-01T00:00:00"
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
@pytest.fixture()
|
|
170
|
+
def recipe_run_id():
|
|
171
|
+
return randint(0, 999999)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
@pytest.fixture()
|
|
175
|
+
def cryonirsp_ci_headers() -> fits.Header:
|
|
176
|
+
"""
|
|
177
|
+
A header with some common by-frame CI keywords
|
|
178
|
+
"""
|
|
179
|
+
ds = CryonirspCIHeaders(dataset_shape=(2, 10, 10), array_shape=(1, 10, 10), time_delta=1)
|
|
180
|
+
header_list = [
|
|
181
|
+
spec122_validator.validate_and_translate_to_214_l0(d.header(), return_type=fits.HDUList)[
|
|
182
|
+
0
|
|
183
|
+
].header
|
|
184
|
+
for d in ds
|
|
185
|
+
]
|
|
186
|
+
|
|
187
|
+
return header_list[0]
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
@pytest.fixture()
|
|
191
|
+
def calibrated_ci_cryonirsp_headers(cryonirsp_ci_headers) -> fits.Header:
|
|
192
|
+
"""
|
|
193
|
+
Same as cryonirsp_ci_headers but with a DATE-END key.
|
|
194
|
+
|
|
195
|
+
Because now that's added during ScienceCal
|
|
196
|
+
"""
|
|
197
|
+
cryonirsp_ci_headers["DATE-END"] = (
|
|
198
|
+
Time(cryonirsp_ci_headers["DATE-BEG"], format="isot", precision=6)
|
|
199
|
+
+ TimeDelta(float(cryonirsp_ci_headers["TEXPOSUR"]) / 1000, format="sec")
|
|
200
|
+
).to_value("isot")
|
|
201
|
+
|
|
202
|
+
return cryonirsp_ci_headers
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
@pytest.fixture()
|
|
206
|
+
def cryonirsp_headers() -> fits.Header:
|
|
207
|
+
"""
|
|
208
|
+
A header with some common by-frame keywords
|
|
209
|
+
"""
|
|
210
|
+
ds = CryonirspHeaders(dataset_shape=(2, 10, 10), array_shape=(1, 10, 10), time_delta=1)
|
|
211
|
+
header_list = [
|
|
212
|
+
spec122_validator.validate_and_translate_to_214_l0(d.header(), return_type=fits.HDUList)[
|
|
213
|
+
0
|
|
214
|
+
].header
|
|
215
|
+
for d in ds
|
|
216
|
+
]
|
|
217
|
+
|
|
218
|
+
return header_list[0]
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
@pytest.fixture()
|
|
222
|
+
def calibrated_cryonirsp_headers(cryonirsp_headers) -> fits.Header:
|
|
223
|
+
"""
|
|
224
|
+
Same as cryonirsp_headers but with a DATE-END key.
|
|
225
|
+
|
|
226
|
+
Because now that's added during ScienceCal
|
|
227
|
+
"""
|
|
228
|
+
cryonirsp_headers["DATE-END"] = (
|
|
229
|
+
Time(cryonirsp_headers["DATE-BEG"], format="isot", precision=6)
|
|
230
|
+
+ TimeDelta(float(cryonirsp_headers["TEXPOSUR"]) / 1000, format="sec")
|
|
231
|
+
).to_value("isot")
|
|
232
|
+
|
|
233
|
+
return cryonirsp_headers
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
@dataclass
|
|
237
|
+
class WavelengthParameter:
|
|
238
|
+
values: tuple
|
|
239
|
+
wavelength: tuple = (1074.5, 1074.7, 1079.8, 1083) # This must always be in order
|
|
240
|
+
|
|
241
|
+
def __hash__(self):
|
|
242
|
+
return hash((self.values, self.wavelength))
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
@dataclass
|
|
246
|
+
class FileParameter:
|
|
247
|
+
"""For parameters that are files on disk."""
|
|
248
|
+
|
|
249
|
+
param_path: str
|
|
250
|
+
is_file: bool = True
|
|
251
|
+
objectKey: str = "not_used_because_its_already_converted"
|
|
252
|
+
bucket: str = "not_used_because_we_dont_transfer"
|
|
253
|
+
# Note: we have these as already-parsed file parameter (i.e., no "__file__") mainly because it allows us to have
|
|
254
|
+
# parameter files that are outside of the workflow basepath (where they would not be able to be tagged with
|
|
255
|
+
# PARAMETER_FILE). This is a pattern that we see in grogu testing.
|
|
256
|
+
# A downside of this approach is that we are slightly more fragile to changes in the underlying __file__ parsing
|
|
257
|
+
# in `*-common`. Apologies to any future devs who run into this problem. To fix it you'll need to make
|
|
258
|
+
# downstream fixtures aware of the actual files so they can be tagged prior to the instantiation of the
|
|
259
|
+
# Parameter object on some Task.
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
# These constants are used to prevent name errors in _create_parameter_files
|
|
263
|
+
# and in CryonirspTestingParameters
|
|
264
|
+
LINEARIZATION_THRESHOLDS_CI = "cryonirsp_linearization_thresholds_ci.npy"
|
|
265
|
+
LINEARIZATION_THRESHOLDS_SP = "cryonirsp_linearization_thresholds_sp.npy"
|
|
266
|
+
SOLAR_ATLAS = "cryonirsp_solar_atlas.npy"
|
|
267
|
+
TELLURIC_ATLAS = "cryonirsp_telluric_atlas.npy"
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def _create_parameter_files(param_path: Path) -> None:
|
|
271
|
+
# linearization thresholds
|
|
272
|
+
thresh = np.ones((10, 10), dtype=np.float64) * 100.0
|
|
273
|
+
np.save(os.path.join(param_path, LINEARIZATION_THRESHOLDS_CI), thresh)
|
|
274
|
+
np.save(os.path.join(param_path, LINEARIZATION_THRESHOLDS_SP), thresh)
|
|
275
|
+
|
|
276
|
+
# solar and telluric atlases
|
|
277
|
+
atlas_wavelengths = range(300, 16600)
|
|
278
|
+
atlas_transmission = np.random.rand(16300)
|
|
279
|
+
atlas = [atlas_wavelengths, atlas_transmission]
|
|
280
|
+
np.save(os.path.join(param_path, SOLAR_ATLAS), atlas)
|
|
281
|
+
np.save(os.path.join(param_path, TELLURIC_ATLAS), atlas)
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
TestingParameters = TypeVar("TestingParameters", bound="CryonirspTestingParameters")
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def cryonirsp_testing_parameters_factory(
|
|
288
|
+
param_path: Path | str = "", create_files: bool = True
|
|
289
|
+
) -> TestingParameters:
|
|
290
|
+
"""Create the InputDatasetParameterValue objects and write the parameter files."""
|
|
291
|
+
if isinstance(param_path, str):
|
|
292
|
+
param_path = Path(param_path)
|
|
293
|
+
absolute_path = param_path.absolute()
|
|
294
|
+
|
|
295
|
+
if create_files:
|
|
296
|
+
_create_parameter_files(absolute_path)
|
|
297
|
+
|
|
298
|
+
@dataclass
|
|
299
|
+
class CryonirspTestingParameters:
|
|
300
|
+
cryonirsp_polcal_num_spatial_bins: int = 1
|
|
301
|
+
cryonirsp_polcal_num_spectral_bins: int = 1
|
|
302
|
+
cryonirsp_polcal_pac_fit_mode: str = "use_M12_I_sys_per_step"
|
|
303
|
+
cryonirsp_polcal_pac_init_set: str = "OCCal_VIS"
|
|
304
|
+
cryonirsp_geo_upsample_factor: int = 100
|
|
305
|
+
cryonirsp_geo_max_shift: int = 80
|
|
306
|
+
cryonirsp_geo_poly_fit_order: int = 3
|
|
307
|
+
cryonirsp_geo_long_axis_gradient_displacement: int = 4
|
|
308
|
+
cryonirsp_geo_strip_long_axis_size_fraction: float = 0.8
|
|
309
|
+
cryonirsp_geo_strip_short_axis_size_fraction: float = 0.1
|
|
310
|
+
cryonirsp_geo_strip_spectral_offset_size_fraction: float = 0.25
|
|
311
|
+
cryonirsp_solar_characteristic_spatial_normalization_percentile: float = 80.0
|
|
312
|
+
cryonirsp_max_cs_step_time_sec: float = 180.0
|
|
313
|
+
cryonirsp_beam_boundaries_smoothing_disk_size: int = 3
|
|
314
|
+
cryonirsp_beam_boundaries_upsample_factor: int = 10
|
|
315
|
+
cryonirsp_beam_boundaries_sp_beam_transition_region_size_fraction: float = 0.05
|
|
316
|
+
cryonirsp_bad_pixel_map_median_filter_size_sp: list[int] = field(
|
|
317
|
+
default_factory=lambda: [20, 1]
|
|
318
|
+
)
|
|
319
|
+
cryonirsp_bad_pixel_map_median_filter_size_ci: list[int] = field(
|
|
320
|
+
default_factory=lambda: [5, 5]
|
|
321
|
+
)
|
|
322
|
+
cryonirsp_bad_pixel_map_threshold_factor: float = 5.0
|
|
323
|
+
cryonirsp_corrections_bad_pixel_median_filter_size: int = 8
|
|
324
|
+
cryonirsp_corrections_bad_pixel_fraction_threshold: float = 0.03
|
|
325
|
+
cryonirsp_fringe_correction_on: bool = True
|
|
326
|
+
cryonirsp_fringe_correction_spectral_filter_size: list[int] = field(
|
|
327
|
+
default_factory=lambda: [1, 20]
|
|
328
|
+
)
|
|
329
|
+
cryonirsp_fringe_correction_spatial_filter_size: list[int] = field(
|
|
330
|
+
default_factory=lambda: [5, 1]
|
|
331
|
+
)
|
|
332
|
+
cryonirsp_fringe_correction_lowpass_cutoff_period: float = 40.0
|
|
333
|
+
cryonirsp_linearization_thresholds_ci: FileParameter = field(
|
|
334
|
+
default_factory=lambda: FileParameter(
|
|
335
|
+
param_path=str(absolute_path / LINEARIZATION_THRESHOLDS_CI)
|
|
336
|
+
)
|
|
337
|
+
)
|
|
338
|
+
cryonirsp_linearization_polyfit_coeffs_ci: list[float] = field(
|
|
339
|
+
default_factory=lambda: [1.0, 0.0, 0.0, 0.0]
|
|
340
|
+
)
|
|
341
|
+
cryonirsp_linearization_thresholds_sp: FileParameter = field(
|
|
342
|
+
default_factory=lambda: FileParameter(
|
|
343
|
+
param_path=str(absolute_path / LINEARIZATION_THRESHOLDS_SP)
|
|
344
|
+
)
|
|
345
|
+
)
|
|
346
|
+
cryonirsp_linearization_polyfit_coeffs_sp: list[float] = field(
|
|
347
|
+
default_factory=lambda: [1.0, 0.0, 0.0, 0.0]
|
|
348
|
+
)
|
|
349
|
+
cryonirsp_linearization_max_memory_gb: float = 4.0
|
|
350
|
+
cryonirsp_linearization_optical_density_filter_attenuation_g278: WavelengthParameter = (
|
|
351
|
+
WavelengthParameter(values=(-1.64, -1.64, -1.64, -1.64))
|
|
352
|
+
)
|
|
353
|
+
cryonirsp_linearization_optical_density_filter_attenuation_g358: WavelengthParameter = (
|
|
354
|
+
WavelengthParameter(values=(-3.75, -3.75, -3.75, -3.75))
|
|
355
|
+
)
|
|
356
|
+
cryonirsp_linearization_optical_density_filter_attenuation_g408: WavelengthParameter = (
|
|
357
|
+
WavelengthParameter(values=(-4.26, -4.26, -4.26, -4.26))
|
|
358
|
+
)
|
|
359
|
+
cryonirsp_solar_atlas: FileParameter = field(
|
|
360
|
+
default_factory=lambda: FileParameter(param_path=str(absolute_path / SOLAR_ATLAS))
|
|
361
|
+
)
|
|
362
|
+
cryonirsp_telluric_atlas: FileParameter = field(
|
|
363
|
+
default_factory=lambda: FileParameter(param_path=str(absolute_path / TELLURIC_ATLAS))
|
|
364
|
+
)
|
|
365
|
+
cryonirsp_camera_mirror_focal_length_mm: float = 932.0
|
|
366
|
+
cryonirsp_pixel_pitch_micron: float = 18.0
|
|
367
|
+
|
|
368
|
+
return CryonirspTestingParameters
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
@pytest.fixture(scope="session")
|
|
372
|
+
def testing_wavelength() -> float:
|
|
373
|
+
return 1079.6
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
@pytest.fixture(scope="session")
|
|
377
|
+
def testing_obs_ip_start_time() -> str:
|
|
378
|
+
return "1946-11-20T12:34:56"
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
@pytest.fixture(scope="session")
|
|
382
|
+
def input_dataset_document_simple_parameters_part():
|
|
383
|
+
def get_input_dataset_parameters_part(parameters):
|
|
384
|
+
parameters_list = []
|
|
385
|
+
|
|
386
|
+
value_id = randint(1000, 2000)
|
|
387
|
+
for pn, pv in asdict(parameters).items():
|
|
388
|
+
values = [
|
|
389
|
+
{
|
|
390
|
+
"parameterValueId": value_id,
|
|
391
|
+
"parameterValue": json.dumps(pv),
|
|
392
|
+
"parameterValueStartDate": "1946-11-20", # Remember Duane Allman
|
|
393
|
+
}
|
|
394
|
+
]
|
|
395
|
+
parameter = {"parameterName": pn, "parameterValues": values}
|
|
396
|
+
parameters_list.append(parameter)
|
|
397
|
+
return parameters_list
|
|
398
|
+
|
|
399
|
+
return get_input_dataset_parameters_part
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
@pytest.fixture(scope="session")
|
|
403
|
+
def assign_input_dataset_doc_to_task(
|
|
404
|
+
input_dataset_document_simple_parameters_part,
|
|
405
|
+
testing_wavelength,
|
|
406
|
+
testing_obs_ip_start_time,
|
|
407
|
+
):
|
|
408
|
+
def update_task(
|
|
409
|
+
task,
|
|
410
|
+
parameters,
|
|
411
|
+
parameter_class=CryonirspParameters,
|
|
412
|
+
arm_id: str = "SP",
|
|
413
|
+
obs_ip_start_time=testing_obs_ip_start_time,
|
|
414
|
+
):
|
|
415
|
+
doc_path = task.scratch.workflow_base_path / "dataset_doc.json"
|
|
416
|
+
with open(doc_path, "w") as f:
|
|
417
|
+
f.write(json.dumps(input_dataset_document_simple_parameters_part(parameters)))
|
|
418
|
+
task.tag(doc_path, CryonirspTag.input_dataset_parameters())
|
|
419
|
+
task.parameters = parameter_class(
|
|
420
|
+
task.input_dataset_parameters,
|
|
421
|
+
wavelength=testing_wavelength,
|
|
422
|
+
arm_id=arm_id,
|
|
423
|
+
obs_ip_start_time=obs_ip_start_time,
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
return update_task
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
def _write_frames_to_task(
|
|
430
|
+
task: Type[WorkflowTaskBase],
|
|
431
|
+
frame_generator: Spec122Dataset,
|
|
432
|
+
change_translated_headers: Callable[[fits.Header], fits.Header] = lambda x: x,
|
|
433
|
+
tag_ramp_frames: Callable[[fits.Header], list[str]] = lambda x: [],
|
|
434
|
+
extra_tags: list[str] | None = None,
|
|
435
|
+
tag_func: Callable[[CryonirspHeaders], list[str]] = lambda x: [],
|
|
436
|
+
):
|
|
437
|
+
if not extra_tags:
|
|
438
|
+
extra_tags = []
|
|
439
|
+
tags = [CryonirspTag.frame()] + extra_tags
|
|
440
|
+
|
|
441
|
+
num_frames = 0
|
|
442
|
+
|
|
443
|
+
frame = next(frame_generator)
|
|
444
|
+
|
|
445
|
+
header = frame.header()
|
|
446
|
+
data = frame.data
|
|
447
|
+
frame_tags = tags + tag_func(frame)
|
|
448
|
+
translated_header = fits.Header(translate_spec122_to_spec214_l0(header))
|
|
449
|
+
translated_header = change_translated_headers(translated_header)
|
|
450
|
+
ramp_tags = tag_ramp_frames(translated_header)
|
|
451
|
+
frame_tags = frame_tags + ramp_tags
|
|
452
|
+
|
|
453
|
+
task.write(data=data, header=translated_header, tags=frame_tags, encoder=fits_array_encoder)
|
|
454
|
+
num_frames += 1
|
|
455
|
+
|
|
456
|
+
return num_frames
|