dkist-processing-cryonirsp 0.0.1rc2__tar.gz → 0.0.1rc4__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.

Potentially problematic release.


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

Files changed (115) hide show
  1. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/.readthedocs.yml +1 -1
  2. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/PKG-INFO +3 -3
  3. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/bitbucket-pipelines.yml +2 -2
  4. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/models/constants.py +26 -3
  5. dkist_processing_cryonirsp-0.0.1rc4/dkist_processing_cryonirsp/models/filter.py +46 -0
  6. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/models/parameters.py +47 -37
  7. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/parsers/cryonirsp_l0_fits_access.py +4 -0
  8. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/parsers/map_repeats.py +2 -3
  9. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/parsers/task.py +2 -7
  10. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/parsers/time.py +4 -3
  11. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tasks/assemble_movie.py +1 -1
  12. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tasks/bad_pixel_map.py +24 -24
  13. dkist_processing_cryonirsp-0.0.1rc4/dkist_processing_cryonirsp/tasks/beam_boundaries_base.py +199 -0
  14. dkist_processing_cryonirsp-0.0.1rc4/dkist_processing_cryonirsp/tasks/ci_beam_boundaries.py +58 -0
  15. dkist_processing_cryonirsp-0.0.1rc4/dkist_processing_cryonirsp/tasks/ci_science.py +164 -0
  16. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tasks/cryonirsp_base.py +4 -40
  17. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tasks/gain.py +12 -10
  18. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tasks/instrument_polarization.py +20 -36
  19. dkist_processing_cryonirsp-0.0.1rc4/dkist_processing_cryonirsp/tasks/linearity_correction.py +282 -0
  20. dkist_processing_cryonirsp-0.0.1rc4/dkist_processing_cryonirsp/tasks/make_movie_frames.py +314 -0
  21. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tasks/mixin/beam_access.py +10 -7
  22. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tasks/mixin/corrections.py +6 -7
  23. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tasks/mixin/intermediate_frame.py +1 -3
  24. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tasks/mixin/linearized_frame.py +0 -1
  25. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tasks/parse.py +35 -27
  26. dkist_processing_cryonirsp-0.0.1rc4/dkist_processing_cryonirsp/tasks/quality_metrics.py +254 -0
  27. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tasks/science_base.py +135 -163
  28. dkist_processing_cryonirsp-0.0.1rc4/dkist_processing_cryonirsp/tasks/sp_beam_boundaries.py +234 -0
  29. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tasks/sp_geometric.py +233 -170
  30. dkist_processing_cryonirsp-0.0.1rc4/dkist_processing_cryonirsp/tasks/sp_science.py +280 -0
  31. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tasks/sp_solar_gain.py +23 -30
  32. dkist_processing_cryonirsp-0.0.1rc4/dkist_processing_cryonirsp/tasks/write_l1.py +310 -0
  33. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tests/ci_e2e_test.py +14 -14
  34. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tests/conftest.py +119 -59
  35. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tests/sp_e2e_test.py +14 -15
  36. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tests/test_assemble_movie.py +0 -4
  37. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tests/test_bad_pixel_maps.py +7 -8
  38. dkist_processing_cryonirsp-0.0.1rc4/dkist_processing_cryonirsp/tests/test_ci_beam_boundaries.py +103 -0
  39. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tests/test_ci_make_movie_frames.py +2 -3
  40. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tests/test_ci_science.py +7 -8
  41. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tests/test_cryo_base.py +3 -7
  42. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tests/test_dark.py +0 -4
  43. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tests/test_gain.py +0 -6
  44. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tests/test_instrument_polarization.py +0 -8
  45. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tests/test_linearity_correction.py +11 -6
  46. dkist_processing_cryonirsp-0.0.1rc4/dkist_processing_cryonirsp/tests/test_parameters.py +184 -0
  47. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tests/test_parse.py +0 -19
  48. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tests/test_quality.py +8 -8
  49. dkist_processing_cryonirsp-0.0.1rc2/dkist_processing_cryonirsp/tests/test_beam_boundaries.py → dkist_processing_cryonirsp-0.0.1rc4/dkist_processing_cryonirsp/tests/test_sp_beam_boundaries.py +14 -25
  50. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tests/test_sp_geometric.py +0 -2
  51. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tests/test_sp_make_movie_frames.py +2 -3
  52. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tests/test_sp_science.py +9 -10
  53. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tests/test_sp_solar.py +0 -2
  54. dkist_processing_cryonirsp-0.0.1rc4/dkist_processing_cryonirsp/tests/test_write_l1.py +236 -0
  55. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/workflows/ci_l0_processing.py +4 -4
  56. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/workflows/sp_l0_processing.py +4 -4
  57. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp.egg-info/PKG-INFO +3 -3
  58. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp.egg-info/SOURCES.txt +7 -6
  59. dkist_processing_cryonirsp-0.0.1rc4/dkist_processing_cryonirsp.egg-info/requires.txt +40 -0
  60. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/setup.cfg +18 -18
  61. dkist_processing_cryonirsp-0.0.1rc2/dkist_processing_cryonirsp/models/spectral_line.py +0 -24
  62. dkist_processing_cryonirsp-0.0.1rc2/dkist_processing_cryonirsp/parsers/spectral_line.py +0 -36
  63. dkist_processing_cryonirsp-0.0.1rc2/dkist_processing_cryonirsp/tasks/beam_boundaries.py +0 -350
  64. dkist_processing_cryonirsp-0.0.1rc2/dkist_processing_cryonirsp/tasks/ci_make_movie_frames.py +0 -152
  65. dkist_processing_cryonirsp-0.0.1rc2/dkist_processing_cryonirsp/tasks/ci_science.py +0 -77
  66. dkist_processing_cryonirsp-0.0.1rc2/dkist_processing_cryonirsp/tasks/linearity_correction.py +0 -230
  67. dkist_processing_cryonirsp-0.0.1rc2/dkist_processing_cryonirsp/tasks/quality_metrics.py +0 -203
  68. dkist_processing_cryonirsp-0.0.1rc2/dkist_processing_cryonirsp/tasks/sp_make_movie_frames.py +0 -160
  69. dkist_processing_cryonirsp-0.0.1rc2/dkist_processing_cryonirsp/tasks/sp_science.py +0 -156
  70. dkist_processing_cryonirsp-0.0.1rc2/dkist_processing_cryonirsp/tasks/write_l1.py +0 -342
  71. dkist_processing_cryonirsp-0.0.1rc2/dkist_processing_cryonirsp/tests/test_parameters.py +0 -88
  72. dkist_processing_cryonirsp-0.0.1rc2/dkist_processing_cryonirsp/tests/test_write_l1.py +0 -305
  73. dkist_processing_cryonirsp-0.0.1rc2/dkist_processing_cryonirsp.egg-info/requires.txt +0 -40
  74. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/.gitignore +0 -0
  75. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/.pre-commit-config.yaml +0 -0
  76. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/CHANGELOG.rst +0 -0
  77. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/README.rst +0 -0
  78. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/__init__.py +0 -0
  79. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/models/__init__.py +0 -0
  80. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/models/tags.py +0 -0
  81. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/models/task_name.py +0 -0
  82. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/parsers/__init__.py +0 -0
  83. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/parsers/cryonirsp_l1_fits_access.py +0 -0
  84. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/parsers/measurements.py +0 -0
  85. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/parsers/modstates.py +0 -0
  86. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/parsers/polarimeter_mode.py +0 -0
  87. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/parsers/polcal_task.py +0 -0
  88. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/parsers/scan_step.py +0 -0
  89. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/parsers/wavelength.py +0 -0
  90. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tasks/__init__.py +0 -0
  91. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tasks/dark.py +0 -0
  92. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tasks/l1_output_data.py +0 -0
  93. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tasks/mixin/__init__.py +0 -0
  94. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tasks/mixin/input_frame.py +0 -0
  95. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tests/__init__.py +0 -0
  96. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tests/e2e_helpers.py +0 -0
  97. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tests/e2e_linearize.py +0 -0
  98. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tests/test_build_quality_report.py +0 -0
  99. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tests/test_cryo_constants.py +0 -0
  100. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tests/test_submit_qualilty.py +0 -0
  101. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/tests/test_workflows.py +0 -0
  102. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp/workflows/__init__.py +0 -0
  103. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp.egg-info/dependency_links.txt +0 -0
  104. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/dkist_processing_cryonirsp.egg-info/top_level.txt +0 -0
  105. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/docs/Makefile +0 -0
  106. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/docs/changelog.rst +0 -0
  107. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/docs/ci_l0_to_l1_cryonirsp.rst +0 -0
  108. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/docs/conf.py +0 -0
  109. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/docs/index.rst +0 -0
  110. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/docs/make.bat +0 -0
  111. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/docs/requirements.txt +0 -0
  112. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/docs/requirements_table.rst +0 -0
  113. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/docs/sp_l0_to_l1_cryonirsp.rst +0 -0
  114. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/licenses/LICENSE.rst +0 -0
  115. {dkist_processing_cryonirsp-0.0.1rc2 → dkist_processing_cryonirsp-0.0.1rc4}/setup.py +0 -0
