dkist-processing-visp 5.0.0rc2__tar.gz → 5.1.0__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 (118) hide show
  1. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/CHANGELOG.rst +43 -0
  2. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/PKG-INFO +63 -62
  3. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/SCIENCE_CHANGELOG.rst +19 -0
  4. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/models/parameters.py +37 -1
  5. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tasks/geometric.py +6 -1
  6. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tasks/solar.py +63 -12
  7. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tests/conftest.py +3 -0
  8. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tests/test_parameters.py +50 -1
  9. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tests/test_solar.py +13 -7
  10. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/workflows/l0_processing.py +7 -2
  11. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/workflows/trial_workflows.py +7 -2
  12. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp.egg-info/PKG-INFO +63 -62
  13. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp.egg-info/SOURCES.txt +0 -10
  14. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp.egg-info/requires.txt +62 -61
  15. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/pyproject.toml +64 -63
  16. dkist_processing_visp-5.0.0rc2/changelog/246.doc.2.rst +0 -2
  17. dkist_processing_visp-5.0.0rc2/changelog/246.doc.rst +0 -2
  18. dkist_processing_visp-5.0.0rc2/changelog/246.feature.2.rst +0 -1
  19. dkist_processing_visp-5.0.0rc2/changelog/246.feature.3.rst +0 -1
  20. dkist_processing_visp-5.0.0rc2/changelog/246.feature.4.rst +0 -1
  21. dkist_processing_visp-5.0.0rc2/changelog/246.feature.rst +0 -4
  22. dkist_processing_visp-5.0.0rc2/changelog/246.misc.2.rst +0 -1
  23. dkist_processing_visp-5.0.0rc2/changelog/246.misc.3.rst +0 -1
  24. dkist_processing_visp-5.0.0rc2/changelog/246.misc.rst +0 -2
  25. dkist_processing_visp-5.0.0rc2/changelog/246.science.rst +0 -11
  26. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/.gitignore +0 -0
  27. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/.pre-commit-config.yaml +0 -0
  28. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/.readthedocs.yml +0 -0
  29. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/.snyk +0 -0
  30. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/README.rst +0 -0
  31. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/bitbucket-pipelines.yml +0 -0
  32. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/changelog/.gitempty +0 -0
  33. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/__init__.py +0 -0
  34. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/config.py +0 -0
  35. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/fonts/Lato-Regular.ttf +0 -0
  36. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/models/__init__.py +0 -0
  37. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/models/constants.py +0 -0
  38. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/models/fits_access.py +0 -0
  39. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/models/metric_code.py +0 -0
  40. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/models/tags.py +0 -0
  41. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/models/task_name.py +0 -0
  42. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/parsers/__init__.py +0 -0
  43. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/parsers/map_repeats.py +0 -0
  44. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/parsers/modulator_states.py +0 -0
  45. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/parsers/polarimeter_mode.py +0 -0
  46. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/parsers/raster_step.py +0 -0
  47. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/parsers/spectrograph_configuration.py +0 -0
  48. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/parsers/time.py +0 -0
  49. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/parsers/visp_l0_fits_access.py +0 -0
  50. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/parsers/visp_l1_fits_access.py +0 -0
  51. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tasks/__init__.py +0 -0
  52. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tasks/assemble_movie.py +0 -0
  53. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tasks/background_light.py +0 -0
  54. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tasks/dark.py +0 -0
  55. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tasks/instrument_polarization.py +0 -0
  56. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tasks/l1_output_data.py +0 -0
  57. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tasks/lamp.py +0 -0
  58. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tasks/make_movie_frames.py +0 -0
  59. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tasks/mixin/__init__.py +0 -0
  60. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tasks/mixin/beam_access.py +0 -0
  61. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tasks/mixin/corrections.py +0 -0
  62. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tasks/mixin/downsample.py +0 -0
  63. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tasks/parse.py +0 -0
  64. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tasks/quality_metrics.py +0 -0
  65. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tasks/science.py +0 -0
  66. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tasks/visp_base.py +0 -0
  67. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tasks/write_l1.py +0 -0
  68. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tests/README.rst +0 -0
  69. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tests/__init__.py +0 -0
  70. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tests/header_models.py +0 -0
  71. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tests/local_trial_workflows/__init__.py +0 -0
  72. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tests/local_trial_workflows/l0_cals_only.py +0 -0
  73. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tests/local_trial_workflows/l0_polcals_as_science.py +0 -0
  74. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tests/local_trial_workflows/l0_solar_gain_as_science.py +0 -0
  75. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tests/local_trial_workflows/l0_to_l1.py +0 -0
  76. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tests/local_trial_workflows/local_trial_helpers.py +0 -0
  77. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tests/test_assemble_movie.py +0 -0
  78. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tests/test_assemble_quality.py +0 -0
  79. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tests/test_background_light.py +0 -0
  80. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tests/test_dark.py +0 -0
  81. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tests/test_downsample.py +0 -0
  82. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tests/test_fits_access.py +0 -0
  83. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tests/test_geometric.py +0 -0
  84. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tests/test_instrument_polarization.py +0 -0
  85. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tests/test_lamp.py +0 -0
  86. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tests/test_make_movie_frames.py +0 -0
  87. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tests/test_map_repeats.py +0 -0
  88. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tests/test_parse.py +0 -0
  89. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tests/test_quality.py +0 -0
  90. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tests/test_science.py +0 -0
  91. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tests/test_trial_create_quality_report.py +0 -0
  92. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tests/test_visp_constants.py +0 -0
  93. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tests/test_workflows.py +0 -0
  94. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/tests/test_write_l1.py +0 -0
  95. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/workflows/__init__.py +0 -0
  96. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp/workflows/single_task_workflows.py +0 -0
  97. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp.egg-info/dependency_links.txt +0 -0
  98. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/dkist_processing_visp.egg-info/top_level.txt +0 -0
  99. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/docs/Makefile +0 -0
  100. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/docs/background_light.rst +0 -0
  101. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/docs/changelog.rst +0 -0
  102. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/docs/conf.py +0 -0
  103. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/docs/gain_correction.rst +0 -0
  104. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/docs/geometric.rst +0 -0
  105. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/docs/index.rst +0 -0
  106. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/docs/l0_to_l1_visp.rst +0 -0
  107. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/docs/l0_to_l1_visp_full-trial.rst +0 -0
  108. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/docs/landing_page.rst +0 -0
  109. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/docs/make.bat +0 -0
  110. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/docs/polarization_calibration.rst +0 -0
  111. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/docs/requirements.txt +0 -0
  112. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/docs/requirements_table.rst +0 -0
  113. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/docs/science_calibration.rst +0 -0
  114. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/docs/scientific_changelog.rst +0 -0
  115. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/licenses/LICENSE.rst +0 -0
  116. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/science_towncrier.sh +0 -0
  117. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/setup.cfg +0 -0
  118. {dkist_processing_visp-5.0.0rc2 → dkist_processing_visp-5.1.0}/towncrier_science.toml +0 -0
