Skip to content

Commit f9a0b26

Browse files
author
Menlo Innovations - CAVA Project
committed
Harrison 3029 - LFAR/PMCL - CoDICE: Pass through several variables into L3a lo direct events
1 parent e88c45d commit f9a0b26

11 files changed

Lines changed: 92 additions & 65 deletions

imap_l3_processing/codice/l3/lo/codice_lo_processor.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,12 @@ def process_l3a_direct_event_data_product(self, dependencies: CodiceLoL3aDirectE
232232
energy_bin_delta_plus=np.flip(esa_energy_per_charge_lookup.delta_plus),
233233
energy_bin_delta_minus=np.flip(esa_energy_per_charge_lookup.delta_minus),
234234
spin_angle_bin=spin_angle_lut.bin_centers,
235-
spin_angle_bin_delta=spin_angle_lut.bin_deltas
235+
spin_angle_bin_delta=spin_angle_lut.bin_deltas,
236+
half_spin_per_esa_step=codice_sw_priority_counts_l1a_data.half_spin_per_esa_step,
237+
rgfo_spin_sector=codice_sw_priority_counts_l1a_data.rgfo_spin_sector,
238+
rgfo_esa_step=codice_sw_priority_counts_l1a_data.rgfo_esa_step,
239+
nso_spin_sector=codice_sw_priority_counts_l1a_data.nso_spin_sector,
240+
nso_esa_step=codice_sw_priority_counts_l1a_data.nso_esa_step
236241
)
237242

238243
def process_l3a_3d_distribution_product(self, dependencies: CodiceLoL3a3dDistributionsDependencies):

imap_l3_processing/codice/l3/lo/models.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,11 @@ class CodiceLoL1aSWPriorityRates:
154154
p2_heplusplus: np.ndarray
155155
p3_heavies: np.ndarray
156156
p4_dcrs: np.ndarray
157+
half_spin_per_esa_step: np.ndarray
158+
rgfo_spin_sector: np.ndarray
159+
rgfo_esa_step: np.ndarray
160+
nso_spin_sector: np.ndarray
161+
nso_esa_step: np.ndarray
157162

158163
@classmethod
159164
def read_from_cdf(cls, cdf_path: Path):
@@ -174,7 +179,12 @@ def read_from_cdf(cls, cdf_path: Path):
174179
p1_hplus=read_variable_and_mask_fill_values(cdf["p1_hplus"]),
175180
p2_heplusplus=read_variable_and_mask_fill_values(cdf["p2_heplusplus"]),
176181
p3_heavies=read_variable_and_mask_fill_values(cdf["p3_heavies"]),
177-
p4_dcrs=read_variable_and_mask_fill_values(cdf["p4_dcrs"])
182+
p4_dcrs=read_variable_and_mask_fill_values(cdf["p4_dcrs"]),
183+
half_spin_per_esa_step=read_variable_and_mask_fill_values(cdf["half_spin_per_esa_step"]),
184+
rgfo_spin_sector=read_variable_and_mask_fill_values(cdf["rgfo_spin_sector"]),
185+
rgfo_esa_step=read_variable_and_mask_fill_values(cdf["rgfo_esa_step"]),
186+
nso_spin_sector=read_variable_and_mask_fill_values(cdf["nso_spin_sector"]),
187+
nso_esa_step=read_variable_and_mask_fill_values(cdf["nso_esa_step"]),
178188
)
179189

180190

@@ -306,6 +316,11 @@ def to_data_product_variables(self) -> list[DataProductVariable]:
306316
EVENT_INDEX_LABEL_VAR_NAME = "event_index_label"
307317
ENERGY_BIN_LABEL_VAR_NAME = "energy_bin_label"
308318
SPIN_ANGLE_BIN_LABEL_VAR_NAME = "spin_angle_bin_label"
319+
HALF_SPIN_PER_ESA_STEP_VAR_NAME = "half_spin_per_esa_step"
320+
RGFO_SPIN_SECTOR_VAR_NAME = "rgfo_spin_sector"
321+
RGFO_ESA_STEP_VAR_NAME = "rgfo_esa_step"
322+
NSO_SPIN_SECTOR_VAR_NAME = "nso_spin_sector"
323+
NSO_ESA_STEP_VAR_NAME = "nso_esa_step"
309324

