dkist-processing-common 10.2.2__tar.gz → 10.3.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 (123) hide show
  1. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/CHANGELOG.rst +18 -0
  2. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/PKG-INFO +2 -2
  3. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tasks/mixin/quality/_metrics.py +88 -19
  4. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tasks/teardown.py +3 -0
  5. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/conftest.py +72 -12
  6. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/test_assemble_quality.py +3 -0
  7. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/test_quality_mixin.py +79 -15
  8. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/test_teardown.py +7 -7
  9. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/test_workflow_task_base.py +7 -7
  10. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common.egg-info/PKG-INFO +2 -2
  11. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common.egg-info/requires.txt +1 -1
  12. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/pyproject.toml +1 -1
  13. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/.gitignore +0 -0
  14. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/.pre-commit-config.yaml +0 -0
  15. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/.readthedocs.yml +0 -0
  16. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/README.rst +0 -0
  17. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/bitbucket-pipelines.yml +0 -0
  18. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/changelog/.gitempty +0 -0
  19. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/check_changelog_updated.sh +0 -0
  20. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/__init__.py +0 -0
  21. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/_util/__init__.py +0 -0
  22. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/_util/constants.py +0 -0
  23. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/_util/graphql.py +0 -0
  24. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/_util/scratch.py +0 -0
  25. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/_util/tags.py +0 -0
  26. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/codecs/__init__.py +0 -0
  27. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/codecs/asdf.py +0 -0
  28. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/codecs/bytes.py +0 -0
  29. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/codecs/fits.py +0 -0
  30. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/codecs/iobase.py +0 -0
  31. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/codecs/json.py +0 -0
  32. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/codecs/path.py +0 -0
  33. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/codecs/quality.py +0 -0
  34. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/codecs/str.py +0 -0
  35. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/config.py +0 -0
  36. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/fonts/Lato-Regular.ttf +0 -0
  37. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/fonts/__init__.py +0 -0
  38. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/manual.py +0 -0
  39. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/models/__init__.py +0 -0
  40. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/models/constants.py +0 -0
  41. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/models/fits_access.py +0 -0
  42. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/models/flower_pot.py +0 -0
  43. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/models/graphql.py +0 -0
  44. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/models/message.py +0 -0
  45. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/models/message_queue_binding.py +0 -0
  46. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/models/metric_code.py +0 -0
  47. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/models/parameters.py +0 -0
  48. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/models/quality.py +0 -0
  49. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/models/tags.py +0 -0
  50. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/models/task_name.py +0 -0
  51. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/models/wavelength.py +0 -0
  52. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/parsers/__init__.py +0 -0
  53. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/parsers/cs_step.py +0 -0
  54. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/parsers/dsps_repeat.py +0 -0
  55. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/parsers/experiment_id_bud.py +0 -0
  56. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/parsers/id_bud.py +0 -0
  57. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/parsers/l0_fits_access.py +0 -0
  58. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/parsers/l1_fits_access.py +0 -0
  59. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/parsers/near_bud.py +0 -0
  60. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/parsers/proposal_id_bud.py +0 -0
  61. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/parsers/quality.py +0 -0
  62. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/parsers/single_value_single_key_flower.py +0 -0
  63. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/parsers/task.py +0 -0
  64. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/parsers/time.py +0 -0
  65. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/parsers/unique_bud.py +0 -0
  66. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/parsers/wavelength.py +0 -0
  67. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tasks/__init__.py +0 -0
  68. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tasks/assemble_movie.py +0 -0
  69. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tasks/base.py +0 -0
  70. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tasks/l1_output_data.py +0 -0
  71. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tasks/mixin/__init__.py +0 -0
  72. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tasks/mixin/globus.py +0 -0
  73. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tasks/mixin/input_dataset.py +0 -0
  74. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tasks/mixin/interservice_bus.py +0 -0
  75. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tasks/mixin/metadata_store.py +0 -0
  76. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tasks/mixin/object_store.py +0 -0
  77. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tasks/mixin/quality/__init__.py +0 -0
  78. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tasks/mixin/quality/_base.py +0 -0
  79. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tasks/output_data_base.py +0 -0
  80. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tasks/parse_l0_input_data.py +0 -0
  81. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tasks/quality_metrics.py +0 -0
  82. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tasks/transfer_input_data.py +0 -0
  83. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tasks/trial_catalog.py +0 -0
  84. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tasks/trial_output_data.py +0 -0
  85. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tasks/write_l1.py +0 -0
  86. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/__init__.py +0 -0
  87. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/test_assemble_movie.py +0 -0
  88. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/test_base.py +0 -0
  89. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/test_codecs.py +0 -0
  90. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/test_constants.py +0 -0
  91. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/test_cs_step.py +0 -0
  92. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/test_fits_access.py +0 -0
  93. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/test_flower_pot.py +0 -0
  94. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/test_input_dataset.py +0 -0
  95. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/test_interservice_bus.py +0 -0
  96. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/test_interservice_bus_mixin.py +0 -0
  97. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/test_output_data_base.py +0 -0
  98. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/test_parameters.py +0 -0
  99. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/test_parse_l0_input_data.py +0 -0
  100. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/test_publish_catalog_messages.py +0 -0
  101. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/test_quality.py +0 -0
  102. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/test_scratch.py +0 -0
  103. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/test_stems.py +0 -0
  104. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/test_submit_dataset_metadata.py +0 -0
  105. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/test_tags.py +0 -0
  106. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/test_task_name.py +0 -0
  107. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/test_task_parsing.py +0 -0
  108. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/test_transfer_input_data.py +0 -0
  109. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/test_transfer_l1_output_data.py +0 -0
  110. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/test_trial_catalog.py +0 -0
  111. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/test_trial_output_data.py +0 -0
  112. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common/tests/test_write_l1.py +0 -0
  113. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common.egg-info/SOURCES.txt +0 -0
  114. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common.egg-info/dependency_links.txt +0 -0
  115. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/dkist_processing_common.egg-info/top_level.txt +0 -0
  116. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/docs/Makefile +0 -0
  117. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/docs/changelog.rst +0 -0
  118. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/docs/conf.py +0 -0
  119. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/docs/index.rst +0 -0
  120. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/docs/make.bat +0 -0
  121. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/docs/requirements.txt +0 -0
  122. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/licenses/LICENSE.rst +0 -0
  123. {dkist_processing_common-10.2.2 → dkist_processing_common-10.3.0}/setup.cfg +0 -0
