Fix GLOWS L2 - add per-bin zero handling for flux calculations#2890
Fix GLOWS L2 - add per-bin zero handling for flux calculations#2890vmartinez-cu wants to merge 2 commits intoIMAP-Science-Operations-Center:devfrom
Conversation
…en calculating photon flux and flux uncertainties. Add a unit test for this test case.
There was a problem hiding this comment.
Pull request overview
This PR updates GLOWS L2 DailyLightcurve flux/uncertainty computation to avoid divide-by-zero when exposure is zero, and adds a regression test around zero-exposure behavior.
Changes:
- Adds a guard to skip flux/uncertainty division when exposure is zero.
- Adds a unit test ensuring zero exposure produces zero flux/uncertainty without triggering NumPy divide/invalid warnings.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
imap_processing/glows/l2/glows_l2_data.py |
Changes the flux/uncertainty calculation guard to avoid division when exposure is zero. |
imap_processing/tests/glows/test_glows_l2_data.py |
Adds a regression test for all-zero exposure values. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if len(self.exposure_times) != 0 and self.exposure_times[0] > 0: | ||
| self.photon_flux = self.raw_histograms / self.exposure_times | ||
| self.flux_uncertainties = raw_uncertainties / self.exposure_times |
There was a problem hiding this comment.
The new guard self.exposure_times[0] > 0 only checks the first element and then divides the entire array. This doesn’t implement per-bin zero handling (as requested in #2370 / PR title) and would still divide by zero if exposure_times ever contains a mix of zeros and non-zeros. Consider using element-wise division (e.g., np.divide(..., out=..., where=self.exposure_times > 0)) so bins with zero exposure remain 0 without relying on the “uniform across bins” assumption.
| if len(self.exposure_times) != 0 and self.exposure_times[0] > 0: | |
| self.photon_flux = self.raw_histograms / self.exposure_times | |
| self.flux_uncertainties = raw_uncertainties / self.exposure_times | |
| if self.exposure_times.size > 0: | |
| exposure_mask = self.exposure_times > 0 | |
| np.divide( | |
| self.raw_histograms, | |
| self.exposure_times, | |
| out=self.photon_flux, | |
| where=exposure_mask, | |
| ) | |
| np.divide( | |
| raw_uncertainties, | |
| self.exposure_times, | |
| out=self.flux_uncertainties, | |
| where=exposure_mask, | |
| ) |
There was a problem hiding this comment.
All bins have the same exposure time so just the first one can be checked
| assert np.all(np.isfinite(lc.photon_flux)) | ||
| assert np.all(np.isfinite(lc.flux_uncertainties)) | ||
|
|
||
|
|
There was a problem hiding this comment.
This test only covers the case where all exposure values are zero. Since the underlying bug report/PR title calls for per-bin zero handling, add a test case that exercises mixed exposure values (some bins zero, others non-zero) once the implementation supports it, to ensure only the zero-exposure bins produce zero flux/uncertainty and no divide-by-zero occurs.
| @pytest.mark.xfail( | |
| reason=( | |
| "Per-bin zero exposure handling not yet implemented. " | |
| "Once supported, construct a dataset with mixed exposure values " | |
| "(some bins zero, others non-zero) and assert that only the " | |
| "zero-exposure bins have zero flux/uncertainty with no divide-by-zero." | |
| ) | |
| ) | |
| def test_mixed_zero_exposure_values(l1b_dataset, mock_ecliptic_bin_centers): | |
| """Placeholder for mixed per-bin exposure test. | |
| This test should be updated once the pipeline supports per-bin zero | |
| exposure handling. The intended behavior is: | |
| * Bins with zero exposure time produce zero photon_flux and | |
| flux_uncertainties. | |
| * Bins with non-zero exposure time retain finite, non-NaN values. | |
| * No divide-by-zero or invalid floating-point operations occur even when | |
| using np.errstate(divide="raise", invalid="raise"). | |
| """ | |
| pytest.xfail( | |
| "TODO: implement mixed per-bin exposure test when exposure can vary by bin." | |
| ) |
There was a problem hiding this comment.
All bins have the same exposure time so if one is zero all will be zero
tech3371
left a comment
There was a problem hiding this comment.
minor requests but looks great. That was so quick! Nice!
|
|
||
| # TODO: Only where exposure counts != 0 | ||
| if len(self.exposure_times) != 0: | ||
| if len(self.exposure_times) != 0 and self.exposure_times[0] > 0: |
There was a problem hiding this comment.
Why do you check only the first one? Just curious
| def test_zero_exposure_values(l1b_dataset, mock_ecliptic_bin_centers): | ||
| """Zero exposure yields zero flux and zero uncertainty per bin. | ||
|
|
||
| Note: all bins have the same exposure time, so if one is zero all are zero. |
There was a problem hiding this comment.
Ah I see. Can you move this note into the actual code. This answers my question above!
|
|
||
| # TODO: Only where exposure counts != 0 | ||
| if len(self.exposure_times) != 0: | ||
| if len(self.exposure_times) != 0 and self.exposure_times[0] > 0: |
There was a problem hiding this comment.
now based on below comment, can you add one more check here to make sure that exposure is same for all? Eg. len(np.unique(self.exposure_times)) == 1
There was a problem hiding this comment.
Good suggestion!
maxinelasp
left a comment
There was a problem hiding this comment.
Looks good, thanks Veronica!
| if ( | ||
| len(self.exposure_times) != 0 | ||
| and self.exposure_times[0] > 0 | ||
| and len(np.unique(self.exposure_times)) == 1 | ||
| ): |
There was a problem hiding this comment.
@maxinelasp What should happen if these conditions aren't met. Should an error be raised or should processing proceed?
This pull request handles zero exposure values in the
DailyLightcurveclass to prevent division by zero errors and adds a corresponding test to ensure correct behavior.Closes #2370