310325

311326
@dataclass
@@ -361,6 +376,11 @@ class CodiceLoL3aDirectEventDataProduct(CodiceLoDirectEventData, DataProduct):
361376
event_index_label: np.ndarray = field(init=False)
362377
energy_bin_label: np.ndarray = field(init=False)
363378
spin_angle_bin_label: np.ndarray = field(init=False)
379+
half_spin_per_esa_step: np.ndarray
380+
rgfo_spin_sector: np.ndarray
381+
rgfo_esa_step: np.ndarray
382+
nso_spin_sector: np.ndarray
383+
nso_esa_step: np.ndarray
364384

365385
def __post_init__(self):
366386
self.priority_index = np.arange(CODICE_LO_L2_NUM_PRIORITIES)
@@ -404,6 +424,11 @@ def to_data_product_variables(self) -> list[DataProductVariable]:
404424
DataProductVariable(PRIORITY_INDEX_LABEL_VAR_NAME, self.priority_index_label),
405425
DataProductVariable(EVENT_INDEX_VAR_NAME, self.event_index),
406426
DataProductVariable(EVENT_INDEX_LABEL_VAR_NAME, self.event_index_label),
427+
DataProductVariable(HALF_SPIN_PER_ESA_STEP_VAR_NAME, self.half_spin_per_esa_step),
428+
DataProductVariable(RGFO_SPIN_SECTOR_VAR_NAME, self.rgfo_spin_sector),
429+
DataProductVariable(RGFO_ESA_STEP_VAR_NAME, self.rgfo_esa_step),
430+
DataProductVariable(NSO_SPIN_SECTOR_VAR_NAME, self.nso_spin_sector),
431+
DataProductVariable(NSO_ESA_STEP_VAR_NAME, self.nso_esa_step),
407432
]
408433

409434