@@ -1,3 +1,21 @@
1
+ v10.3.0 (2024-10-15)
2
+ ====================
3
+
4
+ Features
5
+ --------
6
+
7
+ - Update the machinery in `quality_store_polcal_results` to handle NaN values.
8
+ This is required for the new error-handling paradigm in `dkist-processing-pac` v3.1.0. (`#214 <https://bitbucket.org/dkistdc/dkist-processing-common/pull-requests/214>`__)
9
+ - Add new argument, `num_points_to_sample`, to `quality_store_polcal_results`, which allows a user to reduce the number of points saved for inclusion in the quality report.
10
+ This allows us to mitigate large quality metrics. (`#215 <https://bitbucket.org/dkistdc/dkist-processing-common/pull-requests/215>`__)
11
+
12
+
13
+ Bugfixes
14
+ --------
15
+
16
+ - Correctly clean up tags used for file name uniqueness. (`#217 <https://bitbucket.org/dkistdc/dkist-processing-common/pull-requests/217>`__)
17
+
18
+
1
19
  v10.2.2 (2024-10-14)
2
20
  ====================
3
21
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dkist-processing-common
3
- Version: 10.2.2
3
+ Version: 10.3.0
4
4
  Summary: Common task classes used by the DKIST science data processing pipelines
5
5
  Author-email: NSO / AURA <dkistdc@nso.edu>
6
6
  License: BSD-3-Clause
@@ -17,7 +17,7 @@ Requires-Dist: astropy>=5.1.1
17
17
  Requires-Dist: dkist-fits-specifications<5.0,>=4.0.0
18
18
  Requires-Dist: dkist-header-validator<6.0,>=5.0.0
19
19
  Requires-Dist: dkist-processing-core==4.2.1
20
- Requires-Dist: dkist-processing-pac<4.0,>=3.0
20
+ Requires-Dist: dkist-processing-pac<4.0,>=3.1
21
21
  Requires-Dist: dkist-service-configuration<3.0,>=2.0.2
22
22
  Requires-Dist: dkist-spectral-lines<4.0,>=3.0.0
23
23
  Requires-Dist: globus-sdk>=3.12.0