@@ -2,7 +2,7 @@ version: 2
2
2
  build:
3
3
  os: ubuntu-20.04
4
4
  tools:
5
- python: "3.10"
5
+ python: "3.11"
6
6
  apt_packages:
7
7
  - libopenjp2-7
8
8
  - graphviz
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dkist_processing_cryonirsp
3
- Version: 0.0.1rc2
3
+ Version: 0.0.1rc4
4
4
  Summary: Science processing code for the Cryo-NIRSP instrument on DKIST
5
5
  Home-page: https://bitbucket.org/dkistdc/dkist_processing_cryonirsp/src/main/
6
6
  Author: NSO / AURA
@@ -8,8 +8,8 @@ Author-email: "dkistdc@nso.edu"
8
8
  License: MIT
9
9
  Classifier: Programming Language :: Python
10
10
  Classifier: Programming Language :: Python :: 3
11
- Classifier: Programming Language :: Python :: 3.10
12
- Requires-Python: >=3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Requires-Python: >=3.11
13
13
  Provides-Extra: test
14
14
  Provides-Extra: grogu
15
15
  Provides-Extra: docs
@@ -1,5 +1,5 @@
1
1
  #Build Configuration for docker deployment to artifactory
2
- image: python:3.10
2
+ image: python:3.11
3
3
 