@@ -1,3 +1,46 @@
1
+ v5.1.0 (2025-12-03)
2
+ ===================
3
+
4
+ Misc
5
+ ----
6
+
7
+ - Update workflows to remove `InstrumentPolarizationCalibration`'s dependency on `SolarCalibration`. These two tasks can
8
+ now run in parallel. (`#248 <https://bitbucket.org/dkistdc/dkist-processing-visp/pull-requests/248>`__)
9
+
10
+
11
+ v5.0.0 (2025-12-02)
12
+ ===================
13
+
14
+ Features
15
+ --------
16
+
17
+ - Only compute a single lamp gain for each beam. In the past a single map gain was also computed for each modulation state. (`#246 <https://bitbucket.org/dkistdc/dkist-processing-visp/pull-requests/246>`__)
18
+ - Add quality report metrics that show the quality of the vignette estimation in the Solar Gain tasks. (`#246 <https://bitbucket.org/dkistdc/dkist-processing-visp/pull-requests/246>`__)
19
+ - Update the Solar Gain task to measure vignetting signal, compute a single gain for each beam (as opposed to for each beam and modulator state),
20
+ and stop applying residual spectral shifts to the characteristic spectra before removing from the input gain array.
21
+ See the `Science Changelog <https://docs.dkist.nso.edu/projects/visp/en/stable/scientific_changelog.html>`_ and
22
+ `gain algorithm docs <https://docs.dkist.nso.edu/projects/visp/en/stable/gain_correction.html>`_ for more information. (`#246 <https://bitbucket.org/dkistdc/dkist-processing-visp/pull-requests/246>`__)
23
+ - Add Buds to parse the spectrograph setup into pipeline constants to be used when fitting a solar atlas. (`#246 <https://bitbucket.org/dkistdc/dkist-processing-visp/pull-requests/246>`__)
24
+
25
+
26
+ Misc
27
+ ----
28
+
29
+ - Remove the `LineZonesMixin` and move its methods directly to the Geometric calibration task, which is now the only task that uses them. (`#246 <https://bitbucket.org/dkistdc/dkist-processing-visp/pull-requests/246>`__)
30
+ - Rename the ``solar_spectral_avg_window`` to ``solar_spatial_median_filter_width_px`` to more accurately capture what it does. (`#246 <https://bitbucket.org/dkistdc/dkist-processing-visp/pull-requests/246>`__)
31
+ - Rename all line-zone parameters to be "geo*" parameters instead of "solar*" parameters. These parameters are now only used in the
32
+ Geometric calibration task. (`#246 <https://bitbucket.org/dkistdc/dkist-processing-visp/pull-requests/246>`__)
33
+
34
+
35
+ Documentation
36
+ -------------
37
+
38
+ - Update the online doc page for `gain calibration <https://docs.dkist.nso.edu/projects/visp/en/stable/gain_correction.html>`_
39
+ to reflect the updates to the Solar Gain task. (`#246 <https://bitbucket.org/dkistdc/dkist-processing-visp/pull-requests/246>`__)
40
+ - Add intersphinx mappings for `lmfit <https://lmfit.github.io/lmfit-py/>`_ and `scikit-learn <https://scikit-learn.org/stable/>`_
41
+ for linking bliss. (`#246 <https://bitbucket.org/dkistdc/dkist-processing-visp/pull-requests/246>`__)
42
+
43
+
1
44
  v4.0.0 (2025-11-12)
2
45
  ===================
3
46
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dkist-processing-visp
3
- Version: 5.0.0rc2
3
+ Version: 5.1.0
4
4
  Summary: Science processing code for the ViSP instrument on DKIST
5
5
  Author-email: NSO / AURA <dkistdc@nso.edu>
6
6
  License: BSD-3-Clause
@@ -13,14 +13,14 @@ Classifier: Programming Language :: Python :: 3
13
13
  Classifier: Programming Language :: Python :: 3.12
14
14
  Requires-Python: >=3.12
15
15
  Description-Content-Type: text/x-rst
16
- Requires-Dist: dkist-processing-common==11.8.1rc2
16
+ Requires-Dist: dkist-processing-common==11.8.1
17
17
  Requires-Dist: dkist-processing-math==2.2.1
18
18
  Requires-Dist: dkist-processing-pac==3.1.1
19
19
  Requires-Dist: dkist-header-validator==5.2.1
20
20
  Requires-Dist: dkist-fits-specifications==4.17.0
21
21
  Requires-Dist: dkist-service-configuration==4.1.7
22
22
  Requires-Dist: dkist-spectral-lines==3.0.0
23
- Requires-Dist: solar-wavelength-calibration==2.0.0rc3
23
+ Requires-Dist: solar-wavelength-calibration==2.0.0
24
24
  Requires-Dist: astropy==7.0.2
25
25
  Requires-Dist: numpy==2.2.5
26
26
  Requires-Dist: sunpy==6.1.1
@@ -95,44 +95,44 @@ Requires-Dist: aiohappyeyeballs==2.6.1; extra == "frozen"
95
95
  Requires-Dist: aiohttp==3.13.2; extra == "frozen"
96
96
  Requires-Dist: aiosignal==1.4.0; extra == "frozen"
97
97
  Requires-Dist: aiosmtplib==5.0.0; extra == "frozen"
98
- Requires-Dist: alembic==1.17.1; extra == "frozen"
98
+ Requires-Dist: alembic==1.17.2; extra == "frozen"
99
99
  Requires-Dist: amqp==5.3.1; extra == "frozen"
