dkist-processing-cryonirsp 1.19.19__tar.gz → 2.0.0rc2__tar.gz

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.
Files changed (142) hide show
  1. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/PKG-INFO +40 -36
  2. dkist_processing_cryonirsp-2.0.0rc2/changelog/289.bugfix.rst +2 -0
  3. dkist_processing_cryonirsp-2.0.0rc2/changelog/292.feature.2.rst +3 -0
  4. dkist_processing_cryonirsp-2.0.0rc2/changelog/292.feature.rst +2 -0
  5. dkist_processing_cryonirsp-2.0.0rc2/changelog/292.misc.rst +3 -0
  6. dkist_processing_cryonirsp-2.0.0rc2/changelog/292.science.rst +4 -0
  7. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/models/parameters.py +25 -0
  8. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tasks/instrument_polarization.py +3 -2
  9. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tasks/sp_geometric.py +168 -88
  10. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/conftest.py +6 -1
  11. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/local_trial_workflows/l0_cals_only.py +24 -5
  12. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/local_trial_workflows/l0_to_l1.py +24 -5
  13. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/local_trial_workflows/linearize_only.py +22 -3
  14. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/local_trial_workflows/local_trial_helpers.py +54 -1
  15. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/test_instrument_polarization.py +99 -8
  16. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp.egg-info/PKG-INFO +40 -36
  17. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp.egg-info/SOURCES.txt +5 -0
  18. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp.egg-info/requires.txt +39 -35
  19. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp.egg-info/scm_file_list.json +110 -105
  20. dkist_processing_cryonirsp-2.0.0rc2/dkist_processing_cryonirsp.egg-info/scm_version.json +8 -0
  21. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/pyproject.toml +41 -37
  22. dkist_processing_cryonirsp-1.19.19/dkist_processing_cryonirsp.egg-info/scm_version.json +0 -8
  23. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/.gitignore +0 -0
  24. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/.pre-commit-config.yaml +0 -0
  25. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/.readthedocs.yml +0 -0
  26. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/.rovodev/.review-agent.md +0 -0
  27. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/.snyk +0 -0
  28. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/CHANGELOG.rst +0 -0
  29. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/README.rst +0 -0
  30. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/SCIENCE_CHANGELOG.rst +0 -0
  31. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/bitbucket-pipelines.yml +0 -0
  32. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/changelog/.gitempty +0 -0
  33. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/__init__.py +0 -0
  34. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/codecs/__init__.py +0 -0
  35. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/codecs/fits.py +0 -0
  36. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/config.py +0 -0
  37. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/models/__init__.py +0 -0
  38. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/models/beam_boundaries.py +0 -0
  39. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/models/constants.py +0 -0
  40. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/models/dataset_extras.py +0 -0
  41. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/models/exposure_conditions.py +0 -0
  42. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/models/fits_access.py +0 -0
  43. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/models/tags.py +0 -0
  44. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/models/task_name.py +0 -0
  45. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/parsers/__init__.py +0 -0
  46. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/parsers/check_for_gains.py +0 -0
  47. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/parsers/cryonirsp_l0_fits_access.py +0 -0
  48. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/parsers/cryonirsp_l1_fits_access.py +0 -0
  49. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/parsers/exposure_conditions.py +0 -0
  50. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/parsers/map_repeats.py +0 -0
  51. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/parsers/measurements.py +0 -0
  52. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/parsers/modstates.py +0 -0
  53. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/parsers/optical_density_filters.py +0 -0
  54. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/parsers/polarimetric_check.py +0 -0
  55. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/parsers/scan_step.py +0 -0
  56. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/parsers/time.py +0 -0
  57. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tasks/__init__.py +0 -0
  58. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tasks/assemble_movie.py +0 -0
  59. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tasks/bad_pixel_map.py +0 -0
  60. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tasks/beam_boundaries_base.py +0 -0
  61. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tasks/ci_beam_boundaries.py +0 -0
  62. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tasks/ci_science.py +0 -0
  63. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tasks/cryonirsp_base.py +0 -0
  64. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tasks/dark.py +0 -0
  65. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tasks/gain.py +0 -0
  66. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tasks/l1_output_data.py +0 -0
  67. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tasks/linearity_correction.py +0 -0
  68. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tasks/make_movie_frames.py +0 -0
  69. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tasks/mixin/__init__.py +0 -0
  70. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tasks/mixin/corrections.py +0 -0
  71. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tasks/mixin/shift_measurements.py +0 -0
  72. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tasks/parse.py +0 -0
  73. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tasks/polcal_as_science.py +0 -0
  74. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tasks/quality_metrics.py +0 -0
  75. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tasks/science_base.py +0 -0
  76. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tasks/sp_beam_boundaries.py +0 -0
  77. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tasks/sp_science.py +0 -0
  78. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tasks/sp_solar_gain.py +0 -0
  79. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tasks/sp_wavelength_calibration.py +0 -0
  80. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tasks/write_dataset_extras.py +0 -0
  81. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tasks/write_l1.py +0 -0
  82. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/__init__.py +0 -0
  83. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/header_models.py +0 -0
  84. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/local_trial_workflows/__init__.py +0 -0
  85. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/test_assemble_movie.py +0 -0
  86. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/test_assemble_qualilty.py +0 -0
  87. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/test_bad_pixel_maps.py +0 -0
  88. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/test_ci_beam_boundaries.py +0 -0
  89. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/test_ci_science.py +0 -0
  90. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/test_corrections.py +0 -0
  91. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/test_cryo_base.py +0 -0
  92. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/test_cryo_constants.py +0 -0
  93. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/test_dark.py +0 -0
  94. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/test_fits_access.py +0 -0
  95. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/test_gain.py +0 -0
  96. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/test_linearity_correction.py +0 -0
  97. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/test_make_movie_frames.py +0 -0
  98. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/test_parameters.py +0 -0
  99. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/test_parse.py +0 -0
  100. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/test_polcal_as_science.py +0 -0
  101. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/test_quality.py +0 -0
  102. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/test_sp_beam_boundaries.py +0 -0
  103. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/test_sp_geometric.py +0 -0
  104. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/test_sp_make_movie_frames.py +0 -0
  105. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/test_sp_science.py +0 -0
  106. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/test_sp_solar.py +0 -0
  107. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/test_sp_wavelength_calibration.py +0 -0
  108. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/test_trial_create_quality_report.py +0 -0
  109. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/test_workflows.py +0 -0
  110. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/test_write_dataset_extras.py +0 -0
  111. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/tests/test_write_l1.py +0 -0
  112. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/workflows/__init__.py +0 -0
  113. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/workflows/ci_l0_processing.py +0 -0
  114. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/workflows/sp_l0_processing.py +0 -0
  115. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp/workflows/trial_workflows.py +0 -0
  116. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp.egg-info/dependency_links.txt +0 -0
  117. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/dkist_processing_cryonirsp.egg-info/top_level.txt +0 -0
  118. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/docs/Makefile +0 -0
  119. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/docs/bad_pixel_calibration.rst +0 -0
  120. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/docs/beam_angle_calculation.rst +0 -0
  121. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/docs/beam_boundary_computation.rst +0 -0
  122. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/docs/changelog.rst +0 -0
  123. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/docs/ci_science_calibration.rst +0 -0
  124. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/docs/conf.py +0 -0
  125. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/docs/dataset_extras.rst +0 -0
  126. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/docs/index.rst +0 -0
  127. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/docs/l0_to_l1_cryonirsp_ci-full-trial.rst +0 -0
  128. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/docs/l0_to_l1_cryonirsp_ci.rst +0 -0
  129. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/docs/l0_to_l1_cryonirsp_sp-full-trial.rst +0 -0
  130. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/docs/l0_to_l1_cryonirsp_sp.rst +0 -0
  131. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/docs/landing_page.rst +0 -0
  132. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/docs/linearization.rst +0 -0
  133. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/docs/make.bat +0 -0
  134. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/docs/requirements.txt +0 -0
  135. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/docs/requirements_table.rst +0 -0
  136. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/docs/scientific_changelog.rst +0 -0
  137. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/docs/sp_science_calibration.rst +0 -0
  138. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/docs/wavelength_calibration.rst +0 -0
  139. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/licenses/LICENSE.rst +0 -0
  140. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/science_towncrier.sh +0 -0
  141. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/setup.cfg +0 -0
  142. {dkist_processing_cryonirsp-1.19.19 → dkist_processing_cryonirsp-2.0.0rc2}/towncrier_science.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dkist-processing-cryonirsp