tests/codice/l3/lo/test_codice_lo_processor.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,11 @@ def test_process_l3a_direct_events(self, mock_calculate_mass, mock_calculate_mas
525525
sw_priority_rates.p2_heplusplus = rng.random(priority_counts_variable_shape)
526526
sw_priority_rates.p3_heavies = rng.random(priority_counts_variable_shape)
527527
sw_priority_rates.p4_dcrs = rng.random(priority_counts_variable_shape)
528+
sw_priority_rates.half_spin_per_esa_step = rng.random((len(epochs), num_energy_bins))
529+
sw_priority_rates.rgfo_spin_sector = rng.random(len(epochs))
530+
sw_priority_rates.rgfo_esa_step = rng.random(len(epochs))
531+
sw_priority_rates.nso_spin_sector = rng.random(len(epochs))
532+
sw_priority_rates.nso_esa_step = rng.random(len(epochs))
528533

529534
nsw_priority_rates = create_dataclass_mock(CodiceLoL1aNSWPriorityRates)
530535
nsw_priority_rates.epoch = epochs
@@ -647,6 +652,11 @@ def test_process_l3a_direct_events(self, mock_calculate_mass, mock_calculate_mas
647652
l3a_direct_event_data_product.energy_bin_delta_minus)
648653
self.assertEqual(mock_spin_angle_lookup.bin_centers, l3a_direct_event_data_product.spin_angle_bin)
649654
self.assertEqual(mock_spin_angle_lookup.bin_deltas, l3a_direct_event_data_product.spin_angle_bin_delta)
655+
np.testing.assert_array_equal(sw_priority_rates.half_spin_per_esa_step, l3a_direct_event_data_product.half_spin_per_esa_step)
656+
np.testing.assert_array_equal(sw_priority_rates.rgfo_spin_sector, l3a_direct_event_data_product.rgfo_spin_sector)
657+
np.testing.assert_array_equal(sw_priority_rates.rgfo_esa_step, l3a_direct_event_data_product.rgfo_esa_step)
658+
np.testing.assert_array_equal(sw_priority_rates.nso_spin_sector, l3a_direct_event_data_product.nso_spin_sector)
659+
np.testing.assert_array_equal(sw_priority_rates.nso_esa_step, l3a_direct_event_data_product.nso_esa_step)
650660

651661
@patch('imap_l3_processing.codice.l3.lo.codice_lo_processor.convert_count_rate_to_intensity')
652662
@patch('imap_l3_processing.codice.l3.lo.codice_lo_processor.rebin_3d_distribution_azimuth_to_elevation')
@@ -813,10 +823,10 @@ def test_process_l3a_direct_events_all_fill_integration(self):
813823

814824
dependencies = CodiceLoL3aDirectEventsDependencies.from_file_paths(
815825
sw_priority_rates_cdf=get_test_data_path(
816-
"codice/imap_codice_l1a_lo-sw-priority-all-fill_20250814_v001.cdf"),
826+
"codice/imap_codice_l1a_lo-sw-priority_20260307_v003-all-fill.cdf"),
817827
nsw_priority_rates_cdf=get_test_data_path(
818-
"codice/imap_codice_l1a_lo-nsw-priority-all-fill_20250814_v001.cdf"),
819-
direct_event_path=get_test_data_path("codice/imap_codice_l2_lo-direct-events-all-fill_20250814_v001.cdf"),
828+
"codice/imap_codice_l1a_lo-nsw-priority_20260307_v003-all-fill.cdf"),
829+
direct_event_path=get_test_data_path("codice/imap_codice_l2_lo-direct-events_20260307_v003-all-fill.cdf"),
820830
mass_coefficients_file_path=get_test_data_path(
821831
"codice/imap_codice_mass-coefficient-lookup_20241110_v003.csv"),
822832
esa_to_energy_per_charge_file_path=get_test_data_path(

tests/codice/l3/lo/test_models.py

Lines changed: 45 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -394,9 +394,14 @@ def test_codice_lo_l3a_direct_event_to_data_product(self):
394394
position=rng.random((len(epoch), len(priority), len(event_num))),
395395
spin_angle_bin=rng.random(24),
396396
spin_angle_bin_delta=rng.random(24),
397-
energy_bin=rng.random(128),
398-
energy_bin_delta_plus=rng.random(128),
399-
energy_bin_delta_minus=rng.random(128),
397+
energy_bin=rng.random(len(energy_step)),
398+
energy_bin_delta_plus=rng.random(len(energy_step)),
399+
energy_bin_delta_minus=rng.random(len(energy_step)),
400+
half_spin_per_esa_step=rng.random((len(epoch), len(energy_step))),
401+
rgfo_spin_sector=rng.random(len(epoch)),
402+
rgfo_esa_step=rng.random(len(epoch)),
403+
nso_spin_sector=rng.random(len(epoch)),
404+
nso_esa_step=rng.random(len(epoch)),
400405
)
401406