4
4
  definitions:
5
5
  services:
@@ -40,7 +40,7 @@ definitions:
40
40
  script:
41
41
  - pip install -U pip
42
42
  - pip install .[test]
43
- - pytest -v --cov -m "not development" dkist_processing_cryonirsp
43
+ - pytest -v -n auto -m "not development" --cov dkist_processing_cryonirsp
44
44
  services:
45
45
  - redis
46
46
  - step: &push_workflow
@@ -26,6 +26,10 @@ class CryonirspBudName(Enum):
26
26
  axis_1_type = "AXIS_1_TYPE"
27
27
  axis_2_type = "AXIS_2_TYPE"
28
28
  axis_3_type = "AXIS_3_TYPE"
29
+ roi_1_origin_x = "ROI_1_ORIGIN_X"
30
+ roi_1_origin_y = "ROI_1_ORIGIN_Y"
31
+ roi_1_size_x = "ROI_1_SIZE_X"
32
+ roi_1_size_y = "ROI_1_SIZE_Y"
29
33
 
30
34
 
31
35
  class CryonirspConstants(ConstantsBase):
@@ -70,8 +74,8 @@ class CryonirspConstants(ConstantsBase):
70
74
  return self._db_dict[CryonirspBudName.num_meas.value]
71
75
 
72
76
  @property
73
- def time_obs_list(self) -> list[str]:
74
- """Construct a list of all the dateobs for this dataset."""
77
+ def time_obs_list(self) -> tuple[str]:
78
+ """Construct a sorted tuple of all the dateobs for this dataset."""
75
79
  return self._db_dict[CryonirspBudName.time_obs_list.value]
76
80
 
77
81
  @property
@@ -115,7 +119,6 @@ class CryonirspConstants(ConstantsBase):
115
119
  @property
116
120
  def num_cs_steps(self) -> int:
117
121
  """Find the number of calibration sequence steps."""
118
- # NB: Uses regular BudName
119
122
  return self._db_dict[BudName.num_cs_steps.value]
120
123
 
121
124
  @property
@@ -144,3 +147,23 @@ class CryonirspConstants(ConstantsBase):
144
147
  def axis_3_type(self) -> str:
145
148
  """Find the type of the third array axis."""
146
149
  return self._db_dict[CryonirspBudName.axis_3_type.value]
150
+
151
+ @property
152
+ def roi_1_origin_x(self) -> int:
153
+ """Get the ROI #1 x origin."""
154
+ return self._db_dict[CryonirspBudName.roi_1_origin_x.value]
155
+
156
+ @property
157
+ def roi_1_origin_y(self) -> int:
158
+ """Get the ROI #1 y origin."""
159
+ return self._db_dict[CryonirspBudName.roi_1_origin_y.value]
160
+
161
+ @property
162
+ def roi_1_size_x(self) -> int:
163
+ """Get the ROI #1 x size."""
164
+ return self._db_dict[CryonirspBudName.roi_1_size_x.value]
165
+
166
+ @property
167
+ def roi_1_size_y(self) -> int:
168
+ """Get the ROI #1 y size."""
169
+ return self._db_dict[CryonirspBudName.roi_1_size_y.value]
@@ -0,0 +1,46 @@
1
+ """Cryo filter list and tooling."""
2
+ import astropy.units as u
3
+ from dkist_processing_common.models.wavelength import WavelengthRange
4
+
5
+
6
+ class Filter(WavelengthRange):
7
+ """
8
+ CRYONirsp filter data structure.
9
+
10
+ Parameters
11
+ ----------
12
+ filter_id
13
+ Unique ID of the filter in use
14
+ """
15
+
16
+ filter_id: str
17
+
18
+
19
+ CRYONIRSP_CI_FILTERS = [
20
+ Filter(filter_id="Continuum", min=1049.000 * u.nm, max=1050.000 * u.nm),
21
+ Filter(filter_id="FeXIII1075", min=1074.200 * u.nm, max=1075.200 * u.nm),
22
+ Filter(filter_id="FeXIII1080", min=1079.300 * u.nm, max=1080.300 * u.nm),
23
+ Filter(filter_id="HeI", min=1082.500 * u.nm, max=1083.500 * u.nm),
24
+ Filter(filter_id="HPaschen-beta", min=1281.300 * u.nm, max=1282.300 * u.nm),
25
+ Filter(filter_id="JBand", min=1170.000 * u.nm, max=1330.000 * u.nm),
26
+ Filter(filter_id="SiX", min=1427.500 * u.nm, max=1432.500 * u.nm),
27
+ ]
28
+
29
+
30
+ def find_associated_ci_filter(filter_id: str) -> Filter:
31
+ """
32
+ Given a wavelength, find the Filter that contains that wavelength between its wavemin/wavemax.
33
+
34
+ Parameters
35
+ ----------
36
+ filter_id
37
+ Unique ID of the filter in use
38
+
39
+ Returns
40
+ -------
41
+ A Filter object that contains the wavelength
42
+ """
43
+ matching_filters = [f for f in CRYONIRSP_CI_FILTERS if f.filter_id == filter_id]
44
+ if len(matching_filters) == 1:
45
+ return matching_filters[0]
46
+ raise ValueError(f"Found {len(matching_filters)} matching filters when 1 was expected.")
@@ -15,9 +15,11 @@ class CryonirspParameters(ParameterBase):
15
15
  self,