3
- Version: 1.19.19
3
+ Version: 2.0.0rc2
4
4
  Summary: Science processing code for the Cryo-NIRSP instrument on DKIST
5
5
  Author-email: NSO / AURA <dkistdc@nso.edu>
6
6
  License: BSD-3-Clause
@@ -29,6 +29,7 @@ Requires-Dist: numba==0.61.2
29
29
  Requires-Dist: numpy==2.2.5
30
30
  Requires-Dist: peakutils==1.3.5
31
31
  Requires-Dist: scikit-image==0.25.2
32
+ Requires-Dist: scikit-learn==1.9.0
32
33
  Requires-Dist: scipy==1.15.3
33
34
  Requires-Dist: sunpy==6.1.1
34
35
  Provides-Extra: test
@@ -80,22 +81,21 @@ Requires-Dist: aiohttp==3.14.1; extra == "frozen"
80
81
  Requires-Dist: aiosignal==1.4.0; extra == "frozen"
81
82
  Requires-Dist: aiosmtplib==5.1.2; extra == "frozen"
82
83
  Requires-Dist: aiosqlite==0.21.0; extra == "frozen"
83
- Requires-Dist: alembic==1.18.4; extra == "frozen"
84
+ Requires-Dist: alembic==1.18.5; extra == "frozen"
84
85
  Requires-Dist: amqp==5.3.1; extra == "frozen"
85
86
  Requires-Dist: annotated-doc==0.0.4; extra == "frozen"
86
87
  Requires-Dist: annotated-types==0.7.0; extra == "frozen"
87
- Requires-Dist: anyio==4.14.0; extra == "frozen"
88
+ Requires-Dist: anyio==4.14.1; extra == "frozen"
88
89
  Requires-Dist: apache-airflow==3.2.1; extra == "frozen"
89
90
  Requires-Dist: apache-airflow-core==3.2.1; extra == "frozen"
90
91
  Requires-Dist: apache-airflow-providers-celery==3.20.0; extra == "frozen"
91
92
  Requires-Dist: apache-airflow-providers-common-compat==1.15.0; extra == "frozen"
92
93
  Requires-Dist: apache-airflow-providers-common-io==1.7.3; extra == "frozen"
93
94
  Requires-Dist: apache-airflow-providers-common-sql==2.0.1; extra == "frozen"
94
- Requires-Dist: apache-airflow-providers-postgres==6.7.1; extra == "frozen"
95
+ Requires-Dist: apache-airflow-providers-postgres==6.8.0; extra == "frozen"
95
96
  Requires-Dist: apache-airflow-providers-smtp==3.0.1; extra == "frozen"
96
- Requires-Dist: apache-airflow-providers-standard==1.14.0; extra == "frozen"
97
+ Requires-Dist: apache-airflow-providers-standard==1.15.0; extra == "frozen"
97
98
  Requires-Dist: apache-airflow-task-sdk==1.2.1; extra == "frozen"
98
- Requires-Dist: appnope==0.1.4; extra == "frozen"
99
99
  Requires-Dist: argcomplete==3.6.3; extra == "frozen"
100
100
  Requires-Dist: argon2-cffi==25.1.0; extra == "frozen"
101
101
  Requires-Dist: argon2-cffi-bindings==25.1.0; extra == "frozen"
@@ -114,14 +114,14 @@ Requires-Dist: babel==2.18.0; extra == "frozen"
114
114
  Requires-Dist: beautifulsoup4==4.15.0; extra == "frozen"
115
115
  Requires-Dist: billiard==4.2.4; extra == "frozen"
116
116
  Requires-Dist: bleach==6.4.0; extra == "frozen"
