Skip to content

Commit 7bd2a87

Browse files
author
Menlo Innovations - CAVA Project
committed
Harrison 3029 - PMCL - CoDICE Lo direct events: Finish new normalization factor formula
1 parent 7e14923 commit 7bd2a87

4 files changed

Lines changed: 82 additions & 61 deletions

File tree

imap_l3_processing/codice/l3/lo/codice_lo_processor.py

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@
1616
CodiceLoL3ChargeStateDistributionsDataProduct, CodiceLoL3a3dDistributionDataProduct
1717
from imap_l3_processing.codice.l3.lo.science.codice_lo_calculations import calculate_partial_densities, \
1818
calculate_mass, calculate_mass_per_charge, \
19-
rebin_counts_by_energy_and_spin_angle, rebin_to_counts_by_species_elevation_and_spin_sector, normalize_counts, \
20-
combine_priorities_and_convert_to_rate, rebin_3d_distribution_azimuth_to_elevation, convert_count_rate_to_intensity
19+
rebin_to_counts_by_species_elevation_and_spin_sector, normalize_counts, \
20+
combine_priorities_and_convert_to_rate, rebin_3d_distribution_azimuth_to_elevation, convert_count_rate_to_intensity, \
21+
rebin_direct_events_for_normalization
2122
from imap_l3_processing.data_utils import safe_divide
2223
from imap_l3_processing.models import InputMetadata
2324
from imap_l3_processing.processor import Processor
@@ -171,8 +172,10 @@ def process_l3a_direct_event_data_product(self, dependencies: CodiceLoL3aDirectE
171172
esa_energy_per_charge_lookup = dependencies.energy_lookup
172173

173174
event_buffer = codice_direct_events.tof[0].shape[-1]
175+
num_energies = codice_sw_priority_counts_l1a_data.p0_tcrs.shape[1]
176+
num_spin_sectors = 2*codice_sw_priority_counts_l1a_data.p0_tcrs.shape[2]
174177
mass_coefficient_lookup = dependencies.mass_coefficient_lookup
175-
priority_counts_for_events = [
178+
priority_counts = [
176179
codice_sw_priority_counts_l1a_data.p0_tcrs,
177180
codice_sw_priority_counts_l1a_data.p1_hplus,
178181
codice_sw_priority_counts_l1a_data.p2_heplusplus,
@@ -181,28 +184,30 @@ def process_l3a_direct_event_data_product(self, dependencies: CodiceLoL3aDirectE
181184
codice_nsw_priority_counts_l1a_data.p5_heavies,
182185
codice_nsw_priority_counts_l1a_data.p6_hplus_heplusplus
183186
]
187+
num_priorities = len(priority_counts)
184188

185-
mass_per_charge = np.full((len(codice_direct_events.epoch), len(priority_counts_for_events), event_buffer),
189+
mass_per_charge = np.full((len(codice_direct_events.epoch), num_priorities, event_buffer),
186190
np.nan)
187-
mass = np.full((len(codice_direct_events.epoch), len(priority_counts_for_events), event_buffer), np.nan)
191+
mass = np.full((len(codice_direct_events.epoch), num_priorities, event_buffer), np.nan)
188192

189193
spin_angle_lut = SpinAngleLookup()
190-
normalization = np.full((len(codice_direct_events.epoch), len(priority_counts_for_events),
191-
esa_energy_per_charge_lookup.num_bins, spin_angle_lut.num_bins), np.nan)
194+
normalization = np.full((len(codice_direct_events.epoch), num_priorities,
195+
num_energies, num_spin_sectors), np.nan)
192196

193197
try:
194198
mass_per_charge = calculate_mass_per_charge(codice_direct_events.energy_per_charge,
195199
codice_direct_events.tof)
196200
mass = calculate_mass(codice_direct_events.apd_energy, codice_direct_events.tof, mass_coefficient_lookup)
197201

198-
direct_events_binned_by_energy_and_spin = rebin_counts_by_energy_and_spin_angle(
202+
direct_events_binned_by_energy_and_spin = rebin_direct_events_for_normalization(
199203
codice_direct_events.num_events,
200-
codice_direct_events.spin_angle,
204+
codice_direct_events.spin_sector,
201205
codice_direct_events.energy_step,
202-
spin_angle_lut,
203-
esa_energy_per_charge_lookup)
204-
total_events_per_priority = np.sum(np.stack(priority_counts_for_events, axis=1), axis=(2, 3), keepdims=True)
205-
normalization = total_events_per_priority / direct_events_binned_by_energy_and_spin
206+
num_spin_sectors,
207+
num_energies)
208+
stacked_priorities = np.stack(priority_counts, axis=1)
209+
numerator = np.concatenate((stacked_priorities, stacked_priorities), axis=3)
210+
normalization = numerator / direct_events_binned_by_energy_and_spin
206211
except Exception as e:
207212
print(e)
208213

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

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -53,31 +53,34 @@ def calculate_mass_per_charge(energy_per_charge: np.ndarray, tof: np.ndarray) ->
5353
tof ** 2) * CONVERSION_CONSTANT_K
5454

5555

56-
def rebin_counts_by_energy_and_spin_angle(num_events: np.ndarray, spin_angle: np.ndarray, energy_step: np.ndarray,
57-
spin_angle_lookup: SpinAngleLookup,
58-
energy_lookup: EnergyLookup) -> np.ndarray:
56+
def rebin_direct_events_for_normalization(num_events: np.ndarray, spin_angle: np.ndarray, energy_step: np.ndarray,
57+
num_spin_sectors: int,
58+
num_energies: int) -> np.ndarray:
59+
base_counts = rebin_direct_events_by_energy_and_spin_sector(num_events, spin_angle, energy_step, num_spin_sectors, num_energies)
60+
half_spin = num_spin_sectors//2
61+
result = np.zeros_like(base_counts)
62+
result[:,:,:,0:half_spin] = base_counts[:,:,:,0:half_spin] + base_counts[:,:,:,half_spin:num_spin_sectors]
63+
result[:,:,:,half_spin:num_spin_sectors] = result[:,:,:,0:half_spin]
64+
return result
65+
66+
def rebin_direct_events_by_energy_and_spin_sector(num_events: np.ndarray, spin_sector: np.ndarray, energy_step: np.ndarray,
67+
num_spin_sectors: int,
68+
num_energies: int) -> np.ndarray:
5969
num_epochs = num_events.shape[0]
6070
num_priorities = num_events.shape[1]
61-
num_energies = energy_lookup.num_bins
62-
num_spin_bins = spin_angle_lookup.num_bins
6371

64-
rebinned_output = np.zeros((num_epochs, num_priorities, num_energies, num_spin_bins))
72+
rebinned_output = np.zeros((num_epochs, num_priorities, num_energies, num_spin_sectors))
6573

6674
for time_index in range(num_epochs):
6775
for priority_index in range(num_priorities):
6876
events_at_index = num_events[time_index, priority_index]
6977
if events_at_index is np.ma.masked:
7078
continue
7179

72-
spin_angle_in_degrees = spin_angle[time_index, priority_index, :events_at_index]
73-
energy_in_keV = energy_step[time_index, priority_index, :events_at_index]
80+
spin_sectors = spin_sector[time_index, priority_index, :events_at_index]
81+
energy_indices = energy_step[time_index, priority_index, :events_at_index]
7482

75-
spin_angle_indices = spin_angle_lookup.get_spin_angle_index(spin_angle_in_degrees)
76-
energy_indices = energy_lookup.get_energy_index(energy_in_keV)
77-
spin_angle_indices_first_half = spin_angle_indices % (num_spin_bins//2)
78-
79-
np.add.at(rebinned_output[time_index, priority_index], (energy_indices, spin_angle_indices_first_half), 1)
80-
np.add.at(rebinned_output[time_index, priority_index], (energy_indices, spin_angle_indices_first_half+(num_spin_bins//2)), 1)
83+
np.add.at(rebinned_output[time_index, priority_index], (energy_indices, spin_sectors), 1)
8184
return rebinned_output
8285

8386

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

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212
from imap_l3_processing.codice.l3.lo.models import EnergyAndSpinAngle, CodiceLo3dData
1313
from imap_l3_processing.codice.l3.lo.science.codice_lo_calculations import calculate_partial_densities, \
1414
calculate_total_number_of_events, calculate_normalization_ratio, calculate_mass, calculate_mass_per_charge, \
15-
rebin_to_counts_by_species_elevation_and_spin_sector, rebin_counts_by_energy_and_spin_angle, \
15+
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, \
17-
rebin_3d_distribution_azimuth_to_elevation, convert_count_rate_to_intensity
17+
rebin_3d_distribution_azimuth_to_elevation, convert_count_rate_to_intensity, rebin_direct_events_for_normalization
1818
from imap_l3_processing.constants import ONE_SECOND_IN_MICROSECONDS
1919

2020

@@ -262,33 +262,48 @@ def mock_get_species_index(species):
262262
expected_o_counts[0, 1, 0, 0, 0] = 1
263263
np.testing.assert_array_equal(actual_counts_3d_data.get_3d_distribution("O+5"), expected_o_counts)
264264

265-
def test_rebin_counts_to_by_energy_and_spin_angle(self):
266-
mock_energy_lookup = Mock(spec=EnergyLookup)
265+
def test_rebin_direct_events_by_energy_and_spin_sector(self):
267266
num_energy_bins = 30
268-
mock_energy_lookup.num_bins = num_energy_bins
269-
mock_energy_lookup.get_energy_index.side_effect = [
270-
np.array([7, 7]),
271-
np.array([1, 3, 1, 4])
272-
]
267+
num_spin_angle_bins = 20
268+
num_priorities = 3
269+
num_epochs = 1
270+
event_buffer_len = 15
271+
num_events = np.ma.masked_array(np.array([[1, 2, 4]]), mask=[[False, True, False]])
272+
spin_sector = np.zeros((num_epochs, num_priorities, event_buffer_len), dtype=np.uint8)
273+
spin_sector[0,0,:1] = [3]
274+
spin_sector[0,2,:4] = [5, 6, 5, 7]
273275

274-
mock_spin_angle_lookup = Mock(spec=SpinAngleLookup)
275-
num_spin_angle_bins = 24
276-
mock_spin_angle_lookup.num_bins = num_spin_angle_bins
277-
mock_spin_angle_lookup.get_spin_angle_index.side_effect = [
278-
np.array([3, 3+12]),
279-
np.array([5, 6, 5, 7+12])
280-
]
276+
energy_step = np.zeros((num_epochs, num_priorities, event_buffer_len), dtype=np.uint8)
277+
energy_step[0,0,:1] = [7]
278+
energy_step[0,2,:4] = [1, 3, 1, 4]
281279

282-
rng = np.random.default_rng()
280+
result = rebin_direct_events_by_energy_and_spin_sector(num_events, spin_sector, energy_step, num_spin_angle_bins, num_energy_bins)
281+
282+
expected_rebinned_counts = np.zeros((num_epochs, num_priorities, num_energy_bins, num_spin_angle_bins))
283+
expected_rebinned_counts[0, 0, 7, 3] = 1
284+
expected_rebinned_counts[0, 2, 1, 5] = 2
285+
expected_rebinned_counts[0, 2, 3, 6] = 1
286+
expected_rebinned_counts[0, 2, 4, 7] = 1
287+
288+
np.testing.assert_array_equal(result, expected_rebinned_counts)
289+
290+
def test_rebin_direct_events_for_normalization(self):
291+
num_energy_bins = 30
292+
num_spin_sectors = 24
283293
num_priorities = 3
284-
num_events = np.ma.masked_array(np.array([[2, 2, 4]]), mask=[[False, True, False]])
285294
num_epochs = 1
286-
spin_angle = rng.random((num_epochs, num_priorities, 15))
287-
energy_step = rng.random((num_epochs, num_priorities, 15))
288-
result = rebin_counts_by_energy_and_spin_angle(num_events, spin_angle, energy_step, mock_spin_angle_lookup,
289-
mock_energy_lookup)
295+
event_buffer_len = 15
296+
num_events = np.ma.masked_array(np.array([[2, 2, 4]]), mask=[[False, True, False]])
297+
spin_sector = np.zeros((num_epochs, num_priorities, event_buffer_len), dtype=np.uint8)
298+
spin_sector[0,0,:2] = [3, 3+12]
299+
spin_sector[0,2,:4] = [5, 6, 5, 7+12]
290300

291-
expected_rebinned_counts = np.zeros((num_epochs, num_priorities, num_energy_bins, num_spin_angle_bins))
301+
energy_step = np.zeros((num_epochs, num_priorities, event_buffer_len), dtype=np.uint8)
302+
energy_step[0,0,:2] = [7, 7]
303+
energy_step[0,2,:4] = [1, 3, 1, 4]
304+
result = rebin_direct_events_for_normalization(num_events, spin_sector, energy_step, num_spin_sectors, num_energy_bins)
305+
306+
expected_rebinned_counts = np.zeros((num_epochs, num_priorities, num_energy_bins, num_spin_sectors))
292307
expected_rebinned_counts[0, 0, 7, 3] = 2
293308
expected_rebinned_counts[0, 2, 1, 5] = 2
294309
expected_rebinned_counts[0, 2, 3, 6] = 1

tests/codice/l3/lo/test_codice_lo_processor.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -499,11 +499,11 @@ 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.rebin_counts_by_energy_and_spin_angle')
502+
@patch('imap_l3_processing.codice.l3.lo.codice_lo_processor.rebin_direct_events_for_normalization')
503503
@patch('imap_l3_processing.codice.l3.lo.codice_lo_processor.calculate_mass_per_charge')
504504
@patch('imap_l3_processing.codice.l3.lo.codice_lo_processor.calculate_mass')
505505
def test_process_l3a_direct_events(self, mock_calculate_mass, mock_calculate_mass_per_charge,
506-
mock_rebin_counts_by_energy_and_spin,
506+
mock_rebin_direct_events_for_normalization,
507507
mock_spin_angle_lookup_class):
508508
rng = np.random.default_rng()
509509

@@ -539,7 +539,7 @@ def test_process_l3a_direct_events(self, mock_calculate_mass, mock_calculate_mas
539539

540540
counts_rebinned_by_energy_and_spin = rng.random(
541541
(len(epochs), CODICE_LO_L2_NUM_PRIORITIES, num_energy_bins, num_spin_angle_bins))
542-
mock_rebin_counts_by_energy_and_spin.return_value = np.array(counts_rebinned_by_energy_and_spin)
542+
mock_rebin_direct_events_for_normalization.return_value = counts_rebinned_by_energy_and_spin
543543

544544
mass_per_charge = rng.random((len(epochs), CODICE_LO_L2_NUM_PRIORITIES, event_buffer_size))
545545
mock_calculate_mass_per_charge.return_value = mass_per_charge
@@ -586,8 +586,6 @@ def test_process_l3a_direct_events(self, mock_calculate_mass, mock_calculate_mas
586586
processor = CodiceLoProcessor(dependencies=input_collection, input_metadata=input_metadata)
587587
l3a_direct_event_data_product = processor.process_l3a_direct_event_data_product(dependencies)
588588

589-
mock_spin_angle_lookup_class.assert_called_once()
590-
591589
self.assertEqual(1, mock_calculate_mass.call_count)
592590
np.testing.assert_equal(mock_calculate_mass.call_args.args[0], expected_apd_energy)
593591
np.testing.assert_equal(mock_calculate_mass.call_args.args[1], expected_tof)
@@ -597,12 +595,12 @@ def test_process_l3a_direct_events(self, mock_calculate_mass, mock_calculate_mas
597595
np.testing.assert_equal(mock_calculate_mass_per_charge.call_args.args[0], expected_energy_per_charge)
598596
np.testing.assert_equal(mock_calculate_mass_per_charge.call_args.args[1], expected_tof)
599597

600-
self.assertEqual(1, mock_rebin_counts_by_energy_and_spin.call_count)
601-
np.testing.assert_equal(mock_rebin_counts_by_energy_and_spin.call_args.args[0], expected_num_events)
602-
np.testing.assert_equal(mock_rebin_counts_by_energy_and_spin.call_args.args[1], expected_spin_angle)
603-
np.testing.assert_equal(mock_rebin_counts_by_energy_and_spin.call_args.args[2], expected_energy_step)
604-
np.testing.assert_equal(mock_rebin_counts_by_energy_and_spin.call_args.args[3], mock_spin_angle_lookup)
605-
np.testing.assert_equal(mock_rebin_counts_by_energy_and_spin.call_args.args[4], dependencies.energy_lookup)
598+
self.assertEqual(1, mock_rebin_direct_events_for_normalization.call_count)
599+
np.testing.assert_equal(mock_rebin_direct_events_for_normalization.call_args.args[0], expected_num_events)
600+
np.testing.assert_equal(mock_rebin_direct_events_for_normalization.call_args.args[1], expected_spin_sector)
601+
np.testing.assert_equal(mock_rebin_direct_events_for_normalization.call_args.args[2], expected_energy_step)
602+
np.testing.assert_equal(mock_rebin_direct_events_for_normalization.call_args.args[3], num_spin_angle_bins)
603+
np.testing.assert_equal(mock_rebin_direct_events_for_normalization.call_args.args[4], num_energy_bins)
606604

607605
self.assertIsInstance(l3a_direct_event_data_product, CodiceLoL3aDirectEventDataProduct)
608606
self.assertEqual(input_metadata, l3a_direct_event_data_product.input_metadata)

0 commit comments

Comments
 (0)