402407
np.testing.assert_array_equal(direct_event.event_index, np.arange(len(event_num)))
@@ -440,6 +445,11 @@ def test_codice_lo_l1a_sw_priority_read_from_instrument_team_cdf(self):
440445
np.testing.assert_array_equal(actual_l1a_sw_priority_rates.p2_heplusplus, cdf["p2_heplusplus"][...])
441446
np.testing.assert_array_equal(actual_l1a_sw_priority_rates.p3_heavies, cdf["p3_heavies"][...])
442447
np.testing.assert_array_equal(actual_l1a_sw_priority_rates.p4_dcrs, cdf["p4_dcrs"][...])
448+
np.testing.assert_array_equal(actual_l1a_sw_priority_rates.half_spin_per_esa_step, cdf["half_spin_per_esa_step"][...])
449+
np.testing.assert_array_equal(actual_l1a_sw_priority_rates.rgfo_spin_sector, cdf["rgfo_spin_sector"][...])
450+
np.testing.assert_array_equal(actual_l1a_sw_priority_rates.rgfo_esa_step, cdf["rgfo_esa_step"][...])
451+
np.testing.assert_array_equal(actual_l1a_sw_priority_rates.nso_spin_sector, cdf["nso_spin_sector"][...])
452+
np.testing.assert_array_equal(actual_l1a_sw_priority_rates.nso_esa_step, cdf["nso_esa_step"][...])
443453