117
- Requires-Dist: boto3==1.43.34; extra == "frozen"
118
- Requires-Dist: botocore==1.43.34; extra == "frozen"
117
+ Requires-Dist: boto3==1.43.36; extra == "frozen"
118
+ Requires-Dist: botocore==1.43.36; extra == "frozen"
119
119
  Requires-Dist: cadwyn==7.1.0; extra == "frozen"
120
120
  Requires-Dist: celery==5.6.3; extra == "frozen"
121
121
  Requires-Dist: certifi==2026.6.17; extra == "frozen"
122
122
  Requires-Dist: cffi==2.0.0; extra == "frozen"
123
123
  Requires-Dist: charset-normalizer==3.4.7; extra == "frozen"
124
- Requires-Dist: click==8.4.1; extra == "frozen"
124
+ Requires-Dist: click==8.4.2; extra == "frozen"
125
125
  Requires-Dist: click-didyoumean==0.3.1; extra == "frozen"
126
126
  Requires-Dist: click-plugins==1.1.1.2; extra == "frozen"
127
127
  Requires-Dist: click-repl==0.3.0; extra == "frozen"
@@ -139,7 +139,7 @@ Requires-Dist: dill==0.4.1; extra == "frozen"
139
139
  Requires-Dist: dkist-header-validator==5.4.0; extra == "frozen"
140
140
  Requires-Dist: dkist-processing-common==14.3.4; extra == "frozen"
141
141
  Requires-Dist: dkist-processing-core==7.3.0; extra == "frozen"
142
- Requires-Dist: dkist-processing-cryonirsp==1.19.19; extra == "frozen"
142
+ Requires-Dist: dkist-processing-cryonirsp==2.0.0rc2; extra == "frozen"
143
143
  Requires-Dist: dkist-processing-math==2.2.3; extra == "frozen"
144
144
  Requires-Dist: dkist-processing-pac==3.1.2; extra == "frozen"
145
145
  Requires-Dist: dkist-service-configuration==4.3.0; extra == "frozen"
@@ -148,7 +148,7 @@ Requires-Dist: dkist_fits_specifications==4.28.0; extra == "frozen"
148
148
  Requires-Dist: dnspython==2.8.0; extra == "frozen"
149
149
  Requires-Dist: email-validator==2.3.0; extra == "frozen"
150
150
  Requires-Dist: executing==2.2.1; extra == "frozen"
151
- Requires-Dist: fastapi==0.138.0; extra == "frozen"
151
+ Requires-Dist: fastapi==0.138.1; extra == "frozen"
152
152
  Requires-Dist: fastapi-cli==0.0.27; extra == "frozen"
153
153
  Requires-Dist: fastjsonschema==2.21.2; extra == "frozen"
154
154
  Requires-Dist: flower==2.0.1; extra == "frozen"
@@ -177,6 +177,7 @@ Requires-Dist: isoduration==20.11.0; extra == "frozen"
177
177
  Requires-Dist: itsdangerous==2.2.0; extra == "frozen"
178
178
  Requires-Dist: jedi==0.20.0; extra == "frozen"
179
179
  Requires-Dist: jmespath==1.1.0; extra == "frozen"
180
+ Requires-Dist: joblib==1.5.3; extra == "frozen"
180
181
  Requires-Dist: json5==0.15.0; extra == "frozen"
181
182
  Requires-Dist: jsonpointer==3.1.1; extra == "frozen"
182
183
  Requires-Dist: jsonschema==4.26.0; extra == "frozen"
@@ -208,11 +209,12 @@ Requires-Dist: matplotlib==3.11.0; extra == "frozen"
208
209
  Requires-Dist: matplotlib-inline==0.2.2; extra == "frozen"
209
210
  Requires-Dist: mdurl==0.1.2; extra == "frozen"
210
211
  Requires-Dist: methodtools==0.4.7; extra == "frozen"
211
- Requires-Dist: mistune==3.3.1; extra == "frozen"
212
+ Requires-Dist: mistune==3.3.2; extra == "frozen"
212
213
  Requires-Dist: more-itertools==11.1.0; extra == "frozen"
213
214
  Requires-Dist: moviepy==2.1.2; extra == "frozen"
214
215
  Requires-Dist: msgspec==0.21.1; extra == "frozen"
215
216
  Requires-Dist: multidict==6.7.1; extra == "frozen"
217
+ Requires-Dist: narwhals==2.22.1; extra == "frozen"
216
218
  Requires-Dist: natsort==8.4.0; extra == "frozen"
217
219
  Requires-Dist: nbclient==0.11.0; extra == "frozen"
218
220
  Requires-Dist: nbconvert==7.17.1; extra == "frozen"
@@ -224,30 +226,30 @@ Requires-Dist: notebook_shim==0.2.4; extra == "frozen"
224
226
  Requires-Dist: numba==0.61.2; extra == "frozen"
225
227
  Requires-Dist: numpy==2.2.5; extra == "frozen"
226
228
  Requires-Dist: object-clerk==1.0.0; extra == "frozen"