16
16
  input_dataset_parameters: dict[str, list[InputDatasetParameterValue]],
17
17
  wavelength: float | None = None,
18
+ arm_id: str | None = None,
18
19
  ):
19
20
  super().__init__(input_dataset_parameters)
20
21
  self._wavelength = wavelength
22
+ self._arm_id = arm_id
21
23
 
22
24
  @property
23
25
  def geo_upsample_factor(self) -> float:
@@ -35,24 +37,26 @@ class CryonirspParameters(ParameterBase):
35
37
  return self._find_most_recent_past_value("cryonirsp_geo_poly_fit_order")
36
38
 
37
39
  @property
38
- def geo_gradient_displacement(self) -> int:
39
- """Number of pixels to shift by when computing gradient."""
40
- return self._find_most_recent_past_value("cryonirsp_geo_gradient_displacement")
40
+ def geo_spatial_gradient_displacement(self) -> int:
41
+ """Number of spatial pixels to shift by when computing gradient."""
42
+ return self._find_most_recent_past_value("cryonirsp_geo_spatial_gradient_displacement")
41
43
 
42
44
  @property
43
- def geo_strip_spatial_length_divisor(self) -> int:
44
- """Divisor applied to the array spatial axis size to compute the strip length."""
45
- return self._find_most_recent_past_value("cryonirsp_geo_strip_spatial_length_divisor")
45
+ def geo_strip_spatial_size_fraction(self) -> float:
46
+ """Fraction of full spatial size to use for spatial size of strips used to find the beam angle."""
47
+ return self._find_most_recent_past_value("cryonirsp_geo_strip_spatial_size_fraction")
46
48
 
47
49
  @property
48
- def geo_strip_spectral_width_divisor(self) -> int:
49
- """Divisor applied to the array width to compute the vertical strip width."""
50
- return self._find_most_recent_past_value("cryonirsp_geo_strip_spectral_width_divisor")
50
+ def geo_strip_spectral_size_fraction(self) -> float:
51
+ """Fraction of full spectral size to use for spectral size of strips used to find the beam angle."""
52
+ return self._find_most_recent_past_value("cryonirsp_geo_strip_spectral_size_fraction")
51
53
 
52
54
  @property
53
- def geo_strip_spectral_offset_divisor(self) -> int:
54
- """Divisor applied to the array width to compute the vertical strip half-offset."""
55
- return self._find_most_recent_past_value("cryonirsp_geo_strip_spectral_offset_divisor")
55
+ def geo_strip_spectral_offset_size_fraction(self) -> float:
56
+ """Fraction of full spectral size to set as the +/- offset from spectral center for the two strips used to find the beam angle."""
57
+ return self._find_most_recent_past_value(
58
+ "cryonirsp_geo_strip_spectral_offset_size_fraction"
59
+ )
56
60
 
57
61
  @property
58
62
  def solar_spectral_avg_window(self):
@@ -122,9 +126,22 @@ class CryonirspParameters(ParameterBase):
122
126
  return self._find_most_recent_past_value("cryonirsp_beam_boundaries_upsample_factor")
123
127
 
124
128
  @property
125
- def bad_pixel_map_median_filter_size(self) -> int:
129
+ def beam_boundaries_sp_beam_transition_region_size_fraction(self) -> float:
130
+ """
131
+ Fraction of full spectral size to use as the size of the transition region between the two SP beams.
132
+
133
+ A region with size = (this parameter * full spectral size) and centered at the center of the spectral dimension
134
+ will be ignored when extracting the beams.
135
+ """
136
+ return self._find_most_recent_past_value(
137
+ "cryonirsp_beam_boundaries_sp_beam_transition_region_size_fraction"
138
+ )
139
+
140
+ @property
141
+ def bad_pixel_map_median_filter_size(self) -> list[int, int]:
126
142
  """Return the smoothing disk size to be used in the bad pixel map computation."""
127
- return self._find_most_recent_past_value("cryonirsp_bad_pixel_map_median_filter_size")
143
+ filter_size = self._find_parameter_for_arm("cryonirsp_bad_pixel_map_median_filter_size")
144
+ return filter_size
128
145
 
129
146
  @property
130
147
  def bad_pixel_map_threshold_factor(self) -> float:
@@ -132,35 +149,23 @@ class CryonirspParameters(ParameterBase):
132
149
  return self._find_most_recent_past_value("cryonirsp_bad_pixel_map_threshold_factor")
133
150
 
134
151
  @property
135
- def bad_pixel_correction_median_filter_size(self) -> int:
152
+ def corrections_bad_pixel_median_filter_size(self) -> int:
136
153
  """Return the size of the median filter to be used for bad pixel correction."""
137
154
  return self._find_most_recent_past_value(
138
- "cryonirsp_bad_pixel_correction_median_filter_size"
155
+ "cryonirsp_corrections_bad_pixel_median_filter_size"
139
156
  )
140
157
 
141
158
  @cached_property
142
- def ci_thresholds(self) -> np.ndarray:
143
- """Name of parameter associated with the CI linearization thresholds."""
144
- param_dict = self._find_most_recent_past_value("cryonirsp_ci_thresholds")
145
- return self._get_param_data_from_npy_file(param_dict)
146
-
147
- @cached_property
148
- def ci_polyfit(self) -> np.ndarray:
149
- """Name of parameter associated with the CI linearization polyfit coefficients."""
150
- param_dict = self._find_most_recent_past_value("cryonirsp_ci_polyfit")
151
- return self._get_param_data_from_npy_file(param_dict)
159
+ def linearization_thresholds(self) -> np.ndarray:
160
+ """Name of parameter associated with the linearization thresholds."""
161
+ param_dict = self._find_parameter_for_arm("cryonirsp_linearization_thresholds")
162
+ return self._load_param_value_from_npy_file(param_dict)
152
163
 
153
164
  @cached_property
154
- def sp_thresholds(self) -> np.ndarray:
155
- """Name of parameter associated with the SP linearization thresholds."""
156
- param_dict = self._find_most_recent_past_value("cryonirsp_sp_thresholds")
157
- return self._get_param_data_from_npy_file(param_dict)
158
-
159
- @cached_property
160
- def sp_polyfit(self) -> np.ndarray:
161
- """Name of parameter associated with the SP linearization polyfit coefficients."""
162
- param_dict = self._find_most_recent_past_value("cryonirsp_sp_polyfit")
163
- return self._get_param_data_from_npy_file(param_dict)
165
+ def linearization_polyfit_coeffs(self) -> np.ndarray:
166
+ """Name of parameter associated with the linearization polyfit coefficients."""
167
+ param_dict = self._find_parameter_for_arm("cryonirsp_linearization_polyfit_coeffs")
168
+ return self._load_param_value_from_npy_file(param_dict)
164
169
 
165
170
  def _find_parameter_closest_wavelength(self, parameter_name: str) -> Any:
166
171
  """
@@ -186,8 +191,13 @@ class CryonirspParameters(ParameterBase):
186
191
  return chosen_value
187
192
 
188
193
  @staticmethod
189
- def _get_param_data_from_npy_file(param_dict: dict) -> np.ndarray:
194
+ def _load_param_value_from_npy_file(param_dict: dict) -> np.ndarray:
190
195
  """Return the data associated with a parameter file saved in numpy format."""
191
196
  file_path = param_dict["param_path"]
192
197
  result = np.load(file_path)
193
198
  return result
199
+
200
+ def _find_parameter_for_arm(self, parameter_name: str) -> Any:
201
+ full_param_name = f"{parameter_name}_{self._arm_id.lower()}"
202
+ param = self._find_most_recent_past_value(full_param_name)
203
+ return param
@@ -32,6 +32,10 @@ class CryonirspRampFitsAccess(L0FitsAccess):
32
32
  self.camera_readout_mode = self.header["CNCAMMD"]
33
33
  self.curr_frame_in_ramp: int = self.header["CNCNDR"]
34
34
  self.arm_id: str = self.header["CNARMID"]
35
+ self.roi_1_origin_x = self.header["HWROI1OX"]
36
+ self.roi_1_origin_y = self.header["HWROI1OY"]
37
+ self.roi_1_size_x = self.header["HWROI1SX"]
38
+ self.roi_1_size_y = self.header["HWROI1SY"]
35
39
 
36
40
 
37
41
  class CryonirspL0FitsAccess(L0FitsAccess):
@@ -52,7 +52,7 @@ class SingleScanStep:
52
52
  return self.date_obs < other.date_obs
53
53
 
54
54
  def __hash__(self) -> int:
55
- # Not strictly necessary, but does allow for using set() on these objects
55
+ """Not strictly necessary, but does allow for using set() on these objects."""
56
56
  return hash(
57
57
  (
58
58
  self.scan_step,
@@ -104,7 +104,7 @@ class MapScanFlower(MapScanStemBase):
104
104
  """Compute the map scan number for a single frame.
105
105
 
106
106
  The frame implies a SingleScanStep. That object is then compared to the sorted list of objects for a single