444454
def test_codice_lo_l1a_nsw_priority_read_from_instrument_team_cdf(self):
445455
instrument_team_cdf_path = get_test_instrument_team_data_path(
@@ -523,7 +533,7 @@ def test_codice_lo_3d_distributions_data_product(self):
523533
self.assert_variable_attributes(next(actual_variables), elevation.astype(str), ELEVATION_ANGLE_LABEL_VAR_NAME)
524534

525535
def test_codice_lo_l2_direct_events_reads_from_correct_float_data(self):
526-
all_fill_l2_cdf_path = get_test_data_path('codice/imap_codice_l2_lo-direct-events-all-fill_20250814_v001.cdf')
536+
all_fill_l2_cdf_path = get_test_data_path('codice/imap_codice_l2_lo-direct-events_20260307_v003-all-fill.cdf')
527537

528538
rng = np.random.default_rng()
529539
with tempfile.TemporaryDirectory() as tmpdir:
@@ -550,20 +560,20 @@ def test_codice_lo_l2_direct_events_reads_from_correct_float_data(self):
550560
np.testing.assert_array_equal(l2_direct_event.elevation_angle, cdf["elevation_angle"][:, :7, ...])
551561

552562
def test_codice_lo_l2_direct_events_read_from_cdf_handles_fill_value(self):
553-
all_fill_l2_cdf_path = get_test_data_path('codice/imap_codice_l2_lo-direct-events-all-fill_20250814_v001.cdf')
563+
all_fill_l2_cdf_path = get_test_data_path('codice/imap_codice_l2_lo-direct-events_20260307_v003-all-fill.cdf')
554564
l2_direct_event = CodiceLoL2DirectEventData.read_from_cdf(all_fill_l2_cdf_path)
555565

556566
with CDF(str(all_fill_l2_cdf_path)) as cdf:
557567
np.testing.assert_array_equal(cdf["epoch"], l2_direct_event.epoch)
558568
np.testing.assert_array_equal(cdf["epoch_delta_plus"], l2_direct_event.epoch_delta_plus)
559569
np.testing.assert_array_equal(cdf["epoch_delta_minus"], l2_direct_event.epoch_delta_minus)
560570

561-
np.testing.assert_array_equal(l2_direct_event.apd_energy, np.full((9, 7, 10000), np.nan))
562-
np.testing.assert_array_equal(l2_direct_event.energy_step, np.full((9, 7, 10000), np.nan))
563-
np.testing.assert_array_equal(l2_direct_event.energy_per_charge, np.full((9, 7, 10000), np.nan))
564-
np.testing.assert_array_equal(l2_direct_event.spin_angle, np.full((9, 7, 10000), np.nan))
565-
np.testing.assert_array_equal(l2_direct_event.spin_sector, np.full((9, 7, 10000), np.nan))
566-
np.testing.assert_array_equal(l2_direct_event.elevation_angle, np.full((9, 7, 10000), np.nan))
571+
np.testing.assert_array_equal(l2_direct_event.apd_energy, np.full((2, 7, 10000), np.nan))
572+
np.testing.assert_array_equal(l2_direct_event.energy_step, np.full((2, 7, 10000), np.nan))
573+
np.testing.assert_array_equal(l2_direct_event.energy_per_charge, np.full((2, 7, 10000), np.nan))
574+
np.testing.assert_array_equal(l2_direct_event.spin_angle, np.full((2, 7, 10000), np.nan))
575+
np.testing.assert_array_equal(l2_direct_event.spin_sector, np.full((2, 7, 10000), np.nan))
576+
np.testing.assert_array_equal(l2_direct_event.elevation_angle, np.full((2, 7, 10000), np.nan))
567577

568578
self.assertIsInstance(l2_direct_event.position, np.ma.masked_array)
569579
np.testing.assert_array_equal(l2_direct_event.position.data, cdf["position"][:, :7, ...])
@@ -594,60 +604,37 @@ def test_codice_lo_l2_direct_events_read_from_cdf_handles_fill_value(self):
594604
self.assertTrue(np.all(l2_direct_event.tof.mask))
595605

596606
def test_codice_lo_l1a_sw_priority_read_from_cdf_handles_fill_value(self):
597-
l1a_sw_all_fill_path = get_test_data_path("codice/imap_codice_l1a_lo-sw-priority-all-fill_20250814_v001.cdf")
607+
l1a_sw_all_fill_path = get_test_data_path("codice/imap_codice_l1a_lo-sw-priority_20260307_v003-all-fill.cdf")
598608
l1a_sw = CodiceLoL1aSWPriorityRates.read_from_cdf(l1a_sw_all_fill_path)
599609

600610
with CDF(str(l1a_sw_all_fill_path)) as cdf:
601611
np.testing.assert_array_equal(l1a_sw.spin_period, np.full_like(cdf['spin_period'], np.nan))
602612

603-
self.assertIsInstance(l1a_sw.nso_half_spin, np.ma.masked_array)
604-
np.testing.assert_array_equal(l1a_sw.nso_half_spin,
605-
np.full_like(l1a_sw.nso_half_spin.data, cdf['nso_half_spin']))
606-
self.assertTrue(np.all(l1a_sw.nso_half_spin.mask))
607-
608-
self.assertIsInstance(l1a_sw.rgfo_half_spin, np.ma.masked_array)
609-
np.testing.assert_array_equal(l1a_sw.rgfo_half_spin,
610-
np.full_like(l1a_sw.rgfo_half_spin.data, cdf['rgfo_half_spin']))
611-
self.assertTrue(np.all(l1a_sw.rgfo_half_spin.mask))
612-
613-
self.assertIsInstance(l1a_sw.data_quality, np.ma.masked_array)
614-
np.testing.assert_array_equal(l1a_sw.data_quality,
615-
np.full_like(l1a_sw.data_quality.data, cdf['data_quality']))
616-
self.assertTrue(np.all(l1a_sw.data_quality.mask))
617-
618-
self.assertIsInstance(l1a_sw.p0_tcrs, np.ma.masked_array)
619-
np.testing.assert_array_equal(l1a_sw.p0_tcrs, np.full_like(l1a_sw.p0_tcrs.data, cdf['p0_tcrs']))
620-
self.assertTrue(np.all(l1a_sw.p0_tcrs.mask))
621-
622-
self.assertIsInstance(l1a_sw.p1_hplus, np.ma.masked_array)
623-
np.testing.assert_array_equal(l1a_sw.p1_hplus, np.full_like(l1a_sw.p1_hplus.data, cdf['p1_hplus']))
624-
self.assertTrue(np.all(l1a_sw.p1_hplus.mask))
625-
626-
self.assertIsInstance(l1a_sw.p2_heplusplus, np.ma.masked_array)
627-
np.testing.assert_array_equal(l1a_sw.p2_heplusplus,
628-
np.full_like(l1a_sw.p2_heplusplus.data, cdf['p2_heplusplus']))
629-
self.assertTrue(np.all(l1a_sw.p2_heplusplus.mask))
630-
631-
self.assertIsInstance(l1a_sw.p3_heavies, np.ma.masked_array)
632-
np.testing.assert_array_equal(l1a_sw.p3_heavies, np.full_like(l1a_sw.p3_heavies.data, cdf['p3_heavies']))
633-
self.assertTrue(np.all(l1a_sw.p3_heavies.mask))
634-
635-
self.assertIsInstance(l1a_sw.p4_dcrs, np.ma.masked_array)
636-
np.testing.assert_array_equal(l1a_sw.p4_dcrs, np.full_like(l1a_sw.p4_dcrs.data, cdf['p4_dcrs']))
637-
self.assertTrue(np.all(l1a_sw.p4_dcrs.mask))
638-
639-
self.assertIsInstance(l1a_sw.st_bias_gain_mode, np.ma.masked_array)
640-
np.testing.assert_array_equal(l1a_sw.st_bias_gain_mode,
641-
np.full_like(l1a_sw.st_bias_gain_mode.data, cdf['st_bias_gain_mode']))
642-
self.assertTrue(np.all(l1a_sw.st_bias_gain_mode.mask))
643-
644-
self.assertIsInstance(l1a_sw.sw_bias_gain_mode, np.ma.masked_array)
645-
np.testing.assert_array_equal(l1a_sw.sw_bias_gain_mode,
646-
np.full_like(l1a_sw.sw_bias_gain_mode.data, cdf['sw_bias_gain_mode']))
647-
self.assertTrue(np.all(l1a_sw.sw_bias_gain_mode.mask))
613+
integer_variable_names = [
614+
"nso_half_spin",
615+
"rgfo_half_spin",
616+
"data_quality",
617+
"p0_tcrs",
618+
"p1_hplus",
619+
"p2_heplusplus",
620+
"p3_heavies",
621+
"p4_dcrs",
622+
"st_bias_gain_mode",
623+
"sw_bias_gain_mode",
624+
"half_spin_per_esa_step",
625+
"rgfo_spin_sector",
626+
"rgfo_esa_step",
627+
"nso_spin_sector",
628+
"nso_esa_step",
629+
]
630+
631+
for var in integer_variable_names:
632+
var_data = getattr(l1a_sw, var)
633+
self.assertIsInstance(var_data, np.ma.masked_array)
634+
np.testing.assert_equal(var_data.mask, np.full_like(cdf[var], True))
648635

649636
def test_codice_lo_l1a_nsw_priority_read_from_cdf_handles_fill_value(self):
650-
l1a_nsw_all_fill_path = get_test_data_path("codice/imap_codice_l1a_lo-nsw-priority-all-fill_20250814_v001.cdf")
637+
l1a_nsw_all_fill_path = get_test_data_path("codice/imap_codice_l1a_lo-nsw-priority_20260307_v003-all-fill.cdf")
651638

652639
l1a_nsw_model = CodiceLoL1aNSWPriorityRates.read_from_cdf(l1a_nsw_all_fill_path)
653640

tests/periodically_run_tests.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"test_l3bcde_first_time_processing": "2026-03-05T09:59:10.392514",
3-
"test_spectral_fit_against_validation_data": "2026-03-10T16:24:34.100416",
3+
"test_spectral_fit_against_validation_data": "2026-03-16T11:10:15.284967",
44
"test_calculate_pickup_ions_with_minimize": "2026-03-16T08:50:14.379057",
5-
"test_ultra_all_sp_maps": "2026-03-10T16:26:39.948984",
5+
"test_ultra_all_sp_maps": "2026-03-16T11:12:44.353802",
66
"test_ultra_combined_maps": null,
77
"test_ultra_combined_nsp_maps": "2026-03-13T13:14:24.831481",
88
"test_ultra_combined_sp_maps": "2026-03-13T13:14:37.889683",
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)