227
- Requires-Dist: opentelemetry-api==1.42.1; extra == "frozen"
228
- Requires-Dist: opentelemetry-exporter-otlp==1.42.1; extra == "frozen"
229
- Requires-Dist: opentelemetry-exporter-otlp-proto-common==1.42.1; extra == "frozen"
230
- Requires-Dist: opentelemetry-exporter-otlp-proto-grpc==1.42.1; extra == "frozen"
231
- Requires-Dist: opentelemetry-exporter-otlp-proto-http==1.42.1; extra == "frozen"
232
- Requires-Dist: opentelemetry-instrumentation==0.63b1; extra == "frozen"
233
- Requires-Dist: opentelemetry-instrumentation-aiohttp-client==0.63b1; extra == "frozen"
234
- Requires-Dist: opentelemetry-instrumentation-asgi==0.63b1; extra == "frozen"
235
- Requires-Dist: opentelemetry-instrumentation-botocore==0.63b1; extra == "frozen"
236
- Requires-Dist: opentelemetry-instrumentation-celery==0.63b1; extra == "frozen"
237
- Requires-Dist: opentelemetry-instrumentation-dbapi==0.63b1; extra == "frozen"
238
- Requires-Dist: opentelemetry-instrumentation-fastapi==0.63b1; extra == "frozen"
239
- Requires-Dist: opentelemetry-instrumentation-pika==0.63b1; extra == "frozen"
240
- Requires-Dist: opentelemetry-instrumentation-psycopg2==0.63b1; extra == "frozen"
241
- Requires-Dist: opentelemetry-instrumentation-pymongo==0.63b1; extra == "frozen"
242
- Requires-Dist: opentelemetry-instrumentation-redis==0.63b1; extra == "frozen"
243
- Requires-Dist: opentelemetry-instrumentation-requests==0.63b1; extra == "frozen"
244
- Requires-Dist: opentelemetry-instrumentation-sqlalchemy==0.63b1; extra == "frozen"
245
- Requires-Dist: opentelemetry-instrumentation-system-metrics==0.63b1; extra == "frozen"
229
+ Requires-Dist: opentelemetry-api==1.43.0; extra == "frozen"
230
+ Requires-Dist: opentelemetry-exporter-otlp==1.43.0; extra == "frozen"
231
+ Requires-Dist: opentelemetry-exporter-otlp-proto-common==1.43.0; extra == "frozen"
232
+ Requires-Dist: opentelemetry-exporter-otlp-proto-grpc==1.43.0; extra == "frozen"
233
+ Requires-Dist: opentelemetry-exporter-otlp-proto-http==1.43.0; extra == "frozen"
234
+ Requires-Dist: opentelemetry-instrumentation==0.64b0; extra == "frozen"
235
+ Requires-Dist: opentelemetry-instrumentation-aiohttp-client==0.64b0; extra == "frozen"
236
+ Requires-Dist: opentelemetry-instrumentation-asgi==0.64b0; extra == "frozen"
237
+ Requires-Dist: opentelemetry-instrumentation-botocore==0.64b0; extra == "frozen"
238
+ Requires-Dist: opentelemetry-instrumentation-celery==0.64b0; extra == "frozen"
239
+ Requires-Dist: opentelemetry-instrumentation-dbapi==0.64b0; extra == "frozen"
240
+ Requires-Dist: opentelemetry-instrumentation-fastapi==0.64b0; extra == "frozen"
241
+ Requires-Dist: opentelemetry-instrumentation-pika==0.64b0; extra == "frozen"
242
+ Requires-Dist: opentelemetry-instrumentation-psycopg2==0.64b0; extra == "frozen"
243
+ Requires-Dist: opentelemetry-instrumentation-pymongo==0.64b0; extra == "frozen"
244
+ Requires-Dist: opentelemetry-instrumentation-redis==0.64b0; extra == "frozen"
245
+ Requires-Dist: opentelemetry-instrumentation-requests==0.64b0; extra == "frozen"
246
+ Requires-Dist: opentelemetry-instrumentation-sqlalchemy==0.64b0; extra == "frozen"
247
+ Requires-Dist: opentelemetry-instrumentation-system-metrics==0.64b0; extra == "frozen"
246
248
  Requires-Dist: opentelemetry-propagator-aws-xray==1.0.2; extra == "frozen"
247
- Requires-Dist: opentelemetry-proto==1.42.1; extra == "frozen"
248
- Requires-Dist: opentelemetry-sdk==1.42.1; extra == "frozen"
249
- Requires-Dist: opentelemetry-semantic-conventions==0.63b1; extra == "frozen"
250
- Requires-Dist: opentelemetry-util-http==0.63b1; extra == "frozen"
249
+ Requires-Dist: opentelemetry-proto==1.43.0; extra == "frozen"
250
+ Requires-Dist: opentelemetry-sdk==1.43.0; extra == "frozen"
251
+ Requires-Dist: opentelemetry-semantic-conventions==0.64b0; extra == "frozen"
252
+ Requires-Dist: opentelemetry-util-http==0.64b0; extra == "frozen"
251
253
  Requires-Dist: outcome==1.3.0.post0; extra == "frozen"
252
254
  Requires-Dist: packaging==26.2; extra == "frozen"
253
255
  Requires-Dist: pandas==3.0.3; extra == "frozen"
@@ -268,7 +270,7 @@ Requires-Dist: proglog==0.1.12; extra == "frozen"
268
270
  Requires-Dist: prometheus_client==0.25.0; extra == "frozen"
269
271
  Requires-Dist: prompt_toolkit==3.0.52; extra == "frozen"
270
272
  Requires-Dist: propcache==0.5.2; extra == "frozen"
271
- Requires-Dist: protobuf==6.33.6; extra == "frozen"
273
+ Requires-Dist: protobuf==7.35.1; extra == "frozen"
272
274
  Requires-Dist: psutil==7.2.2; extra == "frozen"
273
275
  Requires-Dist: psycopg2-binary==2.9.12; extra == "frozen"
274
276
  Requires-Dist: ptyprocess==0.7.0; extra == "frozen"
@@ -301,6 +303,7 @@ Requires-Dist: rich-toolkit==0.20.1; extra == "frozen"
301
303
  Requires-Dist: rpds-py==2026.5.1; extra == "frozen"
302
304
  Requires-Dist: s3transfer==0.19.0; extra == "frozen"
303
305
  Requires-Dist: scikit-image==0.25.2; extra == "frozen"
306
+ Requires-Dist: scikit-learn==1.9.0; extra == "frozen"
304
307
  Requires-Dist: scipy==1.15.3; extra == "frozen"
305
308
  Requires-Dist: semantic-version==2.10.0; extra == "frozen"
306
309
  Requires-Dist: setproctitle==1.3.7; extra == "frozen"
@@ -322,6 +325,7 @@ Requires-Dist: tenacity==8.5.0; extra == "frozen"
322
325
  Requires-Dist: termcolor==3.3.0; extra == "frozen"
323
326
  Requires-Dist: terminado==0.18.1; extra == "frozen"
324
327
  Requires-Dist: text-unidecode==1.3; extra == "frozen"
