Skip to content

Commit e2c145b

Browse files
author
Menlo Innovations - CAVA Project
committed
Harrison 3029 - PMCL/SWYN - CoDICE Lo direct events: Calculate normalization_per_event and add WIP metadata for it
1 parent 0b3344a commit e2c145b

5 files changed

Lines changed: 110 additions & 15 deletions

File tree

imap_l3_processing/cdf/config/imap_codice_l3a_lo-direct-events_variable_attrs.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,27 @@ normalization:
162162
LABLAXIS: Normalization
163163
UNITS: " "
164164
SCALETYP: linear
165+
VALIDMIN: 0
166+
VALIDMAX: 1000
165167
LABL_PTR_2: energy_bin_label
166168
LABL_PTR_3: spin_angle_bin_label
169+
VARIABLE_PURPOSE: ""
170+
171+
normalization_per_event:
172+
<<: *per_event
173+
NAME: normalization_per_event
174+
VAR_TYPE: data
175+
CATDESC: Normalization per event
176+
FIELDNAM: Normalization factor
177+
LABLAXIS: Normalization
178+
UNITS: " "
179+
SCALETYP: linear
180+
VALIDMIN: 0
181+
VALIDMAX: 1000
182+
SCALEMIN: 0
183+
SCALEMAX: 10
184+
VARIABLE_PURPOSE: ""
185+
DATA_TYPE: CDF_FLOAT
167186

168187
mass_per_charge:
169188
<<: *per_event

