Add Hi Goodtimes statistical filter 1 check#2732
Add Hi Goodtimes statistical filter 1 check#2732subagonsouth wants to merge 5 commits intoIMAP-Science-Operations-Center:devfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Implements Statistical Filter 1 for IMAP-Hi “goodtimes” processing to detect isotropic qualified-count excursions per ESA sweep/step, and adds unit/integration tests for the new filter and helpers.
Changes:
- Added Statistical Filter 1 implementation (
mark_statistical_filter_1) plus helper functions to compute per-(esa_sweep, esa_step) qualified counts, aggregate across pointings, and detect cull patterns via 2D convolutions. - Added tests covering helper behavior and end-to-end filter behavior (including extreme outliers and error cases).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
imap_processing/hi/hi_goodtimes.py |
Adds Statistical Filter 1 logic, including per-sweep qualified count computation and convolution-based cull mask detection. |
imap_processing/tests/hi/test_hi_goodtimes.py |
Adds unit tests for new helpers and integration tests for Statistical Filter 1 behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
imap_processing/hi/hi_goodtimes.py
Outdated
| # Select only the esa_step values present in the data | ||
| ds_reshaped = ds_reshaped.sel(esa_step=ds_reshaped.coords["esa_step"]) | ||
|
|
There was a problem hiding this comment.
The comment and code here don’t match: the sel(esa_step=ds_reshaped.coords['esa_step']) call is a no-op because it selects the coordinate values already present. If the intent is to restrict qualified_count/dataset to only ESA steps present in the original data, please select from the original unique esa_step values (or remove this block to avoid confusion).
| # Select only the esa_step values present in the data | |
| ds_reshaped = ds_reshaped.sel(esa_step=ds_reshaped.coords["esa_step"]) |
laspsandoval
left a comment
There was a problem hiding this comment.
I went through very carefully and everything looks good. Just some comments/questions about constant values. I'm approving since there isn't anything significant that needs to be changed. Nice work!
imap_processing/hi/hi_goodtimes.py
Outdated
|
|
||
| # Count qualified events per (esa_sweep, esa_step) using 2D array | ||
| n_sweeps = int(esa_sweep.max()) + 1 | ||
| n_esa_steps = int(esa_step.max()) + 1 |
There was a problem hiding this comment.
Does esa_sweep start at zero each time?
There was a problem hiding this comment.
Thanks for this comment. It made me realize that I have a bug. The esa_sweep dimension is somewhat arbitrary. It just counts how many times the ESA steps through the full range of energy steps (nominally 9-steps). But the bug is that the definition of which energy esa_step N corresponds to can change from one pointing to the next. So, I should be using esa_energy_step here in order to make sure that the correct energy steps get aligned.
imap_processing/hi/hi_goodtimes.py
Outdated
| # Count qualified events per (esa_sweep, esa_step) using 2D array | ||
| n_sweeps = int(esa_sweep.max()) + 1 | ||
| n_esa_steps = int(esa_step.max()) + 1 | ||
| counts_2d = np.zeros((n_sweeps, n_esa_steps), dtype=np.float64) |
There was a problem hiding this comment.
# For every pair of indices (sweep_i, step_i), add 1 to that cell.
imap_processing/hi/hi_goodtimes.py
Outdated
| extreme_threshold_sigma: float = 5.0, | ||
| min_consecutive_intervals: int = 3, | ||
| cull_code: int = CullCode.LOOSE, | ||
| min_pointings: int = 4, |
There was a problem hiding this comment.
Curious why you chose 4?
| def _identify_cull_pattern( | ||
| current_counts: xr.DataArray, | ||
| median_per_esa: xr.DataArray, | ||
| sigma_per_esa: xr.DataArray, |
There was a problem hiding this comment.
Good point. These are from the algorithm document, but should be added to the Hi Constants.
| # Dilate the consecutive detection to mark all positions in runs | ||
| # convolve1d centers the kernel, so we dilate to capture run edges | ||
| run_kernel = np.ones(min_consecutive) | ||
| run_positions = convolve1d( |
There was a problem hiding this comment.
Nice way of doing this.
d3cfcab to
0035d4f
Compare
Change Summary
Summary
Implements Statistical Filter 1 for Hi Goodtimes processing, which detects isotropic count rate increases per Algorithm Document Section 2.3.2.3.
New Functions
Algorithm
The filter applies three passes using 2D convolution:
Implementation Highlights
Test Coverage
Adds comprehensive tests for all new functions including edge cases for empty data, NaN handling, and pattern detection verification.
File changes
Closes: #2724