328
+ Requires-Dist: threadpoolctl==3.6.0; extra == "frozen"
325
329
  Requires-Dist: tifffile==2026.6.1; extra == "frozen"
326
330
  Requires-Dist: tinycss2==1.5.1; extra == "frozen"
327
331
  Requires-Dist: tornado==6.5.7; extra == "frozen"
@@ -0,0 +1,2 @@
1
+ Use `resize_arrays_local_mean` to average arrays in the instrument polarization task instead of `resize_arrays`.
2
+ With an output shape of `(1,1)`, `resize_arrays` returns the center pixel.
@@ -0,0 +1,3 @@
1
+ The calculation of a single spectral shift is now *only* via interpolation of the correlation signal between the reference
2
+ and target spectra. Previously a second refinement of this value was done via a chi-squared minimization, but this was show
3
+ to increase noise with no increase in accuracy.
@@ -0,0 +1,2 @@
1
+ Compute spectral shift calculations on smoothed/filtered spectral derivatives of the reference and target spectra. This
2
+ amplifies the spectral signal and greatly improves the accuracy of the individual shift measurements.
@@ -0,0 +1,3 @@
1
+ Add option to all local trial workflows (``--parameter-doc``) that uses an existing input dataset parameter-part document
2
+ to populate the parameters during a local run. This is an easy way for a user to run a pipeline locally using the exact
3
+ parameters used in production.
@@ -0,0 +1,4 @@
1
+ Improvements to the calculation of the spectral curvature to make them more robust against noisy input data. This change
2
+ only affects science data for datasets where the spectral curvature was previously mis-identified. For these datasets this
3
+ change will correct un-aligned spectral axes in L1 data and improve the quality of gain correction because the solar gain
4
+ frames will have better removal of solar absorption lines.
@@ -37,11 +37,36 @@ class CryonirspParameters(ParameterBase, ParameterWavelengthMixin, ParameterArmI
37
37
  """Pixel precision (1/upsample_factor) to use during phase matching of beam/modulator images."""
38
38
  return self._find_most_recent_past_value("cryonirsp_geo_upsample_factor")
39
39
 
40
+ @property
41
+ def geo_savgol_window_length_px(self) -> int:
42
+ """Size of the window for the SavGol filter used to smooth the spectral derivative during prep for spectral shift measurements."""
43
+ return self._find_most_recent_past_value("cryonirsp_geo_savgol_window_length_px")
44
+
45
+ @property
46
+ def geo_savgol_polyorder(self) -> int:
47
+ """Order of the polynomial fit in the SavGol filter used to smooth the spectral derivative during prep for the spectral shift measurements."""
48
+ return self._find_most_recent_past_value("cryonirsp_geo_savgol_polyorder")
49
+
50
+ @property
51
+ def geo_derivative_cutoff_threshold(self) -> float:
52
+ """Threshold value for the spectral derivative above which pixels will be set to NaN."""
53
+ return self._find_most_recent_past_value("cryonirsp_geo_derivative_cutoff_threshold")
54
+
40
55
  @property
41
56
  def geo_max_shift(self) -> int:
42
57
  """Max allowed pixel shift when computing spectral curvature."""
43
58
  return self._find_most_recent_past_value("cryonirsp_geo_max_shift")
44
59
 
60
+ @property
61
+ def geo_shift_peak_interpolation_width_px(self) -> int:
62
+ """Return the ± pixel width used to interpolate the correlation peaks when computing spectral curvature."""
63
+ return self._find_most_recent_past_value("cryonirsp_geo_shift_peak_interpolation_width_px")
64
+
65
+ @property
66
+ def geo_poly_fit_min_samples(self) -> float:
67
+ """Return fractional number of samples required for the RANSAC regressor used to fit spectral shifts along the slit."""
68
+ return self._find_most_recent_past_value("cryonirsp_geo_poly_fit_min_samples")
69
+
45
70
  @property
46
71
  def geo_poly_fit_order(self) -> int:
47
72
  """Order of polynomial used to fit spectral shift as a function of slit position."""
@@ -12,6 +12,7 @@ from dkist_processing_math.arithmetic import divide_arrays_by_array
12
12
  from dkist_processing_math.arithmetic import subtract_array_from_arrays
13
13
  from dkist_processing_math.statistics import average_numpy_arrays
14
14
  from dkist_processing_math.transform.binning import resize_arrays
15
+ from dkist_processing_math.transform.binning import resize_arrays_local_mean
15
16
  from dkist_processing_pac.fitter.polcal_fitter import PolcalFitter
16
17
  from dkist_processing_pac.input_data.drawer import Drawer
17
18
  from dkist_processing_pac.input_data.dresser import Dresser
@@ -281,8 +282,8 @@ class InstrumentPolarizationCalibrationBase(CryonirspTaskBase, ABC):
281
282
  self.parameters.polcal_num_spatial_bins,
282
283
  self.parameters.polcal_num_spectral_bins,
283
284
  )
284
- local_binned_array = next(resize_arrays(gain_corrected_array, output_shape))
285
- global_binned_array = next(resize_arrays(gain_corrected_array, (1, 1)))
285
+ local_binned_array = next(resize_arrays_local_mean(gain_corrected_array, output_shape))
286
+ global_binned_array = next(resize_arrays_local_mean(gain_corrected_array, (1, 1)))
286
287
 
287
288
  with self.telemetry_span(f"Create reduced CryonirspL0FitsAccess for {apm_str}"):