imap_l3_processing/codice/l3/lo/codice_lo_processor.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
calculate_mass, calculate_mass_per_charge, \
1919
rebin_to_counts_by_species_elevation_and_spin_sector, normalize_counts, \
2020
combine_priorities_and_convert_to_rate, rebin_3d_distribution_azimuth_to_elevation, convert_count_rate_to_intensity, \
21-
calculate_normalization_factor
21+
calculate_normalization_factor, lookup_normalization_per_event
2222
from imap_l3_processing.data_utils import safe_divide
2323
from imap_l3_processing.models import InputMetadata
2424
from imap_l3_processing.processor import Processor
@@ -192,11 +192,14 @@ def process_l3a_direct_event_data_product(self, dependencies: CodiceLoL3aDirectE
192192
codice_direct_events.tof)
193193
mass = calculate_mass(codice_direct_events.apd_energy, codice_direct_events.tof, mass_coefficient_lookup)
194194
stacked_priorities = np.stack(priority_counts, axis=1)
195-
normalization = calculate_normalization_factor(
196-
stacked_priorities,
195+
normalization = calculate_normalization_factor(stacked_priorities, codice_direct_events.num_events,
196+
codice_direct_events.energy_step,
197+
codice_direct_events.spin_sector)
198+
normalization_per_event = lookup_normalization_per_event(
199+
normalization,
197200
codice_direct_events.num_events,
198-
codice_direct_events.spin_sector,
199201
codice_direct_events.energy_step,
202+
codice_direct_events.spin_sector
200203
)
201204

202205
return CodiceLoL3aDirectEventDataProduct(
@@ -227,7 +230,7 @@ def process_l3a_direct_event_data_product(self, dependencies: CodiceLoL3aDirectE
227230
rgfo_esa_step=codice_sw_priority_counts_l1a_data.rgfo_esa_step,
228231
nso_spin_sector=codice_sw_priority_counts_l1a_data.nso_spin_sector,
229232
nso_esa_step=codice_sw_priority_counts_l1a_data.nso_esa_step,
230-
normalization_per_event=1,
233+
normalization_per_event=normalization_per_event,
231234
spin_sector=codice_direct_events.spin_sector,
232235
)
233236

imap_l3_processing/codice/l3/lo/science/codice_lo_calculations.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@ def rebin_direct_events_by_energy_and_spin_sector(num_events: np.ndarray, spin_s
8383
np.add.at(rebinned_output[time_index, priority_index], (energy_indices, spin_sectors), 1)
8484
return rebinned_output
8585

86-
def calculate_normalization_factor(priority_counts: np.ndarray, num_events: np.ndarray, spin_sectors: np.ndarray, energy_steps: np.ndarray) -> np.ndarray:
86+
def calculate_normalization_factor(priority_counts: np.ndarray, num_events: np.ndarray, energy_steps: np.ndarray,
87+
spin_sectors: np.ndarray) -> np.ndarray:
8788
numerator = np.concatenate((priority_counts, priority_counts), axis=3)
8889
num_energies = priority_counts.shape[2]
8990
num_spin_sectors = 2 * priority_counts.shape[3]
@@ -98,6 +99,16 @@ def calculate_normalization_factor(priority_counts: np.ndarray, num_events: np.n
9899
output[(denominator==0)&(numerator!=0)] = np.nan
99100
return output
100101

102+
def lookup_normalization_per_event(normalization: np.ndarray, num_events: np.ndarray, energy_steps: np.ndarray, spin_sectors: np.ndarray) -> np.ndarray:
103+
results = np.full(spin_sectors.shape, np.nan)
104+
for (epoch, priority), count in np.ma.ndenumerate(num_events, compressed=True):
105+
energy_step = energy_steps[epoch, priority, :count]
106+
spin_sector = spin_sectors[epoch, priority, :count]
107+
108+
assert 0 == np.ma.count_masked(energy_step) == np.ma.count_masked(spin_sector) , "Expected all events to have an energy_step and spin_sector!"
109+
results[epoch, priority, :count] = normalization[epoch, priority, energy_step, spin_sector]
110+
return results
111+
101112
def rebin_to_counts_by_species_elevation_and_spin_sector(num_events: np.ndarray, mass: np.ndarray,
102113
mass_per_charge: np.ndarray,
103114
energy: np.ndarray,

tests/codice/l3/lo/science/test_codice_lo_calculations.py

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
rebin_to_counts_by_species_elevation_and_spin_sector, rebin_direct_events_by_energy_and_spin_sector, \
1616
CODICE_LO_NUM_AZIMUTH_BINS, normalize_counts, combine_priorities_and_convert_to_rate, \
1717
rebin_3d_distribution_azimuth_to_elevation, convert_count_rate_to_intensity, rebin_direct_events_for_normalization, \
18-
calculate_normalization_factor
18+
calculate_normalization_factor, lookup_normalization_per_event
1919
from imap_l3_processing.constants import ONE_SECOND_IN_MICROSECONDS
2020

2121

@@ -336,7 +336,7 @@ def test_calculate_normalization_factor(self):
336336
spin_sectors[0, 0, :3] = [0, 2, 12]
337337
energy_steps[0, 0, :3] = [0, 0, 0]
338338

339-
result = calculate_normalization_factor(priority_counts, num_events, spin_sectors, energy_steps)
339+
result = calculate_normalization_factor(priority_counts, num_events, energy_steps, spin_sectors)
340340

341341
expected_shape = (num_epochs, num_priorities, num_esa_steps, num_l2_spin_sectors)
342342
expected = np.full(expected_shape, 0.0)
@@ -367,7 +367,7 @@ def test_calculate_normalization_factor_floor_is_one(self):
367367
spin_sectors[0, 0, :3] = [0, 12, 2]
368368
energy_steps[0, 0, :3] = [0, 0, 0]
369369

370-
result = calculate_normalization_factor(priority_counts, num_events, spin_sectors, energy_steps)
370+
result = calculate_normalization_factor(priority_counts, num_events, energy_steps, spin_sectors)
371371

372372
expected_shape = (num_epochs, num_priorities, num_esa_steps, num_l2_spin_sectors)
373373
expected = np.full(expected_shape, 0.0)
@@ -395,7 +395,7 @@ def test_normalization_factor_is_fill_when_priority_nonzero_and_direct_zero(self
395395
spin_sectors = np.zeros((num_epochs, num_priorities, event_buffer_len), dtype=int)
396396
energy_steps = np.zeros((num_epochs, num_priorities, event_buffer_len), dtype=int)
397397

398-
result = calculate_normalization_factor(priority_counts, num_events, spin_sectors, energy_steps)
398+
result = calculate_normalization_factor(priority_counts, num_events, energy_steps, spin_sectors)
399399

400400
expected_shape = (num_epochs, num_priorities, num_esa_steps, num_l2_spin_sectors)
401401
expected = np.full(expected_shape, 0.0)
@@ -407,6 +407,59 @@ def test_normalization_factor_is_fill_when_priority_nonzero_and_direct_zero(self
407407
expected[0, 0, 0, (0, 2, 12, 14)] = np.nan
408408
np.testing.assert_equal(result, expected)
409409

410+
def test_lookup_normalization_per_event(self):
411+
num_epochs = 2
412+
num_priorities = 7
413+
num_esa_steps = 128
414+
num_l1_spin_sectors = 12
415+
num_l2_spin_sectors = 2*num_l1_spin_sectors
416+
417+
normalization = np.zeros((num_epochs, num_priorities, num_esa_steps, num_l2_spin_sectors))
418+
normalization[0,0,0,0] = 123.
419+
normalization[0,0,0,12] = 456.
420+
normalization[0,0,1,2] = 789.
421+
422+
event_buffer_len = 15
423+
num_events = np.zeros((num_epochs, num_priorities), dtype=int)
424+
spin_sectors = np.ma.array(np.full((num_epochs, num_priorities, event_buffer_len), 255, dtype=int), mask=True)
425+
energy_steps = np.ma.array(np.full((num_epochs, num_priorities, event_buffer_len), 255, dtype=int), mask=True)
426+
427+
num_events[0, 0] = 3
428+
spin_sectors[0, 0, :3] = [0, 12, 2]
429+
energy_steps[0, 0, :3] = [0, 0, 1]
430+
431+
normalization_per_event = lookup_normalization_per_event(normalization, num_events, energy_steps, spin_sectors)
432+
433+
np.testing.assert_array_equal(normalization_per_event[0, 0, :3], [123, 456, 789])
434+
435+
expected_normalization_per_event = np.full((num_epochs, num_priorities, event_buffer_len), np.nan)
436+
expected_normalization_per_event[0, 0, :3] = [123, 456, 789]
437+
438+
np.testing.assert_array_equal(normalization_per_event, expected_normalization_per_event)
439+
440+
441+
def test_lookup_normalization_per_event_skips_masked_num_events(self):
442+
num_epochs = 2
443+
num_priorities = 7
444+
num_esa_steps = 128
445+
num_l1_spin_sectors = 12
446+
num_l2_spin_sectors = 2*num_l1_spin_sectors
447+
448+
normalization = np.zeros((num_epochs, num_priorities, num_esa_steps, num_l2_spin_sectors))
449+
normalization[0,0,0,0] = 123.
450+
normalization[0,0,0,12] = 456.
451+
normalization[0,0,1,2] = 789.
452+
453+
event_buffer_len = 15
454+
num_events = np.ma.array(np.full((num_epochs, num_priorities),65535), mask=True)
455+
spin_sectors = np.ma.array(np.full((num_epochs, num_priorities, event_buffer_len), 255, dtype=int), mask=True)
456+
energy_steps = np.ma.array(np.full((num_epochs, num_priorities, event_buffer_len), 255, dtype=int), mask=True)
457+
458+
normalization_per_event = lookup_normalization_per_event(normalization, num_events, energy_steps, spin_sectors)
459+
460+
expected_normalization_per_event = np.full((num_epochs, num_priorities, event_buffer_len), np.nan)
461+
np.testing.assert_array_equal(normalization_per_event, expected_normalization_per_event)
462+
410463
def test_normalize_counts(self):
411464
num_epochs = 2
412465
num_priorities = 3

tests/codice/l3/lo/test_codice_lo_processor.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -499,11 +499,13 @@ def test_process_l3a_partial_densities(self, mock_calculate_partial_densities):
499499
self.assertEqual(fe_hiq_partial_density, result_data.fe_hiq_partial_density),
500500

501501
@patch('imap_l3_processing.codice.l3.lo.codice_lo_processor.SpinAngleLookup')
502-
@patch('imap_l3_processing.codice.l3.lo.codice_lo_processor.calculate_normalization_factor')
503-
@patch('imap_l3_processing.codice.l3.lo.codice_lo_processor.calculate_mass_per_charge')
504-
@patch('imap_l3_processing.codice.l3.lo.codice_lo_processor.calculate_mass')
502+
@patch('imap_l3_processing.codice.l3.lo.codice_lo_processor.lookup_normalization_per_event', autospec=True)
503+
@patch('imap_l3_processing.codice.l3.lo.codice_lo_processor.calculate_normalization_factor', autospec=True)
504+
@patch('imap_l3_processing.codice.l3.lo.codice_lo_processor.calculate_mass_per_charge', autospec=True)
505+
@patch('imap_l3_processing.codice.l3.lo.codice_lo_processor.calculate_mass', autospec=True)
505506
def test_process_l3a_direct_events(self, mock_calculate_mass, mock_calculate_mass_per_charge,
506507
mock_calculate_normalization_factor,
508+
mock_lookup_normalization_per_event,
507509
mock_spin_angle_lookup_class):
508510
rng = np.random.default_rng()
509511

@@ -607,9 +609,14 @@ def test_process_l3a_direct_events(self, mock_calculate_mass, mock_calculate_mas
607609
np.testing.assert_equal(actual_stacked_priorities[:, 6], nsw_priority_rates.p6_hplus_heplusplus)
608610

609611
np.testing.assert_equal(mock_calculate_normalization_factor.call_args.args[1], expected_num_events)
610-
np.testing.assert_equal(mock_calculate_normalization_factor.call_args.args[2], expected_spin_sector)
611-
np.testing.assert_equal(mock_calculate_normalization_factor.call_args.args[3], expected_energy_step)
612+
np.testing.assert_equal(mock_calculate_normalization_factor.call_args.args[2], expected_energy_step)
613+
np.testing.assert_equal(mock_calculate_normalization_factor.call_args.args[3], expected_spin_sector)
612614

615+
self.assertEqual(1, mock_lookup_normalization_per_event.call_count)
616+
np.testing.assert_equal(mock_lookup_normalization_per_event.call_args.args[0], expected_normalization)
617+
np.testing.assert_equal(mock_lookup_normalization_per_event.call_args.args[1], expected_num_events)
618+
np.testing.assert_equal(mock_lookup_normalization_per_event.call_args.args[2], expected_energy_step)
619+
np.testing.assert_equal(mock_lookup_normalization_per_event.call_args.args[3], expected_spin_sector)
613620

614621
self.assertIsInstance(l3a_direct_event_data_product, CodiceLoL3aDirectEventDataProduct)
615622
self.assertEqual(input_metadata, l3a_direct_event_data_product.input_metadata)
@@ -624,6 +631,8 @@ def test_process_l3a_direct_events(self, mock_calculate_mass, mock_calculate_mas
624631

625632
np.testing.assert_array_equal(l3a_direct_event_data_product.normalization,
626633
np.flip(expected_normalization, axis=2))
634+
self.assertEqual(l3a_direct_event_data_product.normalization_per_event, mock_lookup_normalization_per_event.return_value)
635+
627636

628637
np.testing.assert_array_equal(expected_spin_angle, l3a_direct_event_data_product.spin_angle)
629638
np.testing.assert_array_equal(expected_spin_sector, l3a_direct_event_data_product.spin_sector)

0 commit comments

Comments
 (0)