100
100
  Requires-Dist: annotated-types==0.7.0; extra == "frozen"
101
- Requires-Dist: anyio==4.11.0; extra == "frozen"
101
+ Requires-Dist: anyio==4.12.0; extra == "frozen"
102
102
  Requires-Dist: apache-airflow==2.11.0; extra == "frozen"
103
- Requires-Dist: apache-airflow-providers-celery==3.10.0; extra == "frozen"
104
- Requires-Dist: apache-airflow-providers-common-compat==1.8.0; extra == "frozen"
105
- Requires-Dist: apache-airflow-providers-common-io==1.6.4; extra == "frozen"
106
- Requires-Dist: apache-airflow-providers-common-sql==1.28.2; extra == "frozen"
103
+ Requires-Dist: apache-airflow-providers-celery==3.14.0; extra == "frozen"
104
+ Requires-Dist: apache-airflow-providers-common-compat==1.10.0; extra == "frozen"
105
+ Requires-Dist: apache-airflow-providers-common-io==1.7.0; extra == "frozen"
106
+ Requires-Dist: apache-airflow-providers-common-sql==1.30.0; extra == "frozen"
107
107
  Requires-Dist: apache-airflow-providers-fab==1.5.3; extra == "frozen"
108
- Requires-Dist: apache-airflow-providers-ftp==3.13.2; extra == "frozen"
109
- Requires-Dist: apache-airflow-providers-http==5.4.0; extra == "frozen"
110
- Requires-Dist: apache-airflow-providers-imap==3.9.3; extra == "frozen"
111
- Requires-Dist: apache-airflow-providers-postgres==6.4.0; extra == "frozen"
112
- Requires-Dist: apache-airflow-providers-smtp==2.3.1; extra == "frozen"
113
- Requires-Dist: apache-airflow-providers-sqlite==4.1.2; extra == "frozen"
114
- Requires-Dist: apispec==6.8.4; extra == "frozen"
108
+ Requires-Dist: apache-airflow-providers-ftp==3.14.0; extra == "frozen"
109
+ Requires-Dist: apache-airflow-providers-http==5.6.0; extra == "frozen"
110
+ Requires-Dist: apache-airflow-providers-imap==3.10.0; extra == "frozen"
111
+ Requires-Dist: apache-airflow-providers-postgres==6.5.0; extra == "frozen"
112
+ Requires-Dist: apache-airflow-providers-smtp==2.4.0; extra == "frozen"
113
+ Requires-Dist: apache-airflow-providers-sqlite==4.2.0; extra == "frozen"
114
+ Requires-Dist: apispec==6.9.0; extra == "frozen"
115
115
  Requires-Dist: argcomplete==3.6.3; extra == "frozen"
116
116
  Requires-Dist: asdf==3.5.0; extra == "frozen"
117
117
  Requires-Dist: asdf_standard==1.4.0; extra == "frozen"
118
118
  Requires-Dist: asdf_transform_schemas==0.6.0; extra == "frozen"
119
- Requires-Dist: asgiref==3.10.0; extra == "frozen"
119
+ Requires-Dist: asgiref==3.11.0; extra == "frozen"
120
120
  Requires-Dist: asteval==1.0.7; extra == "frozen"
121
121
  Requires-Dist: astropy==7.0.2; extra == "frozen"
122
- Requires-Dist: astropy-iers-data==0.2025.11.10.0.38.31; extra == "frozen"
123
- Requires-Dist: asyncpg==0.30.0; extra == "frozen"
122
+ Requires-Dist: astropy-iers-data==0.2025.12.1.0.45.12; extra == "frozen"
123
+ Requires-Dist: asyncpg==0.31.0; extra == "frozen"
124
124
  Requires-Dist: attrs==25.4.0; extra == "frozen"
125
125
  Requires-Dist: babel==2.17.0; extra == "frozen"
126
- Requires-Dist: billiard==4.2.2; extra == "frozen"
126
+ Requires-Dist: billiard==4.2.4; extra == "frozen"
127
127
  Requires-Dist: blinker==1.9.0; extra == "frozen"
128
- Requires-Dist: boto3==1.40.72; extra == "frozen"
129
- Requires-Dist: botocore==1.40.72; extra == "frozen"
128
+ Requires-Dist: boto3==1.42.1; extra == "frozen"
129
+ Requires-Dist: botocore==1.42.1; extra == "frozen"
130
130
  Requires-Dist: cachelib==0.13.0; extra == "frozen"
131
- Requires-Dist: celery==5.3.1; extra == "frozen"
131
+ Requires-Dist: celery==5.6.0; extra == "frozen"
132
132
  Requires-Dist: certifi==2025.11.12; extra == "frozen"
133
133
  Requires-Dist: cffi==2.0.0; extra == "frozen"
134
134
  Requires-Dist: charset-normalizer==3.4.4; extra == "frozen"
135
- Requires-Dist: click==8.3.0; extra == "frozen"
135
+ Requires-Dist: click==8.3.1; extra == "frozen"
136
136
  Requires-Dist: click-didyoumean==0.3.1; extra == "frozen"
137
137
  Requires-Dist: click-plugins==1.1.1.2; extra == "frozen"
138
138
  Requires-Dist: click-repl==0.3.0; extra == "frozen"
@@ -149,21 +149,22 @@ Requires-Dist: dacite==1.9.2; extra == "frozen"
149
149
  Requires-Dist: decorator==5.2.1; extra == "frozen"
150
150
  Requires-Dist: dill==0.4.0; extra == "frozen"
151
151
  Requires-Dist: dkist-header-validator==5.2.1; extra == "frozen"
152
- Requires-Dist: dkist-processing-common==11.8.1rc2; extra == "frozen"
152
+ Requires-Dist: dkist-processing-common==11.8.1; extra == "frozen"
153
153
  Requires-Dist: dkist-processing-core==6.0.0; extra == "frozen"
154
154
  Requires-Dist: dkist-processing-math==2.2.1; extra == "frozen"
155
155
  Requires-Dist: dkist-processing-pac==3.1.1; extra == "frozen"
