Conversation
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The job omits a required repoint-table dependency and has unhandled empty-input and partial-output failure paths.
Review effort: Balanced
Findings: 1
Open (3)
What changed in this PR
Adds per-pointing IMAP-Lo pivot-platform CK generation using L1B NHK pivot measurements.
Changes:
- Adds pivot-angle calculation and CK generation.
- Integrates the
pivot-ckernelCLI job. - Extracts reusable CK writing/naming utilities and updates data-access support.
| File | Description |
|---|---|
pyproject.toml |
Updates data-access dependency. |
poetry.lock |
Locks data-access 1.3.0. |
imap_processing/cli.py |
Integrates pivot-kernel processing. |
imap_processing/lo/lo_pivot_kernel.py |
Generates per-pointing pivot CKs. |
imap_processing/lo/l1b/lo_l1b.py |
Shares median pivot calculation. |
imap_processing/spice/pointing_frame.py |
Extracts reusable CK utilities. |
imap_processing/tests/test_cli.py |
Tests CLI integration. |
imap_processing/tests/lo/test_lo_pivot_kernel.py |
Tests pivot CK behavior. |
imap_processing/tests/lo/test_lo_l1b.py |
Tests median pivot calculation. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
laspsandoval
left a comment
There was a problem hiding this comment.
Looks good. One question just to make certain timing is correct.
| """ | ||
| pointing_start_met, pointing_end_met = get_pointing_times_from_id(repoint_id) | ||
| # Use the same pivot angle as the goodtimes product. | ||
| pivot_angle = get_median_pivot_angle(load_cdf(l1b_nhk_path)) |
There was a problem hiding this comment.
The averaging window starts from the first NHK sample, not the pointing start. Is the repoint NHK file always timed so its first sample matches the pointing start? Is it possible that the window of time could include samples when the pivot is moving?
There was a problem hiding this comment.
The get_median_pivot_angle() function actually uses data in the range [hk_start_epoch + 0.5 hours:hk_start_epoch. + 22.5 hours]. This was what the Lo IT wanted in their other L1B processing, so I just extracted that into a function and call it here.
| with tempfile.TemporaryDirectory(dir=kernel_path.parent) as tmp_dir: | ||
| tmp_kernel_path = Path(tmp_dir) / kernel_path.name | ||
| write_lo_pivot_ck(tmp_kernel_path, segment, pivot_angle, l1b_nhk_path.name) | ||
| os.link(tmp_kernel_path, kernel_path) |
There was a problem hiding this comment.
I think Opus 5.5 might be overly-conservative/paranoid here, but it says os.link() may fail on FAT/exFAT and some network mounts, so it might be worth to consider something like:
try:
os.link(tmp_kernel_path, kernel_path)
except FileExistsError:
raise
except OSError:
# The filesystem does not support hard links. Create the kernel exclusively
# and copy into it, removing it if the copy fails.
with open(kernel_path, "xb") as dst:
try:
with open(tmp_kernel_path, "rb") as src:
shutil.copyfileobj(src, dst)
except BaseException:
dst.close()
kernel_path.unlink()
raiseI don't see an obvious reason why this would be necessary, but figured it wouldn't hurt to forward the comment to you.
| coarse_pot_pri[(hk_epoch_ets >= start_et_hk) & (hk_epoch_ets <= end_et_hk)] | ||
| ) | ||
| pivot = get_median_pivot_angle(cdf_hk) | ||
| if np.isnan(pivot): |
There was a problem hiding this comment.
I'm wondering if we should fail loudly here on encountering a nan, since the kernel generation process is going through all the trouble of making sure that the correct angle is spit out. The only reason this is in (I'm sure I put this default 90 in) was to have the code do the same thing as Nathan's pipeline.
On the other hand - the code is calling np.nanmedian. Perhaps this check should go inside the function and it return float | np.nan.
There was a problem hiding this comment.
I made it fail loudly in the kernel generation code:
if np.isnan(pivot_angle):
raise ValueError(f"No valid pivot angle samples in {l1b_nhk_path.name}.")
|
This makes sense to me reading it at a high-level - using This is instructive for me because I was always under the impression that these kernels are generated by code close to the hardware, not code downstream of a pipeline that looks at L1 products (with all the potential buginess of pipeline code affecting the kernel generation). |
|
One part that always confused me was that the This doesn't affect the correctness of this PR, but unless you know the history and rationale behind this, maybe we should open an issue on this? I'm happy to bring it up with the team when I talk to them next. |
vineetbansal
left a comment
There was a problem hiding this comment.
General approach makes sense - I haven't gone through the tests line by line but their intention going by their test names makes sense.
Good point. The |


Change Summary
Overview
Closes #3325. Adds a Lo
pivot-ckerneljob that generates one SPICE CK per pointing for theIMAP_LOpivot-platform frame, from that pointing's Lo L1B NHK. The pivot angle uses the same calculation as goodtimes. Kernels are namedimap_lopivot-repoint#####_<start>_<end>_<NNN>.bc, which imap-data-access v1.3.0 supports.File changes
lo/lo_pivot_kernel.py(new): builds and writes the single-segment pivot CK. Errors if the kernel already exists or there are no valid pivot samples.lo/l1b/lo_l1b.py: the goodtimes pivot calculation is nowget_median_pivot_angle, shared with the new job. Goodtimes output is unchanged.spice/pointing_frame.py: CK writing and filename helpers extracted for reuse. DPS output is unchanged.cli.py: new Lol1b/pivot-ckerneljob (--repointing, one NHK input). Kernel version lookup is now shared withpointing-attitude.pyproject.toml/poetry.lock: bump toimap-data-access>=1.3.0.Testing
get_median_pivot_angle, 3 for the CLI job.pivot.Closes: #3325