107
- (raster_step, modstate) tuple. The location within that sorted list is the map scan number.
107
+ (raster_step, meas_num, modstate, sub_repeat) tuple. The location within that sorted list is the map scan number.
108
108
  """
109
109
  scan_step_obj = self.key_to_petal_dict[key]
110
110
  step_list = sorted(
@@ -136,7 +136,6 @@ class NumMapScansBud(MapScanStemBase):
136
136
  it will not be included.
137
137
  Assumes the incomplete map_scan is always the last one due to summit abort or cancellation.
138
138
  """
139
- # grab header value (fits access attribute from parse file: num subrepeats) of subrepeats and loop through here
140
139
  map_scans_per_scan_step = []
141
140
  for meas_dict in self.scan_step_dict.values():
142
141
  for mod_dict in meas_dict.values():
@@ -10,14 +10,10 @@ from dkist_processing_cryonirsp.parsers.cryonirsp_l0_fits_access import Cryonirs
10
10
 
11
11
  def parse_header_ip_task(fits_obj: CryonirspL0FitsAccess) -> str:
12
12
  """
13
- Parse CryoNIRSP tasks.
13
+ Parse CryoNIRSP tasks that aren't fully captured by the single IPTASK keyword.
14
14
 
15
- Parameters
16
- ----------
17
- fits_obj:
18
- A single FitsAccess object
15
+ Right now this just distinguishes between solar and lamp gain images.
19
16
  """
20
- # Distinguish between lamp and solar gains
21
17
  if (
22
18
  fits_obj.ip_task_type.upper() == TaskName.gain.value
23
19
  and fits_obj.gos_level3_status == "lamp"
@@ -30,7 +26,6 @@ def parse_header_ip_task(fits_obj: CryonirspL0FitsAccess) -> str:
30
26
  ):
31
27
  return TaskName.solar_gain.value
32
28
 
33
- # Everything else is unchanged
34
29
  return fits_obj.ip_task_type
35
30
 
36
31
 
@@ -32,7 +32,8 @@ class CryonirspTaskExposureTimesBud(TaskExposureTimesBud):
32
32
  fits_obj:
33
33
  A single FitsAccess object
34
34
  """
35
- ip_task_type = parse_header_ip_task(fits_obj) # This is where it's different
35
+ # This is where it's different than the common `TaskExposureTimesBud`
36
+ ip_task_type = parse_header_ip_task(fits_obj)
36
37
  if ip_task_type.lower() == self.ip_task_type.lower():
37
38
  raw_exp_time = getattr(fits_obj, self.metadata_key)
38
39
  return round(raw_exp_time, 6)
@@ -64,9 +65,9 @@ class CryonirspTimeObsBud(Stem):
64
65
  """
65
66
  return fits_obj.time_obs
66
67
 
67
- def getter(self, key: Hashable) -> Hashable:
68
+ def getter(self, key: Hashable) -> tuple:
68
69
  """
69
- Get the list of time_obs values.
70
+ Get the sorted tuple of time_obs values.
70
71
 
71
72
  Parameters
72
73
  ----------
@@ -114,7 +114,7 @@ class CIAssembleCryonirspMovie(AssembleCryonirspMovieBase):
114
114
  """
115
115
  map_scan_num = n // self.constants.num_scan_steps + 1
116
116
  scan_step = n % self.constants.num_scan_steps + 1