288
289
  local_result = CryonirspL0FitsAccess(
@@ -4,14 +4,20 @@ import math
4
4
 
5
5
  import numpy as np
6
6
  import peakutils as pku
7
+ import scipy.interpolate as spi
7
8
  import scipy.ndimage as spnd
9
+ import scipy.signal as ss
8
10
  from dkist_processing_common.codecs.fits import fits_array_encoder
9
11
  from dkist_processing_common.models.task_name import TaskName
10
12
  from dkist_processing_math.arithmetic import divide_arrays_by_array
11
13
  from dkist_processing_math.arithmetic import subtract_array_from_arrays
12
14
  from dkist_processing_math.statistics import average_numpy_arrays
13
15
  from dkist_service_configuration.logging import logger
14
- from scipy.optimize import minimize
16
+ from sklearn.linear_model import RANSACRegressor
17
+ from sklearn.pipeline import Pipeline
18
+ from sklearn.pipeline import make_pipeline
19
+ from sklearn.preprocessing import PolynomialFeatures
20
+ from sklearn.preprocessing import RobustScaler
15
21
 
16
22
  from dkist_processing_cryonirsp.codecs.fits import cryo_fits_array_decoder
17
23
  from dkist_processing_cryonirsp.models.beam_boundaries import BeamBoundary
@@ -97,6 +103,9 @@ class SPGeometricCalibration(CryonirspTaskBase, ShiftMeasurementsMixin):
97
103
  beam=beam,
98
104
  )
99
105
 
106
+ with self.telemetry_span(f"Preparing beam {beam} for spectral shift measurement"):
107
+ self.prepare_array_for_shift_measurement(beam=beam)
108
+
100
109
  with self.telemetry_span(f"Computing spectral shifts for {beam = }"):
101
110
  spec_shifts = self.compute_spectral_shifts(beam=beam)
102
111
 
@@ -181,6 +190,22 @@ class SPGeometricCalibration(CryonirspTaskBase, ShiftMeasurementsMixin):
181
190
  )
182
191
  return average_numpy_arrays(array_generator)
183
192
 
193
+ def shift_prepared_data(self, beam: int) -> np.ndarray:
194
+ """
195
+ Array for a single beam that has been prepared for measurements of spectral shift.
196
+
197
+ I.e., the array created by `prepare_array_for_spectral_shift`
198
+ """
199
+ return next(
200
+ self.read(
201
+ tags=[
202
+ CryonirspTag.intermediate_frame(beam=beam),
203
+ CryonirspTag.task("GC_SHIFT_PREPARED"),
204
+ ],
205
+ decoder=cryo_fits_array_decoder,
206
+ )
207
+ )
208
+
184
209
  def do_basic_corrections(self):
185
210
  """Apply dark, bad pixel and lamp gain corrections to all data that will be used for Geometric Calibration."""
186
211
  # There is likely only a single exposure conditions tuple in the list, but we iterate over the list
@@ -415,11 +440,11 @@ class SPGeometricCalibration(CryonirspTaskBase, ShiftMeasurementsMixin):
415
440
  I.e., the spectral shift at each slit position needed to have wavelength be constant across a single spatial
416
441
  pixel. Generally, the algorithm is:
417
442
 
418
- 1. Identify the reference array spectrum as the center of the slit
419
- 2. For each slit position, make an initial guess of the shift via correlation
420
- 3. Take the initial guesses and use them in a chisq minimizer to refine the shifts
421
- 4. Interpolate over those shifts identified as too large
422
- 5. Remove the mean shift so the total shift amount is minimized
443
+ #. Identify the reference array spectrum as the center of the slit
444
+ #. For each slit position, measure the shift by cross-correlating the spectrum with the reference spectrum
445
+ #. Mask spatial pixels with large RMS differences between the reference spectrum and their shifted spectra
446
+ #. Fit a polynomial to the measured shifts as a function of slit position
447
+ #. Remove the mean shift so the total shift amount is minimized
423
448
 
424
449
  Parameters
425
450
  ----------
@@ -432,32 +457,39 @@ class SPGeometricCalibration(CryonirspTaskBase, ShiftMeasurementsMixin):
432
457
  Spectral shift for a single beam
