-
Notifications
You must be signed in to change notification settings - Fork 24
2821 hi l1c use hi goodtimes in pset processing #2829
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
subagonsouth
wants to merge
7
commits into
IMAP-Science-Operations-Center:dev
Choose a base branch
from
subagonsouth:2821-hi-l1c---use-hi-goodtimes-in-pset-processing
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
d264093
Add goodtimes to Hi class in cli.py
subagonsouth 5410453
Add goodtimes to Hi L1C algorithm
subagonsouth 7223b30
Extract fixtures for serving l1b_de and goodtimes datasets
subagonsouth 42ec497
Add tests that explicitly test goodtimes filtering
subagonsouth 83d8e10
Apply suggestions from code review
subagonsouth bd2f415
PR feedback
subagonsouth bb56049
Add hi goodtimes external goodtime file
subagonsouth File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,10 +24,11 @@ | |
| SpiceFrame, | ||
| frame_transform, | ||
| frame_transform_az_el, | ||
| get_spacecraft_to_instrument_spin_phase_offset, | ||
| ) | ||
| from imap_processing.spice.repoint import get_pointing_times | ||
| from imap_processing.spice.spin import ( | ||
| get_instrument_spin_phase, | ||
| get_spacecraft_spin_phase, | ||
| get_spin_data, | ||
| ) | ||
| from imap_processing.spice.time import met_to_ttj2000ns, ttj2000ns_to_et | ||
|
|
@@ -40,22 +41,21 @@ | |
|
|
||
|
|
||
| def hi_l1c( | ||
| de_dataset: xr.Dataset, calibration_prod_config_path: Path | ||
| de_dataset: xr.Dataset, | ||
| calibration_prod_config_path: Path, | ||
| goodtimes_ds: xr.Dataset, | ||
| ) -> list[xr.Dataset]: | ||
| """ | ||
| High level IMAP-Hi l1c processing function. | ||
|
|
||
| This function will be expanded once the l1c processing is better defined. It | ||
| will need to add inputs such as Ephemerides, Goodtimes inputs, and | ||
| instrument status summary and will output a Pointing Set CDF as well as a | ||
| Goodtimes list (CDF?). | ||
|
|
||
| Parameters | ||
| ---------- | ||
| de_dataset : xarray.Dataset | ||
| IMAP-Hi l1b de product. | ||
| calibration_prod_config_path : pathlib.Path | ||
| Calibration product configuration file. | ||
| goodtimes_ds : xarray.Dataset | ||
| Goodtimes dataset with cull_flags. | ||
|
|
||
| Returns | ||
| ------- | ||
|
|
@@ -64,15 +64,17 @@ def hi_l1c( | |
| """ | ||
| logger.info("Running Hi l1c processing") | ||
|
|
||
| # TODO: I am not sure what the input for Goodtimes will be so for now, | ||
| # If the input is an xarray Dataset, do pset processing | ||
| l1c_dataset = generate_pset_dataset(de_dataset, calibration_prod_config_path) | ||
| l1c_dataset = generate_pset_dataset( | ||
| de_dataset, calibration_prod_config_path, goodtimes_ds | ||
| ) | ||
|
|
||
| return [l1c_dataset] | ||
|
|
||
|
|
||
| def generate_pset_dataset( | ||
| de_dataset: xr.Dataset, calibration_prod_config_path: Path | ||
| de_dataset: xr.Dataset, | ||
| calibration_prod_config_path: Path, | ||
| goodtimes_ds: xr.Dataset, | ||
| ) -> xr.Dataset: | ||
| """ | ||
| Generate IMAP-Hi l1c pset xarray dataset from l1b product. | ||
|
|
@@ -83,6 +85,8 @@ def generate_pset_dataset( | |
| IMAP-Hi l1b de product. | ||
| calibration_prod_config_path : pathlib.Path | ||
| Calibration product configuration file. | ||
| goodtimes_ds : xarray.Dataset | ||
| Goodtimes dataset with cull_flags. | ||
|
|
||
| Returns | ||
| ------- | ||
|
|
@@ -110,9 +114,11 @@ def generate_pset_dataset( | |
| ) | ||
| pset_dataset.update(pset_geometry(pset_midpoint_et, logical_source_parts["sensor"])) | ||
| # Bin the counts into the spin-bins | ||
| pset_dataset.update(pset_counts(pset_dataset.coords, config_df, de_dataset)) | ||
| pset_dataset.update( | ||
| pset_counts(pset_dataset.coords, config_df, de_dataset, goodtimes_ds) | ||
| ) | ||
| # Calculate and add the exposure time to the pset_dataset | ||
| pset_dataset.update(pset_exposure(pset_dataset.coords, de_dataset)) | ||
| pset_dataset.update(pset_exposure(pset_dataset.coords, de_dataset, goodtimes_ds)) | ||
| # Get the backgrounds | ||
| pset_dataset.update(pset_backgrounds(pset_dataset.coords)) | ||
|
|
||
|
|
@@ -322,6 +328,7 @@ def pset_counts( | |
| pset_coords: dict[str, xr.DataArray], | ||
| config_df: pd.DataFrame, | ||
| l1b_de_dataset: xr.Dataset, | ||
| goodtimes_ds: xr.Dataset, | ||
| ) -> dict[str, xr.DataArray]: | ||
| """ | ||
| Bin direct events into PSET spin-bins. | ||
|
|
@@ -334,6 +341,8 @@ def pset_counts( | |
| The calibration product configuration dataframe. | ||
| l1b_de_dataset : xarray.Dataset | ||
| The L1B dataset for the pointing being processed. | ||
| goodtimes_ds : xarray.Dataset | ||
| Goodtimes dataset with cull_flags. | ||
|
|
||
| Returns | ||
| ------- | ||
|
|
@@ -362,11 +371,23 @@ def pset_counts( | |
| # Remove DEs with invalid trigger_id. This should only occur for a | ||
| # pointing with no events that gets a single fill event | ||
| good_mask = de_ds["trigger_id"].data != de_ds["trigger_id"].attrs["FILLVAL"] | ||
| if not np.any(good_mask): | ||
| return counts_var | ||
| elif not np.all(good_mask): | ||
| raise ValueError( | ||
| "An event with trigger_id=FILLVAL should only occur for a pointing " | ||
| "with no events that gets a single fill event. Events with mixed " | ||
| "valid and FILLVAL trigger_ids found." | ||
| ) | ||
|
|
||
| # Remove DEs not in Goodtimes/angles | ||
| good_mask &= good_time_and_phase_mask( | ||
| l1b_de_dataset.event_met.values, l1b_de_dataset.spin_phase.values | ||
| # For direct events, use nominal_bin (spacecraft spin bin 0-89) to look up goodtimes | ||
| goodtimes_mask = good_time_and_phase_mask( | ||
| de_ds.event_met.values, | ||
| de_ds.nominal_bin.values, | ||
| goodtimes_ds, | ||
| ) | ||
| de_ds = de_ds.isel(event_met=good_mask) | ||
| de_ds = de_ds.isel(event_met=goodtimes_mask) | ||
|
|
||
| # Get esa_energy_step for each event (recorded per packet, use ccsds_index) | ||
| esa_energy_steps = l1b_de_dataset["esa_energy_step"].data[de_ds["ccsds_index"].data] | ||
|
|
@@ -435,7 +456,9 @@ def pset_backgrounds(pset_coords: dict[str, xr.DataArray]) -> dict[str, xr.DataA | |
|
|
||
|
|
||
| def pset_exposure( | ||
| pset_coords: dict[str, xr.DataArray], l1b_de_dataset: xr.Dataset | ||
| pset_coords: dict[str, xr.DataArray], | ||
| l1b_de_dataset: xr.Dataset, | ||
| goodtimes_ds: xr.Dataset, | ||
| ) -> dict[str, xr.DataArray]: | ||
| """ | ||
| Calculate PSET exposure time. | ||
|
|
@@ -446,6 +469,8 @@ def pset_exposure( | |
| The PSET coordinates from the xarray.Dataset. | ||
| l1b_de_dataset : xarray.Dataset | ||
| The L1B dataset for the pointing being processed. | ||
| goodtimes_ds : xarray.Dataset | ||
| Goodtimes dataset with cull_flags. | ||
|
|
||
| Returns | ||
| ------- | ||
|
|
@@ -482,15 +507,26 @@ def pset_exposure( | |
|
|
||
| # Clock tick MET times are accumulation "edges". To get the mean spin-phase | ||
| # for a given clock tick, add 1/2 clock tick and compute spin-phase. | ||
| spin_phases = np.atleast_1d( | ||
| get_instrument_spin_phase( | ||
| clock_tick_mets + HiConstants.HALF_CLOCK_TICK_S, | ||
| SpiceFrame[f"IMAP_HI_{sensor_number}"], | ||
| ) | ||
| ) | ||
| mid_tick_mets = clock_tick_mets + HiConstants.HALF_CLOCK_TICK_S | ||
|
|
||
| # Compute spacecraft spin phase first (used for goodtimes filtering) | ||
| spacecraft_spin_phase = np.atleast_1d(get_spacecraft_spin_phase(mid_tick_mets)) | ||
|
|
||
| # Convert spacecraft spin phase to nominal_bins (0-89) for goodtimes lookup | ||
| nominal_bins = (spacecraft_spin_phase * 90).astype(np.int32) | ||
| nominal_bins = np.clip(nominal_bins, 0, 89) | ||
|
|
||
| # Compute instrument spin phase from spacecraft spin phase | ||
| # This implementation is identical to spin.get_instrument_spin_phase and | ||
| # is replicated here to avoid querying the spin dataframe again. | ||
| instrument_frame = SpiceFrame[f"IMAP_HI_{sensor_number}"] | ||
| phase_offset = get_spacecraft_to_instrument_spin_phase_offset(instrument_frame) | ||
| spin_phases = (spacecraft_spin_phase + phase_offset) % 1.0 | ||
|
Comment on lines
+519
to
+524
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. helpful comment. is this a big performance concerns? If not, it would be nice to avoid duplicate code and may get us in the future. |
||
|
|
||
| # Remove ticks not in good times/angles | ||
| good_mask = good_time_and_phase_mask(clock_tick_mets, spin_phases) | ||
| good_mask = good_time_and_phase_mask( | ||
| clock_tick_mets, nominal_bins, goodtimes_ds | ||
subagonsouth marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ) | ||
| spin_phases = spin_phases[good_mask] | ||
| clock_tick_weights = clock_tick_weights[good_mask] | ||
|
|
||
|
|
@@ -637,22 +673,40 @@ def get_de_clock_ticks_for_esa_step( | |
|
|
||
|
|
||
| def good_time_and_phase_mask( | ||
| tick_mets: np.ndarray, spin_phases: np.ndarray | ||
| ) -> npt.NDArray: | ||
| mets: np.ndarray, | ||
| nominal_bins: np.ndarray, | ||
| goodtimes_ds: xr.Dataset, | ||
| ) -> npt.NDArray[np.bool_]: | ||
| """ | ||
| Filter out the clock tick times that are not in good times and angles. | ||
| Filter out times that are not in good times based on the goodtimes dataset. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| tick_mets : np.ndarray | ||
| Clock-tick MET times. | ||
| spin_phases : np.ndarray | ||
| Spin phases for each clock tick. | ||
| mets : np.ndarray | ||
| MET times for each event or clock tick. | ||
| nominal_bins : np.ndarray | ||
| Spacecraft spin bins (0-89) for each event or clock tick. | ||
| goodtimes_ds : xarray.Dataset | ||
| Goodtimes dataset with cull_flags variable dimensioned (met, spin_bin). | ||
|
|
||
| Returns | ||
| ------- | ||
| keep_mask : np.ndarray | ||
| Boolean mask indicating which clock ticks are in good times/phases. | ||
| Boolean mask indicating which events/ticks are in good times. | ||
| """ | ||
| # TODO: Implement this once we have Goodtimes data product defined. | ||
| return np.full_like(tick_mets, True, dtype=bool) | ||
| gt_mets = goodtimes_ds["met"].values | ||
| cull_flags = goodtimes_ds["cull_flags"].values | ||
|
|
||
| # Map each event/tick to the nearest goodtimes MET interval | ||
| # searchsorted with side='right' - 1 gives the largest MET <= query MET | ||
| met_indices = np.searchsorted(gt_mets, mets, side="right") - 1 | ||
| met_indices = np.clip(met_indices, 0, len(gt_mets) - 1) | ||
|
|
||
| # Convert nominal_bins to int32 for indexing | ||
| spin_bins = nominal_bins.astype(np.int32) | ||
|
|
||
| # Look up cull_flags for each event/tick | ||
| # Events are good if cull_flags == 0 | ||
| event_cull_flags = cull_flags[met_indices, spin_bins] | ||
|
|
||
| return event_cull_flags == 0 | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what does 0-89 represents? 4 degree size bin? If so, can you assign 90 to variable?