156
- Requires-Dist: dkist-processing-visp==5.0.0rc2; extra == "frozen"
156
+ Requires-Dist: dkist-processing-visp==5.1.0; extra == "frozen"
157
157
  Requires-Dist: dkist-service-configuration==4.1.7; extra == "frozen"
158
158
  Requires-Dist: dkist-spectral-lines==3.0.0; extra == "frozen"
159
159
  Requires-Dist: dkist_fits_specifications==4.17.0; extra == "frozen"
160
160
  Requires-Dist: dnspython==2.8.0; extra == "frozen"
161
161
  Requires-Dist: email-validator==2.3.0; extra == "frozen"
162
+ Requires-Dist: exceptiongroup==1.3.1; extra == "frozen"
162
163
  Requires-Dist: fastjsonschema==2.21.2; extra == "frozen"
163
164
  Requires-Dist: flower==2.0.1; extra == "frozen"
164
- Requires-Dist: fonttools==4.60.1; extra == "frozen"
165
+ Requires-Dist: fonttools==4.61.0; extra == "frozen"
165
166
  Requires-Dist: frozenlist==1.8.0; extra == "frozen"
166
- Requires-Dist: fsspec==2025.10.0; extra == "frozen"
167
+ Requires-Dist: fsspec==2025.12.0; extra == "frozen"
167
168
  Requires-Dist: globus-sdk==3.65.0; extra == "frozen"
168
169
  Requires-Dist: google-re2==1.1.20251105; extra == "frozen"
169
170
  Requires-Dist: googleapis-common-protos==1.72.0; extra == "frozen"
@@ -186,7 +187,7 @@ Requires-Dist: jsonschema==4.25.1; extra == "frozen"
186
187
  Requires-Dist: jsonschema-specifications==2025.9.1; extra == "frozen"
187
188
  Requires-Dist: jupyter_core==5.9.1; extra == "frozen"
188
189
  Requires-Dist: kiwisolver==1.4.9; extra == "frozen"
189
- Requires-Dist: kombu==5.6.0; extra == "frozen"
190
+ Requires-Dist: kombu==5.6.1; extra == "frozen"
190
191
  Requires-Dist: lazy-object-proxy==1.12.0; extra == "frozen"
191
192
  Requires-Dist: lazy_loader==0.4; extra == "frozen"
192
193
  Requires-Dist: limits==5.6.0; extra == "frozen"
@@ -206,33 +207,33 @@ Requires-Dist: more-itertools==10.8.0; extra == "frozen"
206
207
  Requires-Dist: moviepy==2.1.2; extra == "frozen"
207
208
  Requires-Dist: multidict==6.7.0; extra == "frozen"
208
209
  Requires-Dist: nbformat==5.10.4; extra == "frozen"
209
- Requires-Dist: networkx==3.5; extra == "frozen"
210
+ Requires-Dist: networkx==3.6; extra == "frozen"
210
211
  Requires-Dist: numpy==2.2.5; extra == "frozen"
211
212
  Requires-Dist: object-clerk==1.0.0; extra == "frozen"
212
- Requires-Dist: opentelemetry-api==1.38.0; extra == "frozen"
213
- Requires-Dist: opentelemetry-exporter-otlp==1.38.0; extra == "frozen"
214
- Requires-Dist: opentelemetry-exporter-otlp-proto-common==1.38.0; extra == "frozen"
215
- Requires-Dist: opentelemetry-exporter-otlp-proto-grpc==1.38.0; extra == "frozen"
216
- Requires-Dist: opentelemetry-exporter-otlp-proto-http==1.38.0; extra == "frozen"
217
- Requires-Dist: opentelemetry-instrumentation==0.59b0; extra == "frozen"
218
- Requires-Dist: opentelemetry-instrumentation-aiohttp-client==0.59b0; extra == "frozen"
219
- Requires-Dist: opentelemetry-instrumentation-asgi==0.59b0; extra == "frozen"
220
- Requires-Dist: opentelemetry-instrumentation-botocore==0.59b0; extra == "frozen"
221
- Requires-Dist: opentelemetry-instrumentation-celery==0.59b0; extra == "frozen"
222
- Requires-Dist: opentelemetry-instrumentation-dbapi==0.59b0; extra == "frozen"
223
- Requires-Dist: opentelemetry-instrumentation-fastapi==0.59b0; extra == "frozen"
224
- Requires-Dist: opentelemetry-instrumentation-pika==0.59b0; extra == "frozen"
225
- Requires-Dist: opentelemetry-instrumentation-psycopg2==0.59b0; extra == "frozen"
226
- Requires-Dist: opentelemetry-instrumentation-pymongo==0.59b0; extra == "frozen"
227
- Requires-Dist: opentelemetry-instrumentation-redis==0.59b0; extra == "frozen"
228
- Requires-Dist: opentelemetry-instrumentation-requests==0.59b0; extra == "frozen"
229
- Requires-Dist: opentelemetry-instrumentation-sqlalchemy==0.59b0; extra == "frozen"
230
- Requires-Dist: opentelemetry-instrumentation-system-metrics==0.59b0; extra == "frozen"
213
+ Requires-Dist: opentelemetry-api==1.39.0; extra == "frozen"
214
+ Requires-Dist: opentelemetry-exporter-otlp==1.39.0; extra == "frozen"
215
+ Requires-Dist: opentelemetry-exporter-otlp-proto-common==1.39.0; extra == "frozen"
216
+ Requires-Dist: opentelemetry-exporter-otlp-proto-grpc==1.39.0; extra == "frozen"
217
+ Requires-Dist: opentelemetry-exporter-otlp-proto-http==1.39.0; extra == "frozen"
218
+ Requires-Dist: opentelemetry-instrumentation==0.60b0; extra == "frozen"
219
+ Requires-Dist: opentelemetry-instrumentation-aiohttp-client==0.60b0; extra == "frozen"
220
+ Requires-Dist: opentelemetry-instrumentation-asgi==0.60b0; extra == "frozen"
221
+ Requires-Dist: opentelemetry-instrumentation-botocore==0.60b0; extra == "frozen"
222
+ Requires-Dist: opentelemetry-instrumentation-celery==0.60b0; extra == "frozen"
223
+ Requires-Dist: opentelemetry-instrumentation-dbapi==0.60b0; extra == "frozen"
224
+ Requires-Dist: opentelemetry-instrumentation-fastapi==0.60b0; extra == "frozen"
225
+ Requires-Dist: opentelemetry-instrumentation-pika==0.60b0; extra == "frozen"
226
+ Requires-Dist: opentelemetry-instrumentation-psycopg2==0.60b0; extra == "frozen"
227
+ Requires-Dist: opentelemetry-instrumentation-pymongo==0.60b0; extra == "frozen"
228
+ Requires-Dist: opentelemetry-instrumentation-redis==0.60b0; extra == "frozen"
229
+ Requires-Dist: opentelemetry-instrumentation-requests==0.60b0; extra == "frozen"
230
+ Requires-Dist: opentelemetry-instrumentation-sqlalchemy==0.60b0; extra == "frozen"
231
+ Requires-Dist: opentelemetry-instrumentation-system-metrics==0.60b0; extra == "frozen"
231
232
  Requires-Dist: opentelemetry-propagator-aws-xray==1.0.2; extra == "frozen"