433
458
  """
434
459
  logger.info(f"Computing spectral shifts for beam {beam}")
435
- beam_array = self.offset_corrected_data(beam=beam)
460
+ beam_array = self.shift_prepared_data(beam=beam)
436
461
  spatial_size = beam_array.shape[0]
437
462
 
438
463
  if beam == 1:
439
464
  # Use the same reference spectrum for both beams.
440
465
  # We pick the spectrum from the center of the slit, with a buffer of 10 pixels on either side
441
466
  middle_row = spatial_size // 2
442
- self.ref_spec = np.nanmedian(beam_array[middle_row - 10 : middle_row + 10, :], axis=0)
467
+ raw_ref_spec = np.nanmedian(beam_array[middle_row - 10 : middle_row + 10, :], axis=0)
468
+ cleaned_ref_spec = self.clean_spectrum(spectrum=raw_ref_spec)
469
+ self.ref_spec = cleaned_ref_spec
443
470
 
444
- beam_shifts = np.empty(spatial_size) * np.nan
471
+ beam_shifts = np.full(spatial_size, np.nan)
472
+ beam_rms = np.full(spatial_size, np.nan)
445
473
  for i in range(spatial_size):
446
- target_spec = beam_array[i, :]
474
+ target_spec = self.clean_spectrum(spectrum=beam_array[i, :])
447
475
 
448
- initial_guess = self.compute_initial_spec_shift_guess(
476
+ shift = self.compute_single_spec_shift(
449
477
  ref_spec=self.ref_spec, target_spec=target_spec, beam=beam, pos=i
450
478
  )
451
479
 
452
- shift = self.compute_single_spec_shift(
453
- ref_spec=self.ref_spec,
454
- target_spec=target_spec,
455
- initial_guess=initial_guess,
456
- beam=beam,
457
- pos=i,
458
- )
480
+ shifted_target = spnd.shift(target_spec, -shift, cval=np.nan)
481
+ rms = np.nanstd(self.ref_spec - shifted_target)
459
482
 
460
483
  beam_shifts[i] = shift
484
+ beam_rms[i] = rms
485
+
486
+ # Mask spatial positions with large residuals even after applying the measured shift
487
+ # Setting the threshold as 4 times the second quartile amount is a good way to estimate are large
488
+ # fraction of the CDF from a region that won't be polluted with outliers.
489
+ rms_median, rms_25_percentile = np.nanpercentile(beam_rms, [50, 25])
490
+ rms_threshold = rms_median + 4 * (rms_median - rms_25_percentile)
491
+ logger.info(f"Raw shift RMS threshold for beam {beam} is {rms_threshold}")
492
+ beam_shifts[beam_rms > rms_threshold] = np.nan
461
493
 
462
494
  # Subtract the average so we shift my a minimal amount
463
495
  if beam == 1:
@@ -477,22 +509,95 @@ class SPGeometricCalibration(CryonirspTaskBase, ShiftMeasurementsMixin):
477
509
 
478
510
  # Finally, fit the shifts and return the resulting polynomial. Any "bad" fits were set to NaN and will be
479
511
  # interpolated over.
480
- poly_fit_order = self.parameters.geo_poly_fit_order
481
512
  nan_idx = np.isnan(beam_shifts)
482
- poly = np.poly1d(
483
- np.polyfit(np.arange(spatial_size)[~nan_idx], beam_shifts[~nan_idx], poly_fit_order)
513
+ ransac_abscissa = np.arange(spatial_size)[:, None]
514
+ model = self.build_RANSAC_model()
515
+ model.fit(ransac_abscissa[~nan_idx, :], beam_shifts[~nan_idx])
516
+
517
+ return model.predict(ransac_abscissa)
518
+
519
+ def prepare_array_for_shift_measurement(self, beam: int) -> None:
520
+ """
521
+ Apply filtering to an array to amplify the spectral signal and improve subsequent shift measurements.
522
+
523
+ The array to be filtered is the fully offset-and-angle corrected array for the given beam.
524
+
525
+ The filtering steps are:
526
+
527
+ #. Normalize each spectral pixel by a fixed value of its CDF. This removes scaling differences between the reference and target spectra.
528
+
529
+ a. At this stage we also clip values outside the range [0, 2]
530
+
531
+ #. Computed a smoothed spectral derivative for each spatial pixel. This step greatly increases the strength of the true spectral signal.
532
+
533
+ #. Apply a spectral windowing function to protect the correlation of the reference and target spectra from weird and wild edge-effects.
534
+ """
535
+ beam_array = self.offset_corrected_data(beam=beam)
536
+
537
+ normalized = (
538
+ beam_array
539
+ / np.nanpercentile(
540
+ beam_array,
541
+ self.parameters.solar_characteristic_spatial_normalization_percentile,
542
+ axis=1,
543
+ )[:, None]
544
+ )
545
+
546
+ # The normalized array really shouldn't have any values less than 0 or more than 2; we're essentially
547
+ # saying we expect the dynamic range of the spectrum to be less than 200% of the continuum value, which
548
+ # is a very reasonable assumption.
549
+ clipped = np.clip(normalized, 0, 2)
550
+
551
+ # Compute a smoothed derivative in one step with the `deriv` kwarg to `savgol_filter`
552
+ smoothed_derivative = ss.savgol_filter(
553
+ clipped,
554
+ window_length=self.parameters.geo_savgol_window_length_px,
555
+ polyorder=self.parameters.geo_savgol_polyorder,
556
+ deriv=1,
557
+ axis=1,
484
558
  )
485
559
 
486
- return poly(np.arange(spatial_size))
560
+ # Apply a window to reduce bad edge signals. This works because the mean of the derivative should be 0.
561
+ windowed = smoothed_derivative * ss.windows.tukey(smoothed_derivative.shape[1])[None, :]
562
+ windowed[np.abs(windowed) > self.parameters.geo_derivative_cutoff_threshold] = np.nan
487
563
 
488
- def compute_initial_spec_shift_guess(
564
+ self.write(
565
+ data=windowed,
566
+ tags=[
567
+ CryonirspTag.intermediate_frame(beam=beam),
568
+ CryonirspTag.task("GC_SHIFT_PREPARED"),
569
+ ],
570
+ encoder=fits_array_encoder,
571
+ )
572
+
573
+ def clean_spectrum(self, spectrum: np.ndarray) -> np.ndarray:
574
+ """
575
+ Clean non-finite pixels in a spectrum by interpolating over them.
576
+
577
+ Interpolation is done via nearest-neighbor and any extrapolated values are set to the mean of the
578
+ spectrum so that they don't contribute any signal to `compute_single_spec_shift`.
579
+ """
580
+ bad_mask = ~np.isfinite(spectrum)
581
+ abscissa = np.arange(spectrum.size)
582
+ interp_func = spi.interp1d(
583
+ abscissa[~bad_mask],
584
+ spectrum[~bad_mask],
585
+ kind="nearest",
586
+ bounds_error=False,
587
+ fill_value=np.mean(spectrum[~bad_mask]),
588
+ )
589
+ spectrum[bad_mask] = interp_func(abscissa[bad_mask])
590
+ return spectrum
591
+
592
+ def compute_single_spec_shift(
489
593
  self, *, ref_spec: np.ndarray, target_spec: np.ndarray, beam: int, pos: int
490
594
  ) -> float:
491
595
  """
492
- Make a rough guess for the offset between two spectra.
596
+ Compute the offset between two spectra via correlation.
493
597
 