117
- # Note: Use only the first measurement for the movie
117
+
118
118
  tags = [
119
119
  CryonirspTag.map_scan(map_scan_num),
120
120
  CryonirspTag.scan_step(scan_step),
@@ -2,10 +2,10 @@
2
2
  import numpy as np
3
3
  import scipy.ndimage as spnd
4
4
  from dkist_processing_math.statistics import average_numpy_arrays
5
+ from logging42 import logger
5
6
 
6
7
  from dkist_processing_cryonirsp.models.tags import CryonirspTag
7
8
  from dkist_processing_cryonirsp.models.task_name import CryonirspTaskName
8
- from dkist_processing_cryonirsp.models.task_name import TaskName
9
9
  from dkist_processing_cryonirsp.tasks.cryonirsp_base import CryonirspTaskBase
10
10
 
11
11
 
@@ -40,31 +40,32 @@ class BadPixelMapCalibration(CryonirspTaskBase):
40
40
  None
41
41
 
42
42
  """
43
- # Step 1:
44
43
  with self.apm_task_step(f"Compute average uncorrected solar gain image"):
44
+ logger.info("Averaging uncorrected solar gain images")
45
45
  average_solar_gain_array = self._compute_average_gain_array()
46
+
46
47
  with self.apm_task_step(f"Compute the bad pixel map"):
47
- # Step 2:
48
- filter_size = self.parameters.bad_pixel_map_median_filter_size
49
- # TODO: Distinguish between SP and CI here? May need two parameters
50
- # TODO: as a 2D kernel does not need to be as large as the 1D kernel length.
51
- # Filter only along the slit so as not to broaden the spectral lines.
52
- # Broadening the spectral lines causes them to leak into the difference
53
- # array calculated below and possibly get classified as bad pixels
54
- # Use constant mode and the median for cval, as this gives better
55
- # filtering ability and behavior at the edge pixels
56
- filtered_array = spnd.median_filter(
57
- average_solar_gain_array,
58
- size=(filter_size, 1),
59
- mode="constant",
60
- cval=np.median(average_solar_gain_array),
61
- )
62
- # Step 3:
63
- diff = filtered_array - average_solar_gain_array
64
- # Step 4:
65
- thresh = self.parameters.bad_pixel_map_threshold_factor
66
- bad_pixel_map = np.array((np.abs(diff) > thresh * diff.std()), dtype=int)
67
- # Step 5:
48
+ with self.apm_processing_step("Smooth array with median filter"):
49
+ logger.info("Smoothing array with median filter")
50
+ filter_size = self.parameters.bad_pixel_map_median_filter_size
51
+ filtered_array = spnd.median_filter(
52
+ average_solar_gain_array,
53
+ size=filter_size,
54
+ mode="constant",
55
+ cval=np.nanmedian(average_solar_gain_array),
56
+ )
57
+
58
+ with self.apm_processing_step("Identify bad pixels"):
59
+ logger.info("Identifying bad pixels")
60
+ thresh = self.parameters.bad_pixel_map_threshold_factor
61
+
62
+ diff = filtered_array - average_solar_gain_array
63
+ bad_pixel_map = np.array((np.abs(diff) > thresh * diff.std()), dtype=int)
64
+
65
+ # Find and fix any residual zeros that slipped through the bad pixel map.
66
+ zeros = np.where(average_solar_gain_array == 0.0)
67
+ bad_pixel_map[zeros] = 1
68
+
68
69
  with self.apm_writing_step("Writing bad pixel map"):
69
70
  self.intermediate_frame_write_arrays(
70
71
  bad_pixel_map, task=CryonirspTaskName.bad_pixel_map.value
@@ -83,7 +84,6 @@ class BadPixelMapCalibration(CryonirspTaskBase):
83
84
  -------
84
85
  The average gain array
85
86
  """
86
- # Get the linearized solar gain arrays
87
87
  lin_corr_gain_arrays = self.linearized_frame_full_array_generator(
88
88
  tags=[
89
89
  CryonirspTag.task_solar_gain(),
@@ -0,0 +1,199 @@
1
+ """CryoNIRSP compute beam boundary task."""
2
+ from abc import abstractmethod
3
+
4
+ import numpy as np
5
+ from dkist_processing_math.statistics import average_numpy_arrays
6
+ from logging42 import logger
7
+ from skimage import filters
8
+ from skimage import img_as_ubyte
9
+ from skimage.exposure import rescale_intensity
10
+ from skimage.morphology import disk
11
+
12
+ from dkist_processing_cryonirsp.models.tags import CryonirspTag
13
+ from dkist_processing_cryonirsp.tasks.cryonirsp_base import CryonirspTaskBase
14
+
15
+
16
+ class BeamBoundariesCalibrationBase(CryonirspTaskBase):
17
+ """
18
+ Task class for calculation of the beam boundaries for later use during calibration.
19
+
20
+ Parameters
21
+ ----------
22
+ recipe_run_id : int
23
+ id of the recipe run used to identify the workflow run this task is part of
24
+ workflow_name : str
25
+ name of the workflow to which this instance of the task belongs
26
+ workflow_version : str
27
+ version of the workflow to which this instance of the task belongs
28
+
29
+ """
30
+
31
+ def run(self):
32
+ """
33
+ Compute the beam boundaries by analyzing a set of solar gain images.
34
+
35
+ Steps:
36
+ 1. Compute the average gain image
37
+ 2. Correct any bad pixels
38
+ 3. Smooth the image using a median filter with radius 3
39
+ 4. Use a bimodal threshold filter to segment the image into illuminated and non-illuminated regions
40
+ 5. Compute the boundaries of the illuminated region
41
+ 6. Extract the illuminated portion of the image
42
+ 7. Split the beams using a 10% buffer region around the horizontal mid-point
43
+ 8. Find the horizontal shift between the two images
44
+ 9. Identify the boundaries of the overlap
45
+ 10. Save the boundaries as a fits file (json?)
46
+
47
+
48
+ Returns
49
+ -------
50
+ None
51
+
52
+ """
53
+ # Step 1:
54
+ with self.apm_processing_step(f"Compute average solar gain image"):
55
+ logger.info("Averaging solar gain image")
56
+ average_solar_gain_array = self._compute_average_gain_array()
57
+
58
+ # Step 2:
59
+ with self.apm_task_step(f"Retrieve bad pixel map"):
60
+ logger.info("Retrieving bad pixel map")
61
+ bad_pixel_map = self.intermediate_frame_load_full_bad_pixel_map()
62
+ corrected_solar_gain_array = self.corrections_correct_bad_pixels(
63
+ average_solar_gain_array, bad_pixel_map
64
+ )
65
+
66
+ # Step 3
67
+ with self.apm_processing_step(f"Smooth the array to get good segmentation"):
68
+ logger.info("Smoothing corrected solar gain array")
69
+ smoothed_solar_gain_array = self._smooth_gain_array(corrected_solar_gain_array)
70
+
71
+ # Step 4:
72
+ with self.apm_processing_step(
73
+ f"Segment the array into illuminated and non-illuminated pixels"
74
+ ):
75
+ logger.info(
76
+ "Segmenting smoothed solar gain array into illuminated and non-illuminated pixels "
77
+ )
78
+ segmented_array = self._segment_array(smoothed_solar_gain_array)
79
+
80
+ # Step 5:
81
+ with self.apm_processing_step(f"Define the boundaries of the illuminated region"):
82
+ logger.info("Defining boundaries of illuminated region")
83
+ illuminated_boundaries = self._compute_illuminated_boundaries(segmented_array)
84
+
85
+ # Steps 6 - 9:
86
+ with self.apm_processing_step(f"Compute the beam boundaries of the illuminated region"):
87
+ logger.info("Computing beam boundaries of illuminated region")
88
+ boundaries = self._compute_beam_boundaries(
89
+ smoothed_solar_gain_array, illuminated_boundaries
90
+ )
91
+
92
+ # Step 10:
93
+ with self.apm_writing_step("Writing beam boundaries"):
94
+ logger.info("Writing beam boundaries")
95
+ for beam, array in enumerate(boundaries, start=1):
96
+ self.intermediate_frame_write_arrays(
97
+ array, task_tag=CryonirspTag.task_beam_boundaries(), beam=beam
98
+ )
99
+
100
+ def _compute_average_gain_array(self) -> np.ndarray:
101
+ """
102
+ Compute an average of uncorrected solar gain arrays.
103
+
104
+ We are computing the overall illumination pattern for both beams simultaneously,
105
+ so no dark correction is required and no beam splitting is used at this point.
106
+ Solar gain images are used because they have larger flux than the lamp gain images
107
+ and the lamp gain images do not have the same illumination pattern as the solar
108
+ gain images.
109
+
110
+ Returns
111
+ -------
112
+ The average gain array
113
+ """
114
+ lin_corr_gain_arrays = self.linearized_frame_full_array_generator(
115
+ tags=[
116
+ CryonirspTag.task_solar_gain(),
117
+ CryonirspTag.linearized(),
118
+ CryonirspTag.frame(),
119
+ ]
120
+ )
121
+ averaged_gain_data = average_numpy_arrays(lin_corr_gain_arrays)
122
+ return averaged_gain_data
123
+
124
+ def _smooth_gain_array(self, array: np.ndarray) -> np.ndarray:
125
+ """
126
+ Smooth the input array with morphological filtering using a disk shape.
127
+
128
+ The array is smoothed to help eliminate artifacts in the image segmentation step.
129
+
130
+ Parameters
131
+ ----------
132
+ array
133
+ The input array to be smoothed
134
+
135
+ Returns
136
+ -------
137
+ The smoothed output array
138
+ """
139
+ # skimage.filters requires ubyte arrays and float->ubyte conversion only works with float in range [-1, 1]
140
+ norm_gain = img_as_ubyte(rescale_intensity(array, out_range=(0, 1.0)))
141
+
142
+ disk_size = self.parameters.beam_boundaries_smoothing_disk_size
143
+ norm_gain = filters.rank.median(norm_gain, disk(disk_size))
144
+ return norm_gain
145
+
146
+ @staticmethod
147
+ def _segment_array(array: np.ndarray) -> np.ndarray:
148
+ """
149
+ Segment the array into illuminated (True) and non-illuminated (False) regions.
150
+
151
+ Parameters
152
+ ----------
153
+ array
154
+ The array to be segmented
155
+
156
+ Returns
157
+ -------
158
+ The boolean segmented output array
159
+ """
160
+ thresh = filters.threshold_minimum(array)
161
+ logger.info(f"Segmentation threshold = {thresh}")
162
+ segmented_array = (array > thresh).astype(bool)
163
+ return segmented_array
164
+
165
+ @abstractmethod
166
+ def _compute_illuminated_boundaries(self, array: np.ndarray) -> list[int]:
167
+ """
168
+ Compute the inscribed rectangular extent of the illuminated portion of the array.
169
+
170
+ Parameters
171
+ ----------
172
+ array
173
+ The segmented boolean array over which the illuminated boundaries are to be computed
174
+
175
+ Returns
176
+ -------
177
+ The illuminated region boundaries, [y_min, y_max, x_min, x_max]
178
+ """
179
+ pass
180
+
181
+ @abstractmethod
182
+ def _compute_beam_boundaries(
183
+ self, smoothed_solar_gain_array: np.ndarray, illuminated_boundaries: list[int]
184
+ ):
185
+ """
186
+ Compute the beam boundaries from the illuminated beam.
187
+
188
+ Parameters
189
+ ----------
190
+ smoothed_solar_gain_array
191
+ The smoothed solar gain array, ready for beam identification
192
+ illuminated_boundaries
193
+ The boundaries of the illuminated beam [y_min, y_max, x_min, x_max]
194
+
195
+ Returns
196
+ -------
197
+ A list of beam boundary arrays, one for each beam
198
+ """
199
+ pass