232
- Requires-Dist: opentelemetry-proto==1.38.0; extra == "frozen"
233
- Requires-Dist: opentelemetry-sdk==1.38.0; extra == "frozen"
234
- Requires-Dist: opentelemetry-semantic-conventions==0.59b0; extra == "frozen"
235
- Requires-Dist: opentelemetry-util-http==0.59b0; extra == "frozen"
233
+ Requires-Dist: opentelemetry-proto==1.39.0; extra == "frozen"
234
+ Requires-Dist: opentelemetry-sdk==1.39.0; extra == "frozen"
235
+ Requires-Dist: opentelemetry-semantic-conventions==0.60b0; extra == "frozen"
236
+ Requires-Dist: opentelemetry-util-http==0.60b0; extra == "frozen"
236
237
  Requires-Dist: ordered-set==4.1.0; extra == "frozen"
237
238
  Requires-Dist: packaging==25.0; extra == "frozen"
238
239
  Requires-Dist: pandas==2.3.3; extra == "frozen"
@@ -251,11 +252,11 @@ Requires-Dist: proglog==0.1.12; extra == "frozen"
251
252
  Requires-Dist: prometheus_client==0.23.1; extra == "frozen"
252
253
  Requires-Dist: prompt_toolkit==3.0.52; extra == "frozen"
253
254
  Requires-Dist: propcache==0.4.1; extra == "frozen"
254
- Requires-Dist: protobuf==6.33.0; extra == "frozen"
255
+ Requires-Dist: protobuf==6.33.1; extra == "frozen"
255
256
  Requires-Dist: psutil==7.1.3; extra == "frozen"
256
257
  Requires-Dist: psycopg2-binary==2.9.11; extra == "frozen"
257
258
  Requires-Dist: pycparser==2.23; extra == "frozen"
258
- Requires-Dist: pydantic==2.12.4; extra == "frozen"
259
+ Requires-Dist: pydantic==2.12.5; extra == "frozen"
259
260
  Requires-Dist: pydantic-settings==2.12.0; extra == "frozen"
260
261
  Requires-Dist: pydantic_core==2.41.5; extra == "frozen"
261
262
  Requires-Dist: pyerfa==2.0.1.5; extra == "frozen"
@@ -273,18 +274,17 @@ Requires-Dist: requests-toolbelt==1.0.0; extra == "frozen"
273
274
  Requires-Dist: rfc3339-validator==0.1.4; extra == "frozen"
274
275
  Requires-Dist: rich==13.9.4; extra == "frozen"
275
276
  Requires-Dist: rich-argparse==1.7.2; extra == "frozen"
276
- Requires-Dist: rpds-py==0.28.0; extra == "frozen"
277
- Requires-Dist: s3transfer==0.14.0; extra == "frozen"
277
+ Requires-Dist: rpds-py==0.30.0; extra == "frozen"
278
+ Requires-Dist: s3transfer==0.16.0; extra == "frozen"
278
279
  Requires-Dist: scikit-image==0.25.2; extra == "frozen"
279
280
  Requires-Dist: scikit-learn==1.6.1; extra == "frozen"
280
281
  Requires-Dist: scipy==1.15.3; extra == "frozen"
281
282
  Requires-Dist: semantic-version==2.10.0; extra == "frozen"
282
283
  Requires-Dist: setproctitle==1.3.7; extra == "frozen"
283
284
  Requires-Dist: six==1.17.0; extra == "frozen"
284
- Requires-Dist: sniffio==1.3.1; extra == "frozen"
285
- Requires-Dist: solar-wavelength-calibration==2.0.0rc3; extra == "frozen"
285
+ Requires-Dist: solar-wavelength-calibration==2.0.0; extra == "frozen"
286
286
  Requires-Dist: sqids==0.5.1; extra == "frozen"
287
- Requires-Dist: sqlparse==0.5.3; extra == "frozen"
287
+ Requires-Dist: sqlparse==0.5.4; extra == "frozen"
288
288
  Requires-Dist: sunpy==6.1.1; extra == "frozen"
289
289
  Requires-Dist: tabulate==0.9.0; extra == "frozen"
290
290
  Requires-Dist: talus==1.3.4; extra == "frozen"
@@ -299,16 +299,17 @@ Requires-Dist: traitlets==5.14.3; extra == "frozen"
299
299
  Requires-Dist: typing-inspection==0.4.2; extra == "frozen"
300
300
  Requires-Dist: typing_extensions==4.15.0; extra == "frozen"
301
301
  Requires-Dist: tzdata==2025.2; extra == "frozen"
302
+ Requires-Dist: tzlocal==5.3.1; extra == "frozen"
302
303
  Requires-Dist: uc-micro-py==1.0.3; extra == "frozen"
303
304
  Requires-Dist: uncertainties==3.2.3; extra == "frozen"
304
- Requires-Dist: universal_pathlib==0.3.5; extra == "frozen"
305
+ Requires-Dist: universal_pathlib==0.3.7; extra == "frozen"
305
306
  Requires-Dist: urllib3==2.5.0; extra == "frozen"
306
307
  Requires-Dist: vine==5.1.0; extra == "frozen"
307
308
  Requires-Dist: voluptuous==0.15.2; extra == "frozen"