494
- A basic correlation is performed and the location of the peak sets the initial guess. If more than one strong
495
- peak is found then the peak locations are averaged together.
598
+ A basic correlation is performed and the location of the correlation peak is the shift. The true peak is found
599
+ by interpolating around the maximum correlation location. If more than one strong peak is found then the peak
600
+ locations are averaged together.
496
601
  """
497
602
  corr = np.correlate(
498
603
  target_spec - np.nanmean(target_spec),
@@ -507,84 +612,59 @@ class SPGeometricCalibration(CryonirspTaskBase, ShiftMeasurementsMixin):
507
612
  truncated_corr = corr[start:stop]
508
613
 
509
614
  # This min_dist ensures we only find a single peak in each correlation signal
510
- pidx = pku.indexes(truncated_corr, min_dist=truncated_corr.size)
511
- initial_guess = 1 * (pidx - truncated_corr.size // 2)
615
+ peak_idx = pku.indexes(truncated_corr, min_dist=truncated_corr.size)
512
616
 
513
- # These edge-cases are very rare, but do happen sometimes
514
- if initial_guess.size == 0:
617
+ # This edge-case is very rare, but does happen sometimes
618
+ if peak_idx.size != 1:
515
619
  logger.info(
516
- f"Spatial position {pos} in {beam=} doesn't have a correlation peak. Initial guess set to 0"
620
+ f"Spatial position {pos} in {beam = } has either no or multiple correlation peaks. Shift set to NaN."
517
621
  )
518
- initial_guess = 0.0
519
-
520
- elif initial_guess.size > 1:
622
+ return np.nan
623
+
624
+ try:
625
+ peak_interp = pku.interpolate(
626
+ np.arange(truncated_corr.size),
627
+ truncated_corr,
628
+ ind=peak_idx,
629
+ width=self.parameters.geo_shift_peak_interpolation_width_px,
630
+ )
631
+ except:
521
632
  logger.info(
522
- f"Spatial position {pos} in {beam=} has more than one correlation peak ({initial_guess}). Initial guess set to mean ({np.nanmean(initial_guess)})"
633
+ f"Spatial position {pos} in {beam = } failed to interpolate the correlation peak. Falling back "
634
+ "to the rough peak."
523
635
  )
524
- initial_guess = np.nanmean(initial_guess)
636
+ peak_interp = peak_idx[0]
525
637
 
526
- return initial_guess
638
+ shift = 1 * (peak_interp - truncated_corr.size // 2)
527
639
 
528
- def compute_single_spec_shift(
529
- self,
530
- *,
531
- ref_spec: np.ndarray,
532
- target_spec: np.ndarray,
533
- initial_guess: float,
534
- beam: int,
535
- pos: int,
536
- ) -> float:
537
- """
538
- Refine the 1D offset between two spectra.
640
+ return shift.item(0)
539
641
 
540
- A 1-parameter minimization is performed where the goodness-of-fit parameter is simply the Chisq difference
541
- between the reference spectrum and shifted target spectrum.
642
+ def build_RANSAC_model(self) -> Pipeline:
542
643
  """
543
- shift = minimize(
544
- self.shift_chisq,
545
- np.atleast_1d(initial_guess),
546
- args=(ref_spec, target_spec),
547
- method="nelder-mead",
548
- ).x[0]
549
-
550
- max_shift = self.parameters.geo_max_shift
551
- if np.abs(shift) > max_shift:
552
- # Didn't find a good peak
553
- logger.info(
554
- f"shift in {beam = } at spatial pixel {pos} out of range ({shift} > {max_shift})"
555
- )
556
- shift = np.nan
644
+ Build a scikit-learn pipeline from a set of estimators.
557
645
 
558
- return shift
646
+ Namely, construct a `~sklearn.pipeline.Pipeline` built from
559
647
 
560
- @staticmethod
561
- def shift_chisq(par: np.ndarray, ref_spec: np.ndarray, spec: np.ndarray) -> float:
648
+ `~sklearn.preprocessing.PolynomialFeatures` -> `~sklearn.preprocessing.RobustScaler` -> `~sklearn.linear_model.RANSACRegressor`
562
649
  """
563
- Goodness of fit calculation for a simple shift. Uses simple chisq as goodness of fit.
650
+ fit_order = self.parameters.geo_poly_fit_order
651
+ min_samples = self.parameters.geo_poly_fit_min_samples
652
+ logger.info(f"Building RANSAC model with {fit_order = } and {min_samples = }")
564
653
 
565
- Less robust than SPGainCalibration's `refine_shift`, but much faster.
654
+ # PolynomialFeatures casts the pipeline as a polynomial fit
655
+ # see https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.PolynomialFeatures.html#sklearn.preprocessing.PolynomialFeatures
656
+ poly_feature = PolynomialFeatures(degree=fit_order)
566
657
 
567
- Parameters
568
- ----------
569
- par : np.ndarray
570
- Spectral shift being optimized
658
+ # RobustScaler is a scale factor that is robust to outliers
659
+ # see https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.RobustScaler.html#sklearn.preprocessing.RobustScaler
660
+ scaler = RobustScaler()
571
661
 
572
- ref_spec : np.ndarray
573
- Reference spectra
662
+ # The RANSAC regressor iteratively sub-samples the input data and constructs a model with this sub-sample.
663
+ # The method used allows it to be robust to outliers.
664
+ # see https://scikit-learn.org/stable/modules/linear_model.html#ransac-regression
665
+ RANSAC = RANSACRegressor(min_samples=min_samples)
574
666
 
575
- spec : np.ndarray
576
- Spectra being fitted
577
-
578
- Returns
579
- -------
580
- float
581
- Sum of chisquared fit
582
-
583
- """
584
- shift = par[0]
585
- shifted_spec = spnd.shift(spec, -shift, mode="constant", cval=np.nan)
586
- chisq = np.nansum((ref_spec - shifted_spec) ** 2 / ref_spec)
587
- return chisq
667
+ return make_pipeline(poly_feature, scaler, RANSAC)
588
668
 
589
669
  def write_angle(self, angle: float, beam: int) -> None:
590
670
  """
@@ -390,8 +390,13 @@ def cryonirsp_testing_parameters_factory(
390
390
  cryonirsp_polcal_num_spectral_bins: int = 1
391
391
  cryonirsp_polcal_pac_fit_mode: str = "use_M12_I_sys_per_step"
392
392
  cryonirsp_geo_upsample_factor: int = 100
393
+ cryonirsp_geo_savgol_window_length_px: int = 15
394
+ cryonirsp_geo_savgol_polyorder: int = 3
395
+ cryonirsp_geo_derivative_cutoff_threshold: float = 0.2
393
396
  cryonirsp_geo_max_shift: int = 80
394
- cryonirsp_geo_poly_fit_order: int = 3
397
+ cryonirsp_geo_shift_peak_interpolation_width_px: int = 5
398
+ cryonirsp_geo_poly_fit_min_samples: float = 0.6
399
+ cryonirsp_geo_poly_fit_order: int = 4
395
400
  cryonirsp_geo_long_axis_gradient_displacement: int = 4
396
401
  cryonirsp_geo_strip_long_axis_size_fraction: float = 0.8
397
402
  cryonirsp_geo_strip_short_axis_size_fraction: float = 0.1