From 25aac46a4a381917da10582dc98d844c1e094606 Mon Sep 17 00:00:00 2001 From: Luisa Date: Tue, 24 Mar 2026 09:29:50 -0600 Subject: [PATCH 1/3] use midpoint of event et for projecting data to map --- imap_processing/ena_maps/ena_maps.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/imap_processing/ena_maps/ena_maps.py b/imap_processing/ena_maps/ena_maps.py index ed7b67523..ecbc1c0d2 100644 --- a/imap_processing/ena_maps/ena_maps.py +++ b/imap_processing/ena_maps/ena_maps.py @@ -136,14 +136,19 @@ def match_coords_to_indices( if isinstance(input_object, PointingSet) and isinstance(output_object, PointingSet): raise ValueError("Cannot match indices between two PointingSet objects.") - # If event_et is not specified, use epoch of the PointingSet, if present. + # If event_et is not specified, use the first epoch midpoint of the PointingSet, if + # present. # The epoch will be in units of terrestrial time (TT) J2000 nanoseconds, # which must be converted to ephemeris time (ET) for SPICE. if event_et is None: if isinstance(input_object, PointingSet): - event_et = ttj2000ns_to_et(input_object.epoch) + event_et = ttj2000ns_to_et( + input_object.epoch + input_object.epoch_delta / 2 + ) elif isinstance(output_object, PointingSet): - event_et = ttj2000ns_to_et(output_object.epoch) + event_et = ttj2000ns_to_et( + output_object.epoch + output_object.epoch_delta / 2 + ) else: raise ValueError( "Event time must be specified if both objects are SkyMaps." @@ -301,6 +306,18 @@ def epoch(self) -> int: """ return self.data["epoch"].values[0] + @property + def epoch_delta(self) -> int: + """ + The singular epoch delta value from the xarray.Dataset. + + Returns + ------- + epoch_delta: int + The epoch delta value [J2000 TT ns] of the pointing set. + """ + return self.data["epoch_delta"].values[0] if "epoch_delta" in self.data else 0 + @property def unwrapped_dims_dict(self) -> dict[str, tuple[str, ...]]: """ From afc399e65526776e8d3a18ae32e388f4b1eec032 Mon Sep 17 00:00:00 2001 From: Luisa Date: Tue, 24 Mar 2026 09:57:38 -0600 Subject: [PATCH 2/3] test epoch delta was initialized --- imap_processing/ena_maps/ena_maps.py | 2 +- imap_processing/tests/ena_maps/test_ena_maps.py | 4 +++- imap_processing/tests/ultra/mock_data.py | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/imap_processing/ena_maps/ena_maps.py b/imap_processing/ena_maps/ena_maps.py index ecbc1c0d2..b511873f7 100644 --- a/imap_processing/ena_maps/ena_maps.py +++ b/imap_processing/ena_maps/ena_maps.py @@ -136,7 +136,7 @@ def match_coords_to_indices( if isinstance(input_object, PointingSet) and isinstance(output_object, PointingSet): raise ValueError("Cannot match indices between two PointingSet objects.") - # If event_et is not specified, use the first epoch midpoint of the PointingSet, if + # If event_et is not specified, use the midpoint of the PointingSet, if # present. # The epoch will be in units of terrestrial time (TT) J2000 nanoseconds, # which must be converted to ephemeris time (ET) for SPICE. diff --git a/imap_processing/tests/ena_maps/test_ena_maps.py b/imap_processing/tests/ena_maps/test_ena_maps.py index 617aa1035..5555f4f52 100644 --- a/imap_processing/tests/ena_maps/test_ena_maps.py +++ b/imap_processing/tests/ena_maps/test_ena_maps.py @@ -76,7 +76,9 @@ def test_instantiate(self): ultra_pset.num_points, hp.nside2npix(self.nside), ) - + # check the epoch delta + # This was mocked to 10 in the fixture data + assert ultra_pset.epoch_delta == 10 # Check the repr exists assert "UltraPointingSet" in repr(ultra_pset) diff --git a/imap_processing/tests/ultra/mock_data.py b/imap_processing/tests/ultra/mock_data.py index 4a4bae451..bf8c89e39 100644 --- a/imap_processing/tests/ultra/mock_data.py +++ b/imap_processing/tests/ultra/mock_data.py @@ -405,6 +405,7 @@ def mock_l1c_pset_product_healpix( [CoordNames.TIME.value, CoordNames.HEALPIX_INDEX.value], np.full((1, npix), ImapPSETUltraFlags.NONE.value, dtype=np.uint16), ), + "epoch_delta": ([CoordNames.TIME.value], np.array([10], dtype=np.float64)), }, coords={ CoordNames.TIME.value: [ From 2a8ab68e8a70ec9e894c103fe817ef086ecf7d37 Mon Sep 17 00:00:00 2001 From: Luisa Date: Tue, 24 Mar 2026 12:01:03 -0600 Subject: [PATCH 3/3] pr comments' --- imap_processing/ena_maps/ena_maps.py | 36 ++++++++++++------- .../tests/ena_maps/test_ena_maps.py | 36 ++++++++++++++++--- imap_processing/tests/ultra/mock_data.py | 1 + 3 files changed, 56 insertions(+), 17 deletions(-) diff --git a/imap_processing/ena_maps/ena_maps.py b/imap_processing/ena_maps/ena_maps.py index b511873f7..77ef15702 100644 --- a/imap_processing/ena_maps/ena_maps.py +++ b/imap_processing/ena_maps/ena_maps.py @@ -22,7 +22,7 @@ # so we define an enum to handle the coordinate names. from imap_processing.ena_maps.utils.coordinates import CoordNames from imap_processing.spice import geometry -from imap_processing.spice.time import ttj2000ns_to_et +from imap_processing.spice.time import met_to_ttj2000ns, ttj2000ns_to_et logger = logging.getLogger(__name__) @@ -142,13 +142,9 @@ def match_coords_to_indices( # which must be converted to ephemeris time (ET) for SPICE. if event_et is None: if isinstance(input_object, PointingSet): - event_et = ttj2000ns_to_et( - input_object.epoch + input_object.epoch_delta / 2 - ) + event_et = input_object.midpoint_j2000_et elif isinstance(output_object, PointingSet): - event_et = ttj2000ns_to_et( - output_object.epoch + output_object.epoch_delta / 2 - ) + event_et = output_object.midpoint_j2000_et else: raise ValueError( "Event time must be specified if both objects are SkyMaps." @@ -307,16 +303,17 @@ def epoch(self) -> int: return self.data["epoch"].values[0] @property - def epoch_delta(self) -> int: + def midpoint_j2000_et(self) -> float: """ - The singular epoch delta value from the xarray.Dataset. + The midpoint of the pointing in ET. Returns ------- - epoch_delta: int - The epoch delta value [J2000 TT ns] of the pointing set. + midpoint: int + The midpoint value [J2000 ET] of the pointing set. """ - return self.data["epoch_delta"].values[0] if "epoch_delta" in self.data else 0 + epoch_delta = self.data["epoch_delta"].values[0] + return float(ttj2000ns_to_et(self.epoch + epoch_delta / 2)) @property def unwrapped_dims_dict(self) -> dict[str, tuple[str, ...]]: @@ -713,6 +710,21 @@ def __init__(self, dataset: xr.Dataset): # Update az_el_points using the base class method self.update_az_el_points() + @property + def midpoint_j2000_et(self) -> float: + """ + The midpoint of the pointing in ET. + + Returns + ------- + midpoint: int + The midpoint value [J2000 ET] of the pointing set. + """ + epoch_delta = met_to_ttj2000ns( + self.data["pointing_end_met"].data - self.data["pointing_start_met"].data + ) + return float(ttj2000ns_to_et(self.epoch + epoch_delta / 2)) + # Define the Map classes class AbstractSkyMap(ABC): diff --git a/imap_processing/tests/ena_maps/test_ena_maps.py b/imap_processing/tests/ena_maps/test_ena_maps.py index 5555f4f52..7a6049c30 100644 --- a/imap_processing/tests/ena_maps/test_ena_maps.py +++ b/imap_processing/tests/ena_maps/test_ena_maps.py @@ -20,6 +20,7 @@ from imap_processing.ena_maps.utils import spatial_utils from imap_processing.ena_maps.utils.coordinates import CoordNames from imap_processing.spice import geometry +from imap_processing.spice.time import met_to_ttj2000ns, ttj2000ns_to_et @pytest.fixture(autouse=True, scope="module") @@ -76,9 +77,10 @@ def test_instantiate(self): ultra_pset.num_points, hp.nside2npix(self.nside), ) - # check the epoch delta - # This was mocked to 10 in the fixture data - assert ultra_pset.epoch_delta == 10 + # check that the midpoint_j2000_et property is equal to the expected value + assert ultra_pset.midpoint_j2000_et == ttj2000ns_to_et( + ultra_pset.epoch + self.l1c_pset_products[0].epoch_delta[0] / 2 + ) # Check the repr exists assert "UltraPointingSet" in repr(ultra_pset) @@ -150,12 +152,15 @@ class TestHiPointingSet: def test_init(self, hi_pset_cdf_path): """Test coverage for __init__ method.""" pset_ds = load_cdf(hi_pset_cdf_path) + delta = 10 + pset_ds["epoch_delta"] = ("epoch", np.array([delta])) # Add dummy epoch delta hi_pset = ena_maps.HiPointingSet(pset_ds) assert isinstance(hi_pset, ena_maps.HiPointingSet) assert hi_pset.spice_reference_frame == geometry.SpiceFrame.IMAP_HAE assert hi_pset.num_points == 3600 np.testing.assert_array_equal(hi_pset.az_el_points.shape, (3600, 2)) - + # check that the midpoint_j2000_et property is equal to the expected value + assert hi_pset.midpoint_j2000_et == ttj2000ns_to_et(hi_pset.epoch + delta / 2) for var_name in ["exposure_factor", "bg_rate", "bg_rate_sys_err"]: assert var_name in hi_pset.data @@ -166,7 +171,9 @@ def test_from_cdf(self, hi_pset_cdf_path): def test_plays_nice_with_rectangular_sky_map(self, hi_pset_cdf_path): """Test that HiPointingSet works with RectangularSkyMap""" - hi_pset = ena_maps.HiPointingSet(hi_pset_cdf_path) + hi_ds = load_cdf(hi_pset_cdf_path) + hi_ds["epoch_delta"] = ("epoch", np.array([0])) # Add dummy epoch delta + hi_pset = ena_maps.HiPointingSet(hi_ds) rect_map = ena_maps.RectangularSkyMap( spacing_deg=2, spice_frame=geometry.SpiceFrame.IMAP_HAE ) @@ -215,6 +222,16 @@ def lo_pset_ds(): dims=["epoch"], name="epoch", ) + dataset.coords["pointing_start_met"] = xr.DataArray( + [1], + dims=["epoch"], + name="epoch", + ) + dataset.coords["pointing_end_met"] = xr.DataArray( + [10], + dims=["epoch"], + name="epoch", + ) dataset.coords["spin_angle"] = xr.DataArray( [i for i in range(3600)], dims=["spin_angle"], @@ -256,6 +273,14 @@ def test_init(self, lo_pset_ds): assert lo_pset.num_points == 144000 np.testing.assert_array_equal(lo_pset.az_el_points.shape, (144000, 2)) + # check that the midpoint_j2000_et property is equal to the expected value + assert lo_pset.midpoint_j2000_et == ttj2000ns_to_et( + lo_pset.epoch + + met_to_ttj2000ns( + lo_pset_ds.pointing_end_met - lo_pset_ds.pointing_start_met + ) + / 2 + ) for var_name in ["exposure_time", "h_counts"]: assert var_name in lo_pset.data @@ -297,6 +322,7 @@ def create_hi_pset_with_multidim_coords( HiPointingSet with multi-dimensional az_el_points. """ pset_ds = load_cdf(hi_pset_cdf_path) + pset_ds["epoch_delta"] = ("epoch", np.array([0])) # Add dummy epoch delta hi_pset = ena_maps.HiPointingSet(pset_ds) hi_pset.data["hae_longitude"] = xr.DataArray( np.random.uniform(0, 360, shape), diff --git a/imap_processing/tests/ultra/mock_data.py b/imap_processing/tests/ultra/mock_data.py index bf8c89e39..5a410e182 100644 --- a/imap_processing/tests/ultra/mock_data.py +++ b/imap_processing/tests/ultra/mock_data.py @@ -167,6 +167,7 @@ def get_binomial_counts(distance_scaling, lat_bin, central_lat_bin): ], sensitivity, ), + "epoch_delta": ([CoordNames.TIME.value], np.array([10], dtype=np.float64)), }, coords={ CoordNames.TIME.value: [