308
309
  Requires-Dist: wcwidth==0.2.14; extra == "frozen"
309
310
  Requires-Dist: wirerope==1.0.0; extra == "frozen"
310
311
  Requires-Dist: wrapt==1.17.3; extra == "frozen"
311
- Requires-Dist: yamale==6.0.0; extra == "frozen"
312
+ Requires-Dist: yamale==6.1.0; extra == "frozen"
312
313
  Requires-Dist: yarl==1.22.0; extra == "frozen"
313
314
  Requires-Dist: zipp==3.23.0; extra == "frozen"
314
315
 
@@ -1,3 +1,22 @@
1
+ v5.0.0 (2025-12-02)
2
+ ===================
3
+
4
+
5
+
6
+
7
+ - Major update to the gain algorithm to mitigate polarization artifacts in L1 science frames. This change can be split into
8
+ three parts. See the `gain correction <https://docs.dkist.nso.edu/projects/visp/en/stable/gain_correction.html>`_ page for more information.
9
+
10
+ #. Compute a single gain correction image for each beam. In the past we had computed a separate gain image for each modstate as well, but this was found to couple residual polarization structure into the final science frames.
11
+
12
+ #. Stop "refining" (i.e., applying small spectral offsets to) the characteristic solar spectrum on a per-spatial-pixel basis. This was initially done to minimize line residuals in Telluric lines but was found to have a more-negative on polarimetric residuals.
13
+
14
+ #. Separate vignetting caused by aperture masks from the solar signal used to remove spectral lines in the final gain image. This is done by fitting a solar atlas and taking the deviation in continuum to be the vignetting signal.
15
+
16
+ Computing a single gain per beam will greatly reduce the polarization artifacts in L1 data. Measuring the vignette
17
+ signal directly will further reduce these artifacts and improve flatness across the array. (`#246 <https://bitbucket.org/dkistdc/dkist-processing-visp/pull-requests/246>`__)
18
+
19
+
1
20
  v4.0.0 (2025-11-12)
2
21
  ===================
3
22
 
@@ -1,6 +1,7 @@
1
1
  """Visp calibration pipeline parameters."""
2
2
 
3
3
  from datetime import datetime
4
+ from random import randint
4
5
  from typing import Any
5
6
 
6
7
  import astropy.units as u
@@ -186,10 +187,38 @@ class VispParameters(ParameterBase, ParameterWavelengthMixin, ParameterArmIdMixi
186
187
  "visp_solar_vignette_initial_continuum_poly_fit_order"
187
188
  )
188
189
 
190
+ @property
191
+ def solar_vignette_crval_bounds_px(self) -> float:
192
+ """
193
+ Define the bounds (in *pixels*) on crval when fitting the initial vignette signal.
194
+
195
+ The actual bounds on the value of crval are equal to ± the initial dispersion times this number. Note that the
196
+ total range searched by the fitting algorithm will be twice this number (in pixels).
197
+ """
198
+ return self._find_most_recent_past_value("visp_solar_vignette_crval_bounds_px") * u.pix
199
+
200
+ @property
201
+ def solar_vignette_dispersion_bounds_fraction(self) -> float:
202
+ """
203
+ Define the ± fraction away from the initial value for bounds on dispersion when fitting the initial vignette signal.
204
+
205
+ This value should be between 0 and 1. For example, the minimum bound is `init_value * (1 - solar_vignette_dispersion_bounds_fraction)`.
206
+ """
207
+ return self._find_most_recent_past_value("visp_solar_vignette_dispersion_bounds_fraction")
208
+
189
209
  @property
190
210
  def solar_vignette_wavecal_fit_kwargs(self) -> dict[str, Any]:
191
211
  """Define extra keyword arguments to pass to the wavelength calibration fitter."""
192
- return self._find_most_recent_past_value("visp_solar_vignette_wavecal_fit_kwargs")
212
+ doc_dict = self._find_most_recent_past_value("visp_solar_vignette_wavecal_fit_kwargs")
213
+ rng_kwarg = dict()
214
+ fitting_method = doc_dict.get("method", False)
215
+ if fitting_method in ["basinhopping", "differential_evolution", "dual_annealing"]:
216
+ rng = randint(1, 1_000_000)
217
+ rng_kwarg["rng"] = rng
218
+
219
+ # The order here allows us to override `rng` in a parameter value
220
+ fit_kwargs = rng_kwarg | doc_dict
221
+ return fit_kwargs
193
222
 
194
223
  @property
195
224
  def solar_vignette_spectral_poly_fit_order(self) -> int:
@@ -228,6 +257,13 @@ class VispParameters(ParameterBase, ParameterWavelengthMixin, ParameterArmIdMixi
228
257
  config_dict = self._find_most_recent_past_value("visp_wavecal_atlas_download_config")
229
258
  return DownloadConfig.model_validate(config_dict)
230
259
 
260
+ @property
261
+ def wavecal_init_crval_guess_normalization_percentile(self) -> float | None:
262
+ """Define the CDF percentage used to normalize the Atlas to the input spectrum level when computing an initial CRVAL guess."""
263
+ return self._find_most_recent_past_value(
264
+ "visp_wavecal_init_crval_guess_normalization_percentile"
265
+ )
266
+
231
267
  @property
232
268
  def wavecal_init_resolving_power(self) -> int:
233
269
  """Define the initial guess for ViSP resolving power in wavecal fits."""
@@ -274,7 +274,12 @@ class GeometricCalibration(
274
274
  self.prep_input_solar_gain()
275
275
 
276
276
  def prep_lamp_gain(self):
277
- """Apply dark corrections fo INPUT lamp frames."""
277
+ """
278
+ Create average, dark-corrected lamp gain images for each modstate from INPUT lamp gains.
279
+
280
+ This is different from the results of the `~dkist_processing_visp.tasks.lamp.LampCalibration` task because in
281
+ that task the hairlines are masked out, but here we *need* the hairlines to compute the rotation angle.
282
+ """
278
283
  for readout_exp_time in self.constants.lamp_readout_exp_times:
279
284
  for beam in range(1, self.constants.num_beams + 1):
280
285
  logger.info(
@@ -70,6 +70,7 @@ class WavelengthCalibrationParametersWithContinuum(WavelengthCalibrationParamete
70
70
 
71
71
  continuum_poly_fit_order: int
72
72
  normalized_abscissa: np.ndarray
73
+ zeroth_order_continuum_coefficient: float
73
74
 
74
75
  @property
75
76
  def continuum_function(self) -> Callable[[np.ndarray, Parameters], np.ndarray]:
@@ -93,16 +94,26 @@ class WavelengthCalibrationParametersWithContinuum(WavelengthCalibrationParamete
93
94
  # dynamically create the required number of "poly_coeff_{o:02n}" fields. Sorry if this bites you!
94
95
  params = super().lmfit_parameters
95
96
  for o in range(self.continuum_poly_fit_order + 1):
97
+ # `np.polyval` uses its input coefficient list "backwards", so `poly_coeff_{self.continuum_poly_fit_order}`
98
+ # is the 0th order polynomial term.
96
99
  params.add(
97
100
  f"poly_coeff_{o:02n}",
98
101
  vary=True,
99
- value=o // self.continuum_poly_fit_order,
102
+ value=(
103
+ self.zeroth_order_continuum_coefficient
104
+ if o == self.continuum_poly_fit_order
105
+ else 0
106
+ ),
100
107
  min=-1,
101
108
  max=1,
102
109
  )
103
110
 
104
- params[f"poly_coeff_{self.continuum_poly_fit_order:02n}"].min = 0.7
105
- params[f"poly_coeff_{self.continuum_poly_fit_order:02n}"].max = 1.3
111
+ params[f"poly_coeff_{self.continuum_poly_fit_order:02n}"].min = (
112
+ self.zeroth_order_continuum_coefficient * 0.5
113
+ )
114
+ params[f"poly_coeff_{self.continuum_poly_fit_order:02n}"].max = (
115
+ self.zeroth_order_continuum_coefficient * 1.5
116
+ )
106
117
 
107
118
  # Remove the default continuum parameterization
108
119
  del params["continuum_level"]
@@ -551,9 +562,11 @@ class SolarCalibration(
551
562
 
552
563
  fitter = WavelengthCalibrationFitter(input_parameters=init_parameters, atlas=atlas)
553
564
  with self.telemetry_span("Fit atlas and continuum"):
565
+ extra_kwargs = self.parameters.solar_vignette_wavecal_fit_kwargs
566
+ logger.info(f"Calling fitter with extra kwargs: {extra_kwargs}")
554
567
  fit_result = fitter(
555
568
  input_spectrum=representative_spectrum,
556
- **self.parameters.solar_vignette_wavecal_fit_kwargs,
569
+ **extra_kwargs,
557
570
  )
558
571
 
559
572
  return fit_result
@@ -610,6 +623,7 @@ class SolarCalibration(
610
623
  negative_limit=-wavelength_range / 2,
611
624
  positive_limit=wavelength_range / 2,
612
625
  num_steps=500,
626
+ normalization_percentile=self.parameters.wavecal_init_crval_guess_normalization_percentile,
613
627
  )
614
628
 
615
629
  logger.info(f"{crval_init = !s}")
@@ -627,18 +641,22 @@ class SolarCalibration(
627
641
  resolving_power = self.parameters.wavecal_init_resolving_power
628
642
  opacity_factor = self.parameters.wavecal_init_opacity_factor
629
643
  straylight_faction = self.parameters.wavecal_init_straylight_fraction
644
+ relative_atlas_scaling = self.estimate_relative_continuum_level(
645
+ crval_init=crval_init,
646
+ wavelength_range=wavelength_range,
647
+ atlas=atlas,
648
+ representative_spectrum=representative_spectrum,
649
+ )
650
+ logger.info(f"0th order coefficient initial guess: {relative_atlas_scaling}")
630
651
 
631
- # The first time we have to change these ranges (which may never happen!) we should at least consider making
632
- # them parameters.
633
- wavelength_search_width = wavelength_range * 0.15
652
+ wavelength_search_width = dispersion * self.parameters.solar_vignette_crval_bounds_px
634
653
  bounds = BoundsModel(
635
- # An alternative for the crval bounds would be `[input_wavelength_vector.min(), input_wavelength_vector.max()]`
636
654
  crval=LengthBoundRange(
637
655
  min=crval_init - wavelength_search_width, max=crval_init + wavelength_search_width
638
656
  ),
639
657
  dispersion=DispersionBoundRange(
640
- min=dispersion * 0.96,
641
- max=dispersion * 1.04,
658
+ min=dispersion * (1 - self.parameters.solar_vignette_dispersion_bounds_fraction),
659
+ max=dispersion * (1 + self.parameters.solar_vignette_dispersion_bounds_fraction),
642
660
  ),
643
661
  incident_light_angle=AngleBoundRange(
644
662
  min=incident_light_angle - 1 * u.deg, max=incident_light_angle + 1 * u.deg
@@ -646,7 +664,6 @@ class SolarCalibration(
646
664
  resolving_power=UnitlessBoundRange(min=1e5, max=5e5),
647
665
  opacity_factor=UnitlessBoundRange(min=1.0, max=10),
648
666
  straylight_fraction=UnitlessBoundRange(min=0.0, max=0.8),
649
- continuum_level=UnitlessBoundRange(min=0.99, max=1.1),
650
667
  )
651
668
 
652
669
  init_params = WavelengthCalibrationParametersWithContinuum(
@@ -656,7 +673,7 @@ class SolarCalibration(
656
673
  grating_constant=grating_constant,
657
674
  doppler_velocity=doppler_velocity,
658
675
  order=spectral_order,
659
- continuum_level=1.0,
676
+ continuum_level=1,
660
677
  resolving_power=resolving_power,
661
678
  opacity_factor=opacity_factor,
662
679
  straylight_fraction=straylight_faction,
@@ -664,10 +681,44 @@ class SolarCalibration(
664
681
  bounds=bounds,
665
682
  continuum_poly_fit_order=self.parameters.solar_vignette_initial_continuum_poly_fit_order,
666
683
  normalized_abscissa=normalized_abscissa,
684
+ zeroth_order_continuum_coefficient=relative_atlas_scaling,
667
685
  )
668
686
 
669
687
  return init_params
670
688
 
689
+ def estimate_relative_continuum_level(
690
+ self,
691
+ *,
692
+ crval_init: Quantity,
693
+ wavelength_range: Quantity,
694
+ atlas: Atlas,
695
+ representative_spectrum: np.ndarray,
696
+ ) -> float:
697
+ """
698
+ Estimate the multiplicative scaling between the representative spectrum and atlas solar transmission.
699
+
700
+ This scaling is used to set the initial guess of 0th-order polynomial fit coefficient. We estimate the scaling
701
+ factor by comparing the values of the two spectra at a given percent of the CDF. This percent is taken from
702
+ the `~dkist_processing_visp.models.parameters.VispParameters.wavecal_init_crval_guess_normalization_percentile`
703
+ pipeline parameter.
704
+ """
705
+ wave_min = crval_init - wavelength_range / 2
706
+ wave_max = crval_init + wavelength_range / 2
707
+
708
+ atlas_idx = np.where(
709
+ (atlas.solar_atlas_wavelength >= wave_min) & (atlas.solar_atlas_wavelength <= wave_max)
710
+ )
711
+ atlas_norm = np.nanpercentile(
712
+ atlas.solar_atlas_transmission[atlas_idx],
713
+ self.parameters.wavecal_init_crval_guess_normalization_percentile,
714
+ )
715
+ spec_norm = np.nanpercentile(
716
+ representative_spectrum,
717
+ self.parameters.wavecal_init_crval_guess_normalization_percentile,
718
+ )
719
+
720
+ return spec_norm / atlas_norm
721
+
671
722
  def compute_final_vignette_estimate(self, init_vignette_correction: np.ndarray) -> np.ndarray:
672
723
  """
673
724
  Fit the spectral shape of continuum residuals for each spatial pixel.
@@ -149,6 +149,8 @@ class VispInputDatasetParameterValues:
149
149
  )
150
150
  visp_solar_characteristic_spatial_normalization_percentile: float = 90.0
151
151
  visp_solar_vignette_initial_continuum_poly_fit_order: int = 6
152
+ visp_solar_vignette_crval_bounds_px: float = 7
153
+ visp_solar_vignette_dispersion_bounds_fraction: float = 0.02
152
154
  visp_solar_vignette_wavecal_fit_kwargs: dict[str, Any] = field(
153
155
  default_factory=lambda: {
154
156
  "method": "differential_evolution",
@@ -184,6 +186,7 @@ class VispInputDatasetParameterValues:
184
186
  "solar_reference_atlas_hash_id": "md5:84ab4c50689ef235fe5ed4f7ee905ca0",
185
187
  }
186
188
  )
189
+ visp_wavecal_init_crval_guess_normalization_percentile: float = 95
187
190
  visp_wavecal_init_resolving_power: int = 150000
188
191
  visp_wavecal_init_straylight_fraction: float = 0.2
189
192
  visp_wavecal_init_opacity_factor: float = 5.0
@@ -34,6 +34,7 @@ def arm_parameter_names() -> list[str]:
34
34
  @pytest.fixture(scope="session")
35
35
  def unit_parameter_names_and_units() -> dict[str, u.Unit | list[u.Unit]]:
36
36
  return {
37
+ "solar_vignette_crval_bounds_px": u.pix,
37
38
  "wavecal_camera_lens_parameters": [u.m, u.m / u.nm, u.m / u.nm**2],
38
39
  "wavecal_pixel_pitch_micron_per_pix": u.um / u.pix,
39
40
  }
@@ -102,7 +103,11 @@ def test_non_wave_parameters(
102
103
  pn = f"{pn}_{arm_id}"
103
104
  pv = expected[pn]
104
105
  is_wavelength_param = isinstance(pv, dict) and "wavelength" in pv
105
- if parameter_name not in parse_parameter_names and not is_wavelength_param:
106
+ if (
107
+ parameter_name not in parse_parameter_names
108
+ and not is_wavelength_param
109
+ and parameter_name != "solar_vignette_wavecal_fit_kwargs"
110
+ ):
106
111
  param_obj_value = getattr(task_param_attr, parameter_name)
107
112
  if isinstance(pv, tuple):
108
113
  pv = list(pv)
@@ -170,3 +175,47 @@ def test_wave_parameters(
170
175
  is_wavelength_param = isinstance(pv, dict) and "wavelength" in pv
171
176
  if is_wavelength_param and property_name not in parse_parameter_names + arm_parameter_names:
172
177
  assert getattr(task_param_attr, property_name) == pv["values"][idx]
178
+
179
+
180
+ class AnyInt:
181
+ pass
182
+
183
+
184
+ @pytest.mark.parametrize("arm_id", [pytest.param("1")])
185
+ @pytest.mark.parametrize(
186
+ "db_value, expected",
187
+ [
188
+ pytest.param({"method": "nelder"}, {"method": "nelder"}, id="non_rng_method"),
189
+ pytest.param(
190
+ {"method": "basinhopping"}, {"method": "basinhopping", "rng": AnyInt}, id="random_rng"
191
+ ),
192
+ pytest.param(
193
+ {"method": "differential_evolution", "rng": 6.28},
194
+ {"method": "differential_evolution", "rng": 6.28},
195
+ id="override_rng",
196
+ ),
197
+ pytest.param(dict(), dict(), id="no_kwargs"),
198
+ ],
199
+ )
200
+ def test_fit_kwarg_parameters(
201
+ basic_science_task_with_parameter_mixin,
202
+ db_value,
203
+ expected,
204
+ ):
205
+ """
206
+ Given: A Science task with the parameter mixin
207
+ When: Accessing properties for parameters that do not depend on wavelength
208
+ Then: The correct value is returned
209
+ """
210
+ task, _ = next(
211
+ basic_science_task_with_parameter_mixin(
212
+ VispInputDatasetParameterValues(visp_solar_vignette_wavecal_fit_kwargs=db_value)
213
+ )
214
+ )
215
+ kwarg_dict = task.parameters.solar_vignette_wavecal_fit_kwargs
216
+ assert kwarg_dict.keys() == expected.keys()
217
+ for k in expected.keys():
218
+ if expected[k] is AnyInt:
219
+ assert type(kwarg_dict[k]) is int
220
+ else:
221
+ assert expected[k] == kwarg_dict[k]