@@ -651,8 +651,11 @@ class _PolcalQualityMixin:
651
651
  bin_nums: list[int],
652
652
  bin_labels: list[str],
653
653
  skip_recording_constant_pars: bool = False,
654
+ num_points_to_sample: int | None = None,
654
655
  ):
655
656
  """Compute and store all PolCal related metrics."""
657
+ thinning_stride = self._compute_thinning_stride(polcal_fitter, num_points_to_sample)
658
+
656
659
  if not skip_recording_constant_pars:
657
660
  logger.info("Storing constant parameter values")
658
661
  self._store_polcal_constant_parameter_values(polcal_fitter=polcal_fitter, label=label)
@@ -662,7 +665,11 @@ class _PolcalQualityMixin:
662
665
 
663
666
  logger.info("Storing local parameter values")
664
667
  self._store_polcal_local_parameter_values(
665
- polcal_fitter=polcal_fitter, label=label, bin_nums=bin_nums, bin_labels=bin_labels
668
+ polcal_fitter=polcal_fitter,
669
+ label=label,
670
+ bin_nums=bin_nums,
671
+ bin_labels=bin_labels,
672
+ thinning_stride=thinning_stride,
666
673
  )
667
674
 
668
675
  logger.info("Storing fit residuals")
@@ -671,6 +678,7 @@ class _PolcalQualityMixin:
671
678
  label=label,
672
679
  bin_nums=bin_nums,
673
680
  bin_labels=bin_labels,
681
+ thinning_stride=thinning_stride,
674
682
  )
675
683
 
676
684
  logger.info("Storing modulation matrix efficiencies")
@@ -679,6 +687,7 @@ class _PolcalQualityMixin:
679
687
  label=label,
680
688
  bin_nums=bin_nums,
681
689
  bin_labels=bin_labels,
690
+ thinning_stride=thinning_stride,
682
691
  )
683
692
 
684
693
  @staticmethod
@@ -688,6 +697,32 @@ class _PolcalQualityMixin:
688
697
  return None
689
698
  return str(label).replace(" ", "_").upper()
690
699
 
700
+ @staticmethod
701
+ def _compute_thinning_stride(
702
+ polcal_fitter: PolcalFitter, num_points_to_sample: int
703
+ ) -> int | None:
704
+ """
705
+ Compute the stride needed to collect the requested number of data samples.
706
+
707
+ E.g., if there are 20,000 samples in the full `polcal_fitter` and the user wants to only save 200 then the
708
+ stride will be 100.
709
+
710
+ `None` is returned if no subsampling has been requested or if the number of requested samples is larger than
711
+ then number of samples in the full `polcal_fitter`.
712
+ """
713
+ if num_points_to_sample is None:
714
+ return None
715
+
716
+ num_total_points = np.prod(polcal_fitter.local_objects.dresser.shape)
717
+ if num_points_to_sample > num_total_points:
718
+ return None
719
+
720
+ remainder = num_total_points % num_points_to_sample
721
+ if remainder:
722
+ return num_total_points // (num_points_to_sample - 1)
723
+
724
+ return num_total_points // num_points_to_sample
725
+
691
726
  def _store_polcal_constant_parameter_values(
692
727
  self, *, polcal_fitter: PolcalFitter, label: str
693
728
  ) -> None:
@@ -896,6 +931,7 @@ class _PolcalQualityMixin:
896
931
  label: str,
897
932
  bin_nums: list[int],
898
933
  bin_labels: list[str],
934
+ thinning_stride: int | None,
899
935
  ) -> None:
900
936
  """Store local polcal parameter fits.
901
937
 
@@ -909,10 +945,16 @@ class _PolcalQualityMixin:
909
945
  flattened_demod = np.reshape(
910
946
  polcal_fitter.demodulation_matrices, (np.prod(fov_shape), 4, num_mod)
911
947
  )
912
- flattened_mod = np.zeros((np.prod(fov_shape), num_mod, 4))
913
- for i in range(flattened_demod.shape[0]):
914
- flattened_mod[i] = np.linalg.pinv(flattened_demod[i])
948
+ flattened_mod_list = []
949
+ # Need to apply stride this was (instead of argument to `range` because `range` doesn't accept `None` for
950
+ # step argument.
951
+ for i in range(flattened_demod.shape[0])[::thinning_stride]:
952
+ try:
953
+ flattened_mod_list.append(np.linalg.pinv(flattened_demod[i]))
954
+ except:
955
+ pass
915
956
 
957
+ flattened_mod = np.stack(flattened_mod_list)
916
958
  # Move axis so numpoints is the last dimension, which will be easier to understand when
917
959
  # plotting
918
960
  flattened_mod = np.moveaxis(flattened_mod, 0, -1)
@@ -937,8 +979,10 @@ class _PolcalQualityMixin:
937
979
  num_varied_I_sys += 1
938
980
 
939
981
  fit_value_list = []
940
- for point_param in fit_params._all_parameters:
941
- fit_value_list.append(point_param[param].value)
982
+ for point_param in fit_params._all_parameters[::thinning_stride]:
983
+ value = point_param[param].value
984
+ if not np.isnan(value):
985
+ fit_value_list.append(value)
942
986
 
943
987
  init_value = init_param.first_parameters[param].value
944
988
 
@@ -950,6 +994,7 @@ class _PolcalQualityMixin:
950
994
  f"{num_bins} {bin_label}" for num_bins, bin_label in zip(bin_nums, bin_labels)
951
995
  ],
952
996
  "total_bins": int(np.prod(bin_nums)),
997
+ "sampled_bins": flattened_mod.shape[-1],
953
998
  "num_varied_I_sys": num_varied_I_sys,
954
999
  "modmat_list": mod_list,
955
1000
  "free_param_dict": free_param_data,
@@ -1005,7 +1050,9 @@ class _PolcalQualityMixin:
1005
1050
  "Subsequent plots show the distribution of all other free parameters in the fit, along with their initial "
1006
1051
  "values. For I_sys there is a separate fit value for each CS step."
1007
1052
  description += self._compute_bin_description(
1008
- total_bins=data["total_bins"], bin_strs=data["bin_strs"]
1053
+ total_bins=data["total_bins"],
1054
+ bin_strs=data["bin_strs"],
1055
+ sampled_bins=data["sampled_bins"],
1009
1056
  )
1010
1057
 
1011
1058
  metric = ReportMetric(
@@ -1019,7 +1066,13 @@ class _PolcalQualityMixin:
1019
1066
  return metric.model_dump()
1020
1067
 
1021
1068
  def _store_polcal_fit_resdiuals(
1022
- self, *, polcal_fitter: PolcalFitter, label: str, bin_nums: list[int], bin_labels: list[str]
1069
+ self,
1070
+ *,
1071
+ polcal_fitter: PolcalFitter,
1072
+ label: str,
1073
+ bin_nums: list[int],
1074
+ bin_labels: list[str],
1075
+ thinning_stride: int | None,
1023
1076
  ):
1024
1077
  """Store flux residuals and chisq values for a local fit."""
1025
1078
  fit_container = polcal_fitter.local_objects
@@ -1029,7 +1082,7 @@ class _PolcalQualityMixin:
1029
1082
  num_points = np.prod(fov_shape)
1030
1083
  residual_array = np.zeros((num_mod, num_steps, num_points))
1031
1084
  red_chi_list = []
1032
- for i in range(num_points):
1085
+ for i in range(num_points)[::thinning_stride]:
1033
1086
  ## Fit residuals
1034
1087
  point_TM = copy.deepcopy(fit_container.telescope)
1035
1088
  point_CM = copy.deepcopy(fit_container.calibration_unit)
@@ -1054,7 +1107,8 @@ class _PolcalQualityMixin:
1054
1107
  chisq = np.sum(flat_residual**2)
1055
1108
  num_free = sum([fit_params[p].vary for p in fit_params])
1056
1109
  red_chisq = chisq / num_free
1057
- red_chi_list.append(red_chisq)
1110
+ if not np.isnan(red_chisq):
1111
+ red_chi_list.append(red_chisq)
1058
1112
 
1059
1113
  # Convert residuals to panda DataFrame, which will greatly simplify plotting
1060
1114
  col_list = sum(
@@ -1076,6 +1130,7 @@ class _PolcalQualityMixin:
1076
1130
  f"{num_bins} {bin_label}" for num_bins, bin_label in zip(bin_nums, bin_labels)
1077
1131
  ],
1078
1132
  "total_bins": int(np.prod(bin_nums)),
1133
+ "sampled_bins": len(red_chi_list),
1079
1134
  "residual_json": dataframe_str,
1080
1135
  "red_chi_list": red_chi_list,
1081
1136
  }
@@ -1113,10 +1168,11 @@ class _PolcalQualityMixin:
1113
1168
  dataframe_json=data["residual_json"],
1114
1169
  )
1115
1170
 
1116
- description = "The top plot shows relative flux residual distributions for all polcal Calibration Sequence "
1117
- "steps. The bottom plot shows the reduced chi-squared distribution of all fits."
1171
+ description = "The top plot shows relative flux residual distributions for all polcal Calibration Sequence steps. The bottom plot shows the reduced chi-squared distribution of all fits."
1118
1172
  description += self._compute_bin_description(
1119
- total_bins=data["total_bins"], bin_strs=data["bin_strs"]
1173
+ total_bins=data["total_bins"],
1174
+ bin_strs=data["bin_strs"],
1175
+ sampled_bins=data["sampled_bins"],
1120
1176
  )
1121
1177
 
1122
1178
  metric = ReportMetric(
@@ -1136,15 +1192,19 @@ class _PolcalQualityMixin:
1136
1192
  label: str,
1137
1193
  bin_nums: list[int],
1138
1194
  bin_labels: list[str],
1195
+ thinning_stride: int | None,
1139
1196
  ):
1140
1197
  """Compute modulation efficiency for all fit bins and store in a file."""
1141
1198
  fov_shape = polcal_fitter.local_objects.dresser.shape
1142
1199
  num_mod = polcal_fitter.local_objects.dresser.nummod
1143
1200
  num_points = np.prod(fov_shape)
1144
1201
  flat_demod = np.reshape(polcal_fitter.demodulation_matrices, (num_points, 4, num_mod))
1145
- flat_efficiency = 1.0 / np.sqrt(
1146
- num_mod * np.sum(flat_demod**2, axis=2)
1147
- ) # (num_points, 4)
1202
+ thinned_demod = flat_demod[::thinning_stride, :, :]
1203
+ # This will have shape (num_points, 4)
1204
+ flat_efficiency = 1.0 / np.sqrt(num_mod * np.sum(thinned_demod**2, axis=2))
1205
+
1206
+ nan_idx = np.sum(np.isnan(flat_efficiency), axis=1).astype(bool)
1207
+ flat_efficiency = flat_efficiency[~nan_idx, :]
1148
1208
 
1149
1209
  # Because ndarrays are not JSON-able
1150
1210
  # Also, transpose it so the Stokes parameters are the first dimension
@@ -1165,6 +1225,7 @@ class _PolcalQualityMixin:
1165
1225
  f"{num_bins} {bin_label}" for num_bins, bin_label in zip(bin_nums, bin_labels)
1166
1226
  ],
1167
1227
  "total_bins": int(np.prod(bin_nums)),
1228
+ "sampled_bins": flat_efficiency.shape[0],
1168
1229
  "efficiency_list": efficiency_list,
1169
1230
  "warnings": warnings,
1170
1231
  }
@@ -1183,7 +1244,9 @@ class _PolcalQualityMixin:
1183
1244
 
1184
1245
  description = "The modulation efficiencies for all fit modulation matrices."
1185
1246
  description += self._compute_bin_description(
1186
- total_bins=data["total_bins"], bin_strs=data["bin_strs"]
1247
+ total_bins=data["total_bins"],
1248
+ bin_strs=data["bin_strs"],
1249
+ sampled_bins=data["sampled_bins"],
1187
1250
  )
1188
1251
 
1189
1252
  metric = ReportMetric(
@@ -1197,7 +1260,9 @@ class _PolcalQualityMixin:
1197
1260
  return metric.model_dump()
1198
1261
 
1199
1262
  @staticmethod
1200
- def _compute_bin_description(total_bins: int, bin_strs: list[str]) -> str:
1263
+ def _compute_bin_description(
1264
+ total_bins: int, bin_strs: list[str], sampled_bins: int | None = None
1265
+ ) -> str:
1201
1266
  """
1202
1267
  Construct a grammatically correct string that describes the layout of bins present in polcal data.
1203
1268
 
@@ -1210,7 +1275,11 @@ class _PolcalQualityMixin:
1210
1275
  3. For greater than two bin-types we get a list with commas and a final "and":
1211
1276
  "...spanning N TYPE1, M TYPE2, ..., and K TYPEK bins."
1212
1277
  """
1213
- base_str = f" Data show {total_bins} total samples spanning "
1278
+ base_str = f" Data show"
1279
+ if sampled_bins is not None:
1280
+ base_str += f" {sampled_bins} uniformly sampled points from"
1281
+
1282
+ base_str += f" {total_bins} total points spanning "
1214
1283
 
1215
1284
  if len(bin_strs) == 1:
1216
1285
  base_str += bin_strs[0]
@@ -46,6 +46,9 @@ class TeardownBase(WorkflowTaskBase, ABC):
46
46
  with self.apm_task_step("Remove Data and Tags"):
47
47
  self.scratch.purge()
48
48
 
49
+ with self.apm_task_step("Remove File Counters"):
50
+ self.filename_counter.purge()
51
+
49
52
  with self.apm_task_step("Remove Constants"):
50
53
  self.constants._purge()
51
54
 
@@ -3,9 +3,11 @@ Global test fixtures
3
3
  """
4
4
  import json
5
5
  from collections import defaultdict
6
+ from copy import deepcopy
6
7
  from datetime import datetime
7
8
  from datetime import timedelta
8
9
  from pathlib import Path
10
+ from random import choice
9
11
  from random import randint
10
12
  from random import random
11
13
  from typing import Any
@@ -510,7 +512,12 @@ class InstAccess(L0FitsAccess):
510
512
 
511
513
 
512
514
  @pytest.fixture(scope="session")
513
- def cs_with_correct_geometry():
515
+ def cs_data_shape():
516
+ return (10, 4, 3)
517
+
518
+
519
+ @pytest.fixture(scope="session")
520
+ def cs_with_correct_geometry(cs_data_shape):
514
521
  dark_status = [
515
522
  "DarkShutter",
516
523
  "FieldStop (5arcmin)",
@@ -561,7 +568,6 @@ def cs_with_correct_geometry():
561
568
  "clear",
562
569
  "clear",
563
570
  ]
564
- data_shape = (1, 1, 1)
565
571
  num_steps = len(pol_theta)
566
572
  out_dict = dict()
567
573
  start_time = datetime.fromisoformat("2022-05-25T12:00:00")
@@ -587,14 +593,14 @@ def cs_with_correct_geometry():
587
593
  for m in range(ds.num_mod):
588
594
  hdu_list.append(
589
595
  fits.PrimaryHDU(
590
- data=np.ones(data_shape) * 1e3, header=fits.Header(header_list.pop(0))
596
+ data=np.ones(cs_data_shape) * 1e3, header=fits.Header(header_list.pop(0))
591
597
  )
592
598
  )
593
599
 
594
600
  out_dict[n] = [InstAccess(h) for h in hdu_list]
595
601
  start_time = ds.start_time + timedelta(seconds=60)
596
602
 
597
- return out_dict, data_shape
603
+ return out_dict
598
604
 
599
605
 
600
606
  @pytest.fixture(
@@ -630,11 +636,11 @@ def visp_modulation_matrix() -> np.ndarray:
630
636
 
631
637
 
632
638
  @pytest.fixture(scope="session")
633
- def fully_realistic_cs(
639
+ def fully_realistic_local_cs(
634
640
  cs_with_correct_geometry, visp_modulation_matrix, pac_fit_mode, pac_init_set
635
641
  ):
636
642
 
637
- cs_dict, data_shape = cs_with_correct_geometry
643
+ cs_dict = cs_with_correct_geometry
638
644
  dresser = Dresser()
639
645
  dresser.add_drawer(Drawer(cs_dict, skip_darks=False))
640
646
  CM = CalibrationUnit(dresser)
@@ -663,21 +669,75 @@ def fully_realistic_cs(
663
669
 
664
670
 
665
671
  @pytest.fixture(scope="session")
666
- def fully_realistic_dresser(fully_realistic_cs):
672
+ def fully_realistic_local_dresser(fully_realistic_local_cs):
667
673
  dresser = Dresser()
668
- dresser.add_drawer(Drawer(fully_realistic_cs))
674
+ dresser.add_drawer(Drawer(fully_realistic_local_cs))
675
+ return dresser
676
+
677
+
678
+ @pytest.fixture(scope="session")
679
+ def fully_realistic_global_dresser(fully_realistic_local_cs):
680
+
681
+ global_cs_dict = defaultdict(list)
682
+ for step, step_list in fully_realistic_local_cs.items():
683
+ for hdu in step_list:
684
+ hdu_copy = deepcopy(hdu)
685
+ hdu_copy.data = hdu_copy.data[0, 0, 0][None, None, None]
686
+ global_cs_dict[step].append(hdu_copy)
687
+
688
+ dresser = Dresser()
689
+ dresser.add_drawer(Drawer(global_cs_dict, remove_I_trend=False))
690
+
669
691
  return dresser
670
692
 
671
693
 
672
694
  @pytest.fixture(scope="session")
673
- def initialized_polcal_fitter(fully_realistic_dresser, pac_init_set, pac_fit_mode) -> PolcalFitter:
674
- return PolcalFitter(
675
- local_dresser=fully_realistic_dresser,
676
- global_dresser=fully_realistic_dresser,
695
+ def num_polcal_metrics_sample_points() -> int:
696
+ return 10
697
+
698
+
699
+ @pytest.fixture(scope="session")
700
+ def polcal_fit_nan_locations(cs_data_shape, num_polcal_metrics_sample_points):
701
+ # We want one nan that will be un-thinned so it can be ignored by downstream checks
702
+ # and one nan that will be thinned so we can confirm the interaction between thinning and
703
+ # downstream filtering
704
+ num_total_points = np.prod(cs_data_shape)
705
+ remainder = num_total_points % num_polcal_metrics_sample_points
706
+ if remainder:
707
+ stride = num_total_points // (num_polcal_metrics_sample_points - 1)
708
+ else:
709
+ stride = num_total_points // num_polcal_metrics_sample_points
710
+
711
+ thinned_index = list(range(num_total_points))[::stride]
712
+ discarded_indices = list(set(range(num_total_points)) - set(thinned_index))
713
+
714
+ included_nan_idx = np.unravel_index(choice(thinned_index), cs_data_shape)
715
+ discarded_nan_idx = np.unravel_index(choice(discarded_indices), cs_data_shape)
716
+
717
+ return [included_nan_idx, discarded_nan_idx]
718
+
719
+
720
+ @pytest.fixture(scope="session")
721
+ def post_fit_polcal_fitter(
722
+ fully_realistic_local_dresser,
723
+ fully_realistic_global_dresser,
724
+ pac_init_set,
725
+ pac_fit_mode,
726
+ polcal_fit_nan_locations,
727
+ ) -> PolcalFitter:
728
+ fitter = PolcalFitter(
729
+ local_dresser=fully_realistic_local_dresser,
730
+ global_dresser=fully_realistic_global_dresser,
677
731
  fit_mode=pac_fit_mode,
678
732
  init_set=pac_init_set,
679
733
  fit_TM=False,
734
+ suppress_local_starting_values=True,
680
735
  )
736
+ for idx in polcal_fit_nan_locations:
737
+ for parameter in fitter.fit_parameters[idx].values():
738
+ parameter.set(value=np.nan)
739
+
740
+ return fitter
681
741
 
682
742
 
683
743
  class InputDatasetTask(WorkflowTaskBase, InputDatasetMixin):
@@ -186,6 +186,7 @@ def quality_metrics(dataframe_json) -> list[Metric]:
186
186
  },
187
187
  "bin_strs": ["bin1", "bin2"],
188
188
  "total_bins": 100,
189
+ "sampled_bins": 20,
189
190
  "num_varied_I_sys": 2,
190
191
  },
191
192
  ["QUALITY_POLCAL_LOCAL_PAR_VALS", "QUALITY_TASK_BEAM 1"],
@@ -194,6 +195,7 @@ def quality_metrics(dataframe_json) -> list[Metric]:
194
195
  {
195
196
  "bin_strs": ["bin1", "bin2"],
196
197
  "total_bins": 100,
198
+ "sampled_bins": 20,
197
199
  "red_chi_list": [1, 2, 3],
198
200
  "residual_json": dataframe_json,
199
201
  },
@@ -203,6 +205,7 @@ def quality_metrics(dataframe_json) -> list[Metric]:
203
205
  {
204
206
  "bin_strs": ["bin1", "bin2"],
205
207
  "total_bins": 100,
208
+ "sampled_bins": 20,
206
209
  "efficiency_list": ((np.random.randn(4, 100) - 0.5) * 0.3).tolist(),
207
210
  "warnings": ["A warning"],
208
211
  },