dkist-processing-cryonirsp 1.3.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of dkist-processing-cryonirsp might be problematic. Click here for more details.

Files changed (111) hide show
  1. changelog/.gitempty +0 -0
  2. dkist_processing_cryonirsp/__init__.py +11 -0
  3. dkist_processing_cryonirsp/config.py +12 -0
  4. dkist_processing_cryonirsp/models/__init__.py +1 -0
  5. dkist_processing_cryonirsp/models/constants.py +248 -0
  6. dkist_processing_cryonirsp/models/exposure_conditions.py +26 -0
  7. dkist_processing_cryonirsp/models/parameters.py +296 -0
  8. dkist_processing_cryonirsp/models/tags.py +168 -0
  9. dkist_processing_cryonirsp/models/task_name.py +14 -0
  10. dkist_processing_cryonirsp/parsers/__init__.py +1 -0
  11. dkist_processing_cryonirsp/parsers/cryonirsp_l0_fits_access.py +111 -0
  12. dkist_processing_cryonirsp/parsers/cryonirsp_l1_fits_access.py +30 -0
  13. dkist_processing_cryonirsp/parsers/exposure_conditions.py +163 -0
  14. dkist_processing_cryonirsp/parsers/map_repeats.py +40 -0
  15. dkist_processing_cryonirsp/parsers/measurements.py +55 -0
  16. dkist_processing_cryonirsp/parsers/modstates.py +31 -0
  17. dkist_processing_cryonirsp/parsers/optical_density_filters.py +40 -0
  18. dkist_processing_cryonirsp/parsers/polarimetric_check.py +120 -0
  19. dkist_processing_cryonirsp/parsers/scan_step.py +412 -0
  20. dkist_processing_cryonirsp/parsers/time.py +80 -0
  21. dkist_processing_cryonirsp/parsers/wavelength.py +26 -0
  22. dkist_processing_cryonirsp/tasks/__init__.py +19 -0
  23. dkist_processing_cryonirsp/tasks/assemble_movie.py +202 -0
  24. dkist_processing_cryonirsp/tasks/bad_pixel_map.py +96 -0
  25. dkist_processing_cryonirsp/tasks/beam_boundaries_base.py +279 -0
  26. dkist_processing_cryonirsp/tasks/ci_beam_boundaries.py +55 -0
  27. dkist_processing_cryonirsp/tasks/ci_science.py +169 -0
  28. dkist_processing_cryonirsp/tasks/cryonirsp_base.py +67 -0
  29. dkist_processing_cryonirsp/tasks/dark.py +98 -0
  30. dkist_processing_cryonirsp/tasks/gain.py +251 -0
  31. dkist_processing_cryonirsp/tasks/instrument_polarization.py +447 -0
  32. dkist_processing_cryonirsp/tasks/l1_output_data.py +44 -0
  33. dkist_processing_cryonirsp/tasks/linearity_correction.py +582 -0
  34. dkist_processing_cryonirsp/tasks/make_movie_frames.py +302 -0
  35. dkist_processing_cryonirsp/tasks/mixin/__init__.py +1 -0
  36. dkist_processing_cryonirsp/tasks/mixin/beam_access.py +52 -0
  37. dkist_processing_cryonirsp/tasks/mixin/corrections.py +177 -0
  38. dkist_processing_cryonirsp/tasks/mixin/intermediate_frame.py +193 -0
  39. dkist_processing_cryonirsp/tasks/mixin/linearized_frame.py +309 -0
  40. dkist_processing_cryonirsp/tasks/mixin/shift_measurements.py +297 -0
  41. dkist_processing_cryonirsp/tasks/parse.py +281 -0
  42. dkist_processing_cryonirsp/tasks/quality_metrics.py +271 -0
  43. dkist_processing_cryonirsp/tasks/science_base.py +511 -0
  44. dkist_processing_cryonirsp/tasks/sp_beam_boundaries.py +270 -0
  45. dkist_processing_cryonirsp/tasks/sp_dispersion_axis_correction.py +484 -0
  46. dkist_processing_cryonirsp/tasks/sp_geometric.py +585 -0
  47. dkist_processing_cryonirsp/tasks/sp_science.py +299 -0
  48. dkist_processing_cryonirsp/tasks/sp_solar_gain.py +475 -0
  49. dkist_processing_cryonirsp/tasks/trial_output_data.py +61 -0
  50. dkist_processing_cryonirsp/tasks/write_l1.py +1033 -0
  51. dkist_processing_cryonirsp/tests/__init__.py +1 -0
  52. dkist_processing_cryonirsp/tests/conftest.py +456 -0
  53. dkist_processing_cryonirsp/tests/header_models.py +592 -0
  54. dkist_processing_cryonirsp/tests/local_trial_workflows/__init__.py +0 -0
  55. dkist_processing_cryonirsp/tests/local_trial_workflows/l0_cals_only.py +541 -0
  56. dkist_processing_cryonirsp/tests/local_trial_workflows/l0_to_l1.py +615 -0
  57. dkist_processing_cryonirsp/tests/local_trial_workflows/linearize_only.py +96 -0
  58. dkist_processing_cryonirsp/tests/local_trial_workflows/local_trial_helpers.py +592 -0
  59. dkist_processing_cryonirsp/tests/test_assemble_movie.py +144 -0
  60. dkist_processing_cryonirsp/tests/test_assemble_qualilty.py +517 -0
  61. dkist_processing_cryonirsp/tests/test_bad_pixel_maps.py +115 -0
  62. dkist_processing_cryonirsp/tests/test_ci_beam_boundaries.py +106 -0
  63. dkist_processing_cryonirsp/tests/test_ci_science.py +355 -0
  64. dkist_processing_cryonirsp/tests/test_corrections.py +126 -0
  65. dkist_processing_cryonirsp/tests/test_cryo_base.py +202 -0
  66. dkist_processing_cryonirsp/tests/test_cryo_constants.py +76 -0
  67. dkist_processing_cryonirsp/tests/test_dark.py +287 -0
  68. dkist_processing_cryonirsp/tests/test_gain.py +278 -0
  69. dkist_processing_cryonirsp/tests/test_instrument_polarization.py +531 -0
  70. dkist_processing_cryonirsp/tests/test_linearity_correction.py +245 -0
  71. dkist_processing_cryonirsp/tests/test_make_movie_frames.py +111 -0
  72. dkist_processing_cryonirsp/tests/test_parameters.py +266 -0
  73. dkist_processing_cryonirsp/tests/test_parse.py +1439 -0
  74. dkist_processing_cryonirsp/tests/test_quality.py +203 -0
  75. dkist_processing_cryonirsp/tests/test_sp_beam_boundaries.py +112 -0
  76. dkist_processing_cryonirsp/tests/test_sp_dispersion_axis_correction.py +155 -0
  77. dkist_processing_cryonirsp/tests/test_sp_geometric.py +319 -0
  78. dkist_processing_cryonirsp/tests/test_sp_make_movie_frames.py +121 -0
  79. dkist_processing_cryonirsp/tests/test_sp_science.py +483 -0
  80. dkist_processing_cryonirsp/tests/test_sp_solar.py +198 -0
  81. dkist_processing_cryonirsp/tests/test_trial_create_quality_report.py +79 -0
  82. dkist_processing_cryonirsp/tests/test_trial_output_data.py +251 -0
  83. dkist_processing_cryonirsp/tests/test_workflows.py +9 -0
  84. dkist_processing_cryonirsp/tests/test_write_l1.py +436 -0
  85. dkist_processing_cryonirsp/workflows/__init__.py +2 -0
  86. dkist_processing_cryonirsp/workflows/ci_l0_processing.py +77 -0
  87. dkist_processing_cryonirsp/workflows/sp_l0_processing.py +84 -0
  88. dkist_processing_cryonirsp/workflows/trial_workflows.py +190 -0
  89. dkist_processing_cryonirsp-1.3.4.dist-info/METADATA +194 -0
  90. dkist_processing_cryonirsp-1.3.4.dist-info/RECORD +111 -0
  91. dkist_processing_cryonirsp-1.3.4.dist-info/WHEEL +5 -0
  92. dkist_processing_cryonirsp-1.3.4.dist-info/top_level.txt +4 -0
  93. docs/Makefile +134 -0
  94. docs/bad_pixel_calibration.rst +47 -0
  95. docs/beam_angle_calculation.rst +53 -0
  96. docs/beam_boundary_computation.rst +88 -0
  97. docs/changelog.rst +7 -0
  98. docs/ci_science_calibration.rst +33 -0
  99. docs/conf.py +52 -0
  100. docs/index.rst +21 -0
  101. docs/l0_to_l1_cryonirsp_ci-full-trial.rst +10 -0
  102. docs/l0_to_l1_cryonirsp_ci.rst +10 -0
  103. docs/l0_to_l1_cryonirsp_sp-full-trial.rst +10 -0
  104. docs/l0_to_l1_cryonirsp_sp.rst +10 -0
  105. docs/linearization.rst +43 -0
  106. docs/make.bat +170 -0
  107. docs/requirements.txt +1 -0
  108. docs/requirements_table.rst +8 -0
  109. docs/scientific_changelog.rst +10 -0
  110. docs/sp_science_calibration.rst +59 -0
  111. licenses/LICENSE.rst +11 -0
changelog/.gitempty ADDED
File without changes
@@ -0,0 +1,11 @@
1
+ """Init."""
2
+ from importlib.metadata import PackageNotFoundError
3
+ from importlib.metadata import version
4
+
5
+ from dkist_service_configuration.logging import logger # first import to set logging.BasicConfig
6
+
7
+ try:
8
+ __version__ = version(distribution_name=__name__)
9
+ except PackageNotFoundError:
10
+ # package is not installed
11
+ __version__ = "unknown"
@@ -0,0 +1,12 @@
1
+ """Configuration for the dkist-processing-cryonirsp package and the logging thereof."""
2
+ from dkist_processing_common.config import DKISTProcessingCommonConfiguration
3
+
4
+
5
+ class DKISTProcessingCryoNIRSPConfigurations(DKISTProcessingCommonConfiguration):
6
+ """Configurations custom to the dkist-processing-cryonirsp package."""
7
+
8
+ pass # nothing custom yet
9
+
10
+
11
+ dkist_processing_cryonirsp_configurations = DKISTProcessingCryoNIRSPConfigurations()
12
+ dkist_processing_cryonirsp_configurations.log_configurations()
@@ -0,0 +1 @@
1
+ """Init."""
@@ -0,0 +1,248 @@
1
+ """CryoNIRSP additions to common constants."""
2
+ from enum import Enum
3
+ from enum import unique
4
+
5
+ from dkist_processing_common.models.constants import BudName
6
+ from dkist_processing_common.models.constants import ConstantsBase
7
+
8
+ from dkist_processing_cryonirsp.models.exposure_conditions import ExposureConditions
9
+
10
+
11
+ @unique
12
+ class CryonirspBudName(Enum):
13
+ """Names to be used for CryoNIRSP buds."""
14
+
15
+ arm_id = "ARM_ID"
16
+ num_beams = "NUM_BEAMS"
17
+ num_scan_steps = "NUM_SCAN_STEPS"
18
+ num_map_scans = "NUM_MAP_SCANS"
19
+ num_modstates = "NUM_MODSTATES"
20
+ wavelength = "WAVELENGTH"
21
+ wave_min = "WAVE_MIN"
22
+ wave_max = "WAVE_MAX"
23
+ grating_position_deg = "GRATING_POSITION_DEG"
24
+ grating_littrow_angle_deg = "GRATING_LITTROW_ANGLE_DEG"
25
+ grating_constant = "GRATING_CONSTANT"
26
+ camera_readout_mode = "CAM_READOUT_MODE"
27
+ num_meas = "NUM_MEAS"
28
+ time_obs_list = "TIME_OBS_LIST"
29
+ exposure_conditions_list = "EXPOSURE_CONDITIONS_LIST"
30
+ dark_frame_exposure_conditions_list = "DARK_FRAME_EXPOSURE_CONDITIONS_LIST"
31
+ lamp_gain_exposure_conditions_list = "LAMP_GAIN_EXPOSURE_CONDITIONS_LIST"
32
+ solar_gain_exposure_conditions_list = "SOLAR_GAIN_EXPOSURE_CONDITIONS_LIST"
33
+ polcal_exposure_conditions_list = "POLCAL_EXPOSURE_CONDITIONS_LIST"
34
+ observe_exposure_conditions_list = "OBSERVE_EXPOSURE_CONDITIONS_LIST"
35
+ non_dark_and_non_polcal_task_exposure_conditions_list = (
36
+ "NON_DARK_AND_NON_POLCAL_TASK_EXPOSURE_CONDITIONS_LIST"
37
+ )
38
+ picky_dark_exposure_conditions_list = "PICKY_DARK_EXPOSURE_CONDITIONS_LIST"
39
+ modulator_spin_mode = "MODULATOR_SPIN_MODE"
40
+ axis_1_type = "AXIS_1_TYPE"
41
+ axis_2_type = "AXIS_2_TYPE"
42
+ axis_3_type = "AXIS_3_TYPE"
43
+ roi_1_origin_x = "ROI_1_ORIGIN_X"
44
+ roi_1_origin_y = "ROI_1_ORIGIN_Y"
45
+ roi_1_size_x = "ROI_1_SIZE_X"
46
+ roi_1_size_y = "ROI_1_SIZE_Y"
47
+ optical_density_filter_picky_bud = "OPTICAL_DENSITY_FILTER_PICKY_BUD"
48
+ solar_gain_ip_start_time = "SOLAR_GAIN_IP_START_TIME"
49
+
50
+
51
+ class CryonirspConstants(ConstantsBase):
52
+ """CryoNIRSP specific constants to add to the common constants."""
53
+
54
+ @property
55
+ def arm_id(self) -> str:
56
+ """Arm used to record the data, SP or CI."""
57
+ return self._db_dict[CryonirspBudName.arm_id.value]
58
+
59
+ @property
60
+ def num_beams(self) -> int:
61
+ """Determine the number of beams present in the data."""
62
+ if self.arm_id == "SP":
63
+ return 2
64
+ else:
65
+ return 1
66
+
67
+ @property
68
+ def num_scan_steps(self) -> int:
69
+ """Determine the number of scan steps."""
70
+ return self._db_dict[CryonirspBudName.num_scan_steps.value]
71
+
72
+ @property
73
+ def num_map_scans(self) -> int:
74
+ """Determine the number of scan steps."""
75
+ return self._db_dict[CryonirspBudName.num_map_scans.value]
76
+
77
+ @property
78
+ def wavelength(self) -> float:
79
+ """Wavelength."""
80
+ return self._db_dict[CryonirspBudName.wavelength.value]
81
+
82
+ @property
83
+ def wave_min(self) -> float:
84
+ """Wavelength minimum."""
85
+ return self._db_dict[CryonirspBudName.wave_min.value]
86
+
87
+ @property
88
+ def wave_max(self) -> float:
89
+ """Wavelength maximum."""
90
+ return self._db_dict[CryonirspBudName.wave_max.value]
91
+
92
+ @property
93
+ def solar_gain_ip_start_time(self) -> str:
94
+ """Solar gain IP start time."""
95
+ return self._db_dict[CryonirspBudName.solar_gain_ip_start_time.value]
96
+
97
+ @property
98
+ def grating_position_deg(self) -> float:
99
+ """Grating position angle (deg)."""
100
+ return self._db_dict[CryonirspBudName.grating_position_deg.value]
101
+
102
+ @property
103
+ def grating_littrow_angle_deg(self) -> float:
104
+ """Grating littrow angle (deg)."""
105
+ return self._db_dict[CryonirspBudName.grating_littrow_angle_deg.value]
106
+
107
+ @property
108
+ def grating_constant(self) -> float:
109
+ """Grating constant."""
110
+ return self._db_dict[CryonirspBudName.grating_constant.value]
111
+
112
+ @property
113
+ def camera_readout_mode(self) -> str:
114
+ """Determine the readout mode of the camera."""
115
+ return self._db_dict[CryonirspBudName.camera_readout_mode.value]
116
+
117
+ @property
118
+ def num_meas(self) -> int:
119
+ """Determine the number of measurements in dataset."""
120
+ return self._db_dict[CryonirspBudName.num_meas.value]
121
+
122
+ @property
123
+ def time_obs_list(self) -> tuple[str]:
124
+ """Construct a sorted tuple of all the dateobs for this dataset."""
125
+ return self._db_dict[CryonirspBudName.time_obs_list.value]
126
+
127
+ @property
128
+ def exposure_conditions_list(self) -> [ExposureConditions]:
129
+ """Construct a list of ExposureConditions tuples for the dataset."""
130
+ raw_conditions: list[list[int, str]] = self._db_dict[
131
+ CryonirspBudName.exposure_conditions.value
132
+ ]
133
+ conditions = [ExposureConditions(*item) for item in raw_conditions]
134
+ return conditions
135
+
136
+ @property
137
+ def dark_exposure_conditions_list(self) -> [ExposureConditions]:
138
+ """Construct a list of dark frame ExposureConditions tuples for the dataset."""
139
+ raw_conditions: list[list[int, str]] = self._db_dict[
140
+ CryonirspBudName.dark_frame_exposure_conditions_list.value
141
+ ]
142
+ conditions = [ExposureConditions(*item) for item in raw_conditions]
143
+ return conditions
144
+
145
+ @property
146
+ def lamp_gain_exposure_conditions_list(self) -> [ExposureConditions]:
147
+ """Construct a list of lamp gain ExposureConditions tuples for the dataset."""
148
+ raw_conditions: list[list[int, str]] = self._db_dict[
149
+ CryonirspBudName.lamp_gain_exposure_conditions_list.value
150
+ ]
151
+ conditions = [ExposureConditions(*item) for item in raw_conditions]
152
+ return conditions
153
+
154
+ @property
155
+ def solar_gain_exposure_conditions_list(self) -> [ExposureConditions]:
156
+ """Construct a list of solar gain ExposureConditions tuples for the dataset."""
157
+ raw_conditions: list[list[int, str]] = self._db_dict[
158
+ CryonirspBudName.solar_gain_exposure_conditions_list.value
159
+ ]
160
+ conditions = [ExposureConditions(*item) for item in raw_conditions]
161
+ return conditions
162
+
163
+ @property
164
+ def observe_exposure_conditions_list(self) -> [ExposureConditions]:
165
+ """Construct a list of observe ExposureConditions tuples for the dataset."""
166
+ raw_conditions: list[list[int, str]] = self._db_dict[
167
+ CryonirspBudName.observe_exposure_conditions_list.value
168
+ ]
169
+ conditions = [ExposureConditions(*item) for item in raw_conditions]
170
+ return conditions
171
+
172
+ @property
173
+ def polcal_exposure_conditions_list(self) -> [ExposureConditions]:
174
+ """Construct a list of polcal ExposureConditions tuples for the dataset."""
175
+ if self.correct_for_polarization:
176
+ raw_conditions: list[list[int, str]] = self._db_dict[
177
+ CryonirspBudName.polcal_exposure_conditions_list.value
178
+ ]
179
+ conditions = [ExposureConditions(*item) for item in raw_conditions]
180
+ return conditions
181
+ else:
182
+ return []
183
+
184
+ @property
185
+ def non_dark_and_non_polcal_task_exposure_conditions_list(self) -> [ExposureConditions]:
186
+ """Return a list of all exposure times required for all tasks other than dark and polcal."""
187
+ raw_conditions: list[list[int, str]] = self._db_dict[
188
+ CryonirspBudName.non_dark_and_non_polcal_task_exposure_conditions_list.value
189
+ ]
190
+ conditions = [ExposureConditions(*item) for item in raw_conditions]
191
+ return conditions
192
+
193
+ @property
194
+ def num_modstates(self) -> int:
195
+ """Find the number of modulation states."""
196
+ return self._db_dict[CryonirspBudName.num_modstates.value]
197
+
198
+ @property
199
+ def num_cs_steps(self) -> int:
200
+ """Find the number of calibration sequence steps."""
201
+ return self._db_dict[BudName.num_cs_steps.value]
202
+
203
+ @property
204
+ def stokes_I_list(self) -> [str]:
205
+ """List containing only the Stokes-I parameter."""
206
+ return ["I"]
207
+
208
+ @property
209
+ def correct_for_polarization(self) -> bool:
210
+ """Correct for polarization."""
211
+ return self.num_modstates > 1 and self._db_dict[
212
+ CryonirspBudName.modulator_spin_mode.value
213
+ ] in ["Continuous", "Stepped"]
214
+
215
+ @property
216
+ def axis_1_type(self) -> str:
217
+ """Find the type of the first array axis."""
218
+ return self._db_dict[CryonirspBudName.axis_1_type.value]
219
+
220
+ @property
221
+ def axis_2_type(self) -> str:
222
+ """Find the type of the second array axis."""
223
+ return self._db_dict[CryonirspBudName.axis_2_type.value]
224
+
225
+ @property
226
+ def axis_3_type(self) -> str:
227
+ """Find the type of the third array axis."""
228
+ return self._db_dict[CryonirspBudName.axis_3_type.value]
229
+
230
+ @property
231
+ def roi_1_origin_x(self) -> int:
232
+ """Get the ROI #1 x origin."""
233
+ return self._db_dict[CryonirspBudName.roi_1_origin_x.value]
234
+
235
+ @property
236
+ def roi_1_origin_y(self) -> int:
237
+ """Get the ROI #1 y origin."""
238
+ return self._db_dict[CryonirspBudName.roi_1_origin_y.value]
239
+
240
+ @property
241
+ def roi_1_size_x(self) -> int:
242
+ """Get the ROI #1 x size."""
243
+ return self._db_dict[CryonirspBudName.roi_1_size_x.value]
244
+
245
+ @property
246
+ def roi_1_size_y(self) -> int:
247
+ """Get the ROI #1 y size."""
248
+ return self._db_dict[CryonirspBudName.roi_1_size_y.value]
@@ -0,0 +1,26 @@
1
+ """Support classes for exposure conditions."""
2
+ from collections import namedtuple
3
+ from enum import StrEnum
4
+
5
+ # Number of digits used to round the exposure when creating the ExposureConditions tuple in fits_access
6
+ CRYO_EXP_TIME_ROUND_DIGITS: int = 3
7
+
8
+ """Base class to hold a tuple of exposure time and filter name."""
9
+ ExposureConditionsBase = namedtuple("ExposureConditions", ["exposure_time", "filter_name"])
10
+
11
+
12
+ class ExposureConditions(ExposureConditionsBase):
13
+ """Define str to make tags look reasonable."""
14
+
15
+ def __str__(self):
16
+ return f"{self.exposure_time}_{self.filter_name}"
17
+
18
+
19
+ class AllowableOpticalDensityFilterNames(StrEnum):
20
+ """Enum to implement list of allowable Optical Density Filter names."""
21
+
22
+ G278 = "G278"
23
+ G358 = "G358"
24
+ G408 = "G408"
25
+ OPEN = "OPEN"
26
+ NONE = "NONE"
@@ -0,0 +1,296 @@
1
+ """CryoNIRSP calibration pipeline parameters."""
2
+ from datetime import datetime
3
+ from functools import cached_property
4
+
5
+ import astropy.units as u
6
+ import numpy as np
7
+ from dkist_processing_common.models.parameters import ParameterArmIdMixin
8
+ from dkist_processing_common.models.parameters import ParameterBase
9
+ from dkist_processing_common.models.parameters import ParameterWavelengthMixin
10
+ from dkist_processing_common.tasks.mixin.input_dataset import InputDatasetMixin
11
+
12
+ from dkist_processing_cryonirsp.models.exposure_conditions import AllowableOpticalDensityFilterNames
13
+
14
+
15
+ class CryonirspParsingParameters(ParameterBase):
16
+ """
17
+ Parameters specifically (and only) for the Parse task.
18
+
19
+ Needed because the Parse task doesn't yet know about arm id, wavelength, or obs_ip_start_time, which are all
20
+ required to initialize the main parameter class.
21
+ """
22
+
23
+ @property
24
+ def max_cs_step_time_sec(self):
25
+ """Time window within which CS steps with identical GOS configurations are considered to be the same."""
26
+ return self._find_most_recent_past_value(
27
+ "cryonirsp_max_cs_step_time_sec", start_date=datetime.now()
28
+ )
29
+
30
+
31
+ class CryonirspParameters(ParameterBase, ParameterWavelengthMixin, ParameterArmIdMixin):
32
+ """Put all CryoNIRSP parameters parsed from the input dataset document in a single property."""
33
+
34
+ @property
35
+ def geo_upsample_factor(self) -> int:
36
+ """Pixel precision (1/upsample_factor) to use during phase matching of beam/modulator images."""
37
+ return self._find_most_recent_past_value("cryonirsp_geo_upsample_factor")
38
+
39
+ @property
40
+ def geo_max_shift(self) -> int:
41
+ """Max allowed pixel shift when computing spectral curvature."""
42
+ return self._find_most_recent_past_value("cryonirsp_geo_max_shift")
43
+
44
+ @property
45
+ def geo_poly_fit_order(self) -> int:
46
+ """Order of polynomial used to fit spectral shift as a function of slit position."""
47
+ return self._find_most_recent_past_value("cryonirsp_geo_poly_fit_order")
48
+
49
+ @property
50
+ def geo_long_axis_gradient_displacement(self) -> int:
51
+ """Number of pixels to shift along the long axis of a strip when computing a gradient."""
52
+ return self._find_most_recent_past_value("cryonirsp_geo_long_axis_gradient_displacement")
53
+
54
+ @property
55
+ def geo_strip_long_axis_size_fraction(self) -> float:
56
+ """Fraction of full array size for the long axis of the strips used to find the beam angle."""
57
+ return self._find_most_recent_past_value("cryonirsp_geo_strip_long_axis_size_fraction")
58
+
59
+ @property
60
+ def geo_strip_short_axis_size_fraction(self) -> float:
61
+ """Fraction of full array size for the short axis of the strips used to find the beam angle."""
62
+ return self._find_most_recent_past_value("cryonirsp_geo_strip_short_axis_size_fraction")
63
+
64
+ @property
65
+ def geo_strip_spectral_offset_size_fraction(self) -> float:
66
+ """Fraction of full spectral size to set as the +/- offset from spectral center for the two strips used to find the beam angle."""
67
+ return self._find_most_recent_past_value(
68
+ "cryonirsp_geo_strip_spectral_offset_size_fraction"
69
+ )
70
+
71
+ @property
72
+ def polcal_num_spectral_bins(self) -> int:
73
+ """Return Number of demodulation matrices to compute across the entire FOV in the spectral dimension."""
74
+ return self._find_most_recent_past_value("cryonirsp_polcal_num_spectral_bins")
75
+
76
+ @property
77
+ def polcal_num_spatial_bins(self) -> int:
78
+ """Return Number of demodulation matrices to compute across the entire FOV in the spatial dimension."""
79
+ return self._find_most_recent_past_value("cryonirsp_polcal_num_spatial_bins")
80
+
81
+ @property
82
+ def polcal_pac_fit_mode(self):
83
+ """Name of set of fitting flags to use during PAC Calibration Unit parameter fits."""
84
+ return self._find_most_recent_past_value("cryonirsp_polcal_pac_fit_mode")
85
+
86
+ @property
87
+ def polcal_pac_init_set(self):
88
+ """Name of set of initial values for Calibration Unit parameter fit."""
89
+ return self._find_most_recent_past_value("cryonirsp_polcal_pac_init_set")
90
+
91
+ @property
92
+ def beam_boundaries_smoothing_disk_size(self) -> int:
93
+ """Return the size of the smoothing disk (in pixels) to be used in the beam boundaries computation."""
94
+ return self._find_most_recent_past_value("cryonirsp_beam_boundaries_smoothing_disk_size")
95
+
96
+ @property
97
+ def beam_boundaries_upsample_factor(self) -> int:
98
+ """Return the upsample factor to be used in the beam boundaries cross correlation computation."""
99
+ return self._find_most_recent_past_value("cryonirsp_beam_boundaries_upsample_factor")
100
+
101
+ @property
102
+ def beam_boundaries_sp_beam_transition_region_size_fraction(self) -> float:
103
+ """
104
+ Fraction of full spectral size to use as the size of the transition region between the two SP beams.
105
+
106
+ A region with size = (this parameter * full spectral size) and centered at the center of the spectral dimension
107
+ will be ignored when extracting the beams.
108
+ """
109
+ return self._find_most_recent_past_value(
110
+ "cryonirsp_beam_boundaries_sp_beam_transition_region_size_fraction"
111
+ )
112
+
113
+ @property
114
+ def bad_pixel_map_median_filter_size(self) -> list[int, int]:
115
+ """Return the smoothing disk size to be used in the bad pixel map computation."""
116
+ filter_size = self._find_parameter_for_arm("cryonirsp_bad_pixel_map_median_filter_size")
117
+ return filter_size
118
+
119
+ @property
120
+ def bad_pixel_map_threshold_factor(self) -> float:
121
+ """Return the threshold multiplicative factor to be used in the bad pixel map computation."""
122
+ return self._find_most_recent_past_value("cryonirsp_bad_pixel_map_threshold_factor")
123
+
124
+ @property
125
+ def corrections_bad_pixel_median_filter_size(self) -> int:
126
+ """Return the size of the median filter to be used for bad pixel correction."""
127
+ return self._find_most_recent_past_value(
128
+ "cryonirsp_corrections_bad_pixel_median_filter_size"
129
+ )
130
+
131
+ @cached_property
132
+ def corrections_bad_pixel_fraction_threshold(self) -> float:
133
+ """
134
+ Return the fraction of the bad pixel mask.
135
+
136
+ If exceeded, will cause the fallback to a faster but worse algorithm.
137
+ """
138
+ return self._find_most_recent_past_value(
139
+ "cryonirsp_corrections_bad_pixel_fraction_threshold"
140
+ )
141
+
142
+ @cached_property
143
+ def linearization_thresholds(self) -> np.ndarray:
144
+ """Name of parameter associated with the linearization thresholds."""
145
+ param_dict = self._find_parameter_for_arm("cryonirsp_linearization_thresholds")
146
+ value = self._load_param_value_from_numpy_save(param_dict)
147
+ # float64 data can blow up the memory required for linearization - convert to float32
148
+ if np.issubdtype(value.dtype, np.float64):
149
+ value = value.astype(np.float32, casting="same_kind")
150
+ return value
151
+
152
+ @cached_property
153
+ def linearization_polyfit_coeffs(self) -> np.ndarray:
154
+ """Name of parameter associated with the linearization polyfit coefficients."""
155
+ param_value = self._find_parameter_for_arm("cryonirsp_linearization_polyfit_coeffs")
156
+ return np.asarray(param_value, dtype=np.float32)
157
+
158
+ @cached_property
159
+ def linearization_max_memory_gb(self) -> float:
160
+ """Get the maximum amount of memory in GB available to the linearity correction task worker."""
161
+ mem_size = self._find_most_recent_past_value("cryonirsp_linearization_max_memory_gb")
162
+ return mem_size
163
+
164
+ @property
165
+ def solar_characteristic_spatial_normalization_percentile(self) -> float:
166
+ """Percentile to pass to `np.nanpercentile` when normalizing each spatial position of the characteristic spectra."""
167
+ return self._find_most_recent_past_value(
168
+ "cryonirsp_solar_characteristic_spatial_normalization_percentile"
169
+ )
170
+
171
+ @property
172
+ def fringe_correction_on(self) -> bool:
173
+ """Return True if fringe correction should be performed."""
174
+ return self._find_most_recent_past_value(
175
+ "cryonirsp_fringe_correction_on",
176
+ start_date=self._obs_ip_start_datetime,
177
+ )
178
+
179
+ @property
180
+ def fringe_correction_spectral_filter_size(self) -> list[int, int]:
181
+ """Get the filter kernel size for the spectral filtering in the fringe removal process."""
182
+ return self._find_most_recent_past_value("cryonirsp_fringe_correction_spectral_filter_size")
183
+
184
+ @property
185
+ def fringe_correction_spatial_filter_size(self) -> list[int, int]:
186
+ """Get the filter kernel size for the spatial filtering in the fringe removal process."""
187
+ return self._find_most_recent_past_value("cryonirsp_fringe_correction_spatial_filter_size")
188
+
189
+ @property
190
+ def fringe_correction_lowpass_cutoff_period(self) -> float:
191
+ """Get the lowpass filter cutoff period in pixels for the fringe removal process."""
192
+ return self._find_most_recent_past_value(
193
+ "cryonirsp_fringe_correction_lowpass_cutoff_period"
194
+ )
195
+
196
+ @cached_property
197
+ def linearization_filter_attenuation_dict(self) -> dict:
198
+ """Return a dict that maps the filter name to its attenuation parameter."""
199
+ filter_attenuation_dict = {
200
+ AllowableOpticalDensityFilterNames.G278.value: self._linearization_optical_density_filter_attenuation_g278,
201
+ AllowableOpticalDensityFilterNames.G358.value: self._linearization_optical_density_filter_attenuation_g358,
202
+ AllowableOpticalDensityFilterNames.G408.value: self._linearization_optical_density_filter_attenuation_g408,
203
+ AllowableOpticalDensityFilterNames.OPEN.value: self._linearization_optical_density_filter_attenuation_open,
204
+ AllowableOpticalDensityFilterNames.NONE.value: self._linearization_optical_density_filter_attenuation_none,
205
+ }
206
+ return filter_attenuation_dict
207
+
208
+ @cached_property
209
+ def _linearization_optical_density_filter_attenuation_g278(self) -> float:
210
+ """Return the attenuation value for the G278 filter."""
211
+ return self._find_parameter_closest_wavelength(
212
+ "cryonirsp_linearization_optical_density_filter_attenuation_g278"
213
+ )
214
+
215
+ @cached_property
216
+ def _linearization_optical_density_filter_attenuation_g358(self) -> float:
217
+ """Return the attenuation value for the G358 filter."""
218
+ return self._find_parameter_closest_wavelength(
219
+ "cryonirsp_linearization_optical_density_filter_attenuation_g358"
220
+ )
221
+
222
+ @cached_property
223
+ def _linearization_optical_density_filter_attenuation_g408(self) -> float:
224
+ """Return the attenuation value for the G408 filter."""
225
+ return self._find_parameter_closest_wavelength(
226
+ "cryonirsp_linearization_optical_density_filter_attenuation_g408"
227
+ )
228
+
229
+ @cached_property
230
+ def _linearization_optical_density_filter_attenuation_none(self) -> float:
231
+ """Return the attenuation value for a filter of 'none'."""
232
+ return 0.0
233
+
234
+ @cached_property
235
+ def _linearization_optical_density_filter_attenuation_open(self) -> float:
236
+ """Return the attenuation value for a filter of 'Open'."""
237
+ return 0.0
238
+
239
+ @property
240
+ def cryo_instrument_alignment_angle(self) -> u.Quantity:
241
+ """Return the CryoNIRSP instrument alignment angle."""
242
+ alignment_angle = -91.0 * u.deg
243
+ return alignment_angle
244
+
245
+ @property
246
+ def ci_spatial_scale_along_slit(self) -> u.Quantity:
247
+ """Return the CryoNIRSP CI spatial scale along the slit."""
248
+ spatial_scale_along_slit = 0.05308 * u.arcsec / u.pix
249
+ return spatial_scale_along_slit
250
+
251
+ @property
252
+ def sp_spatial_scale_along_slit(self) -> u.Quantity:
253
+ """Return the CryoNIRSP SP spatial scale along the slit."""
254
+ spatial_scale_along_slit = 0.12 * u.arcsec / u.pix
255
+ return spatial_scale_along_slit
256
+
257
+ @property
258
+ def mirror_scan_recalibration_constant(self) -> float:
259
+ """Return the CryoNIRSP mirror scan recalibration constant."""
260
+ mirror_scan_recalibration_constant = 0.466 / 0.5
261
+ return mirror_scan_recalibration_constant
262
+
263
+ @property
264
+ def camera_mirror_focal_length_mm(self) -> u.Quantity:
265
+ """Return the CryoNIRSP camera mirror focal length."""
266
+ return (
267
+ self._find_most_recent_past_value("cryonirsp_camera_mirror_focal_length_mm")
268
+ * u.millimeter
269
+ )
270
+
271
+ @property
272
+ def pixel_pitch_micron(self) -> u.Quantity:
273
+ """Return the CryoNIRSP pixel pitch."""
274
+ return self._find_most_recent_past_value("cryonirsp_pixel_pitch_micron") * u.micron
275
+
276
+ @cached_property
277
+ def solar_atlas(self) -> np.ndarray:
278
+ """Solar reference atlas.
279
+
280
+ Contains two arrays:
281
+ - wavelength in nanometers
282
+ - transmission at given wavelength
283
+ """
284
+ param_dict = self._find_most_recent_past_value("cryonirsp_solar_atlas")
285
+ return self._load_param_value_from_numpy_save(param_dict)
286
+
287
+ @cached_property
288
+ def telluric_atlas(self) -> np.ndarray:
289
+ """Telluric reference atlas.
290
+
291
+ Contains two arrays:
292
+ - wavelength in nanometers
293
+ - transmission at given wavelength
294
+ """
295
+ param_dict = self._find_most_recent_past_value("cryonirsp_telluric_atlas")
296
+ return self._load_param_value_from_numpy_save(param_dict)