diff --git a/imap_processing/cdf/config/imap_codice_l2-hi-direct-events_variable_attrs.yaml b/imap_processing/cdf/config/imap_codice_l2-hi-direct-events_variable_attrs.yaml index e7e281457..93604c66d 100644 --- a/imap_processing/cdf/config/imap_codice_l2-hi-direct-events_variable_attrs.yaml +++ b/imap_processing/cdf/config/imap_codice_l2-hi-direct-events_variable_attrs.yaml @@ -67,10 +67,10 @@ num_events: DEPEND_0: epoch DEPEND_1: priority DICT_KEY: SPASE>Support>SupportQuantity:Other + DISPLAY_TYPE: time_series FIELDNAM: Number of Events FILLVAL: *uint16_fillval FORMAT: I5 - LABLAXIS: Number of Events LABL_PTR_1: priority_label SCALETYP: linear UNITS: " " @@ -83,10 +83,10 @@ data_quality: DEPEND_0: epoch DEPEND_1: priority DICT_KEY: SPASE>Support>SupportQuantity:DataQuality + DISPLAY_TYPE: no_plot FIELDNAM: Data Quality FILLVAL: *uint8_fillval FORMAT: I3 - LABLAXIS: Data Quality LABL_PTR_1: priority_label SCALETYP: linear UNITS: " " @@ -100,10 +100,10 @@ energy_step: DEPEND_1: priority DEPEND_2: event_num DICT_KEY: SPASE>Support>SupportQuantity:Other + DISPLAY_TYPE: no_plot FIELDNAM: Energy Step FILLVAL: *uint8_fillval FORMAT: I3 - LABLAXIS: Energy Step LABL_PTR_1: priority_label LABL_PTR_2: event_num_label SCALETYP: linear @@ -118,10 +118,10 @@ energy_per_charge: DEPEND_1: priority DEPEND_2: event_num DICT_KEY: SPASE>Particle>ParticleType:Ion,ParticleQuantity:EnergyPerCharge + DISPLAY_TYPE: spectrogram FIELDNAM: Energy per Charge FILLVAL: *real_fillval FORMAT: F12.4 - LABLAXIS: Energy per Charge LABL_PTR_1: priority_label LABL_PTR_2: event_num_label SCALETYP: linear @@ -136,10 +136,10 @@ gain: DEPEND_1: priority DEPEND_2: event_num DICT_KEY: SPASE>Support>SupportQuantity:Other + DISPLAY_TYPE: no_plot FIELDNAM: Gain FILLVAL: *uint8_fillval FORMAT: I1 - LABLAXIS: Gain LABL_PTR_1: priority_label LABL_PTR_2: event_num_label SCALETYP: linear @@ -154,10 +154,10 @@ multi_flag: DEPEND_1: priority DEPEND_2: event_num DICT_KEY: SPASE>Support>SupportQuantity:DataQuality + DISPLAY_TYPE: no_plot FIELDNAM: Multi Flag FILLVAL: *uint8_fillval FORMAT: I1 - LABLAXIS: Multi Flag LABL_PTR_1: priority_label LABL_PTR_2: event_num_label SCALETYP: linear @@ -172,10 +172,10 @@ type: DEPEND_1: priority DEPEND_2: event_num DICT_KEY: SPASE>Support>SupportQuantity:Other + DISPLAY_TYPE: no_plot FIELDNAM: Type FILLVAL: *uint8_fillval FORMAT: I1 - LABLAXIS: Type LABL_PTR_1: priority_label LABL_PTR_2: event_num_label SCALETYP: linear @@ -190,10 +190,10 @@ tof: DEPEND_1: priority DEPEND_2: event_num DICT_KEY: SPASE>Particle>ParticleType:Ion,ParticleQuantity:TimeOfFlight + DISPLAY_TYPE: spectrogram FIELDNAM: Time of Flight FILLVAL: *real_fillval FORMAT: F12.4 - LABLAXIS: TOF LABL_PTR_1: priority_label LABL_PTR_2: event_num_label SCALETYP: linear @@ -226,6 +226,7 @@ spin_angle: DEPEND_1: priority DEPEND_2: event_num DICT_KEY: SPASE>Particle>ParticleType:Ion,ParticleQuantity:ArrivalDirection,Qualifier:DirectionAngle.AzimuthAngle + DISPLAY_TYPE: spectrogram FIELDNAM: Spin Angle FILLVAL: *real_fillval FORMAT: F8.2 @@ -243,10 +244,10 @@ elevation_angle: DEPEND_1: priority DEPEND_2: event_num DICT_KEY: SPASE>Particle>ParticleType:Ion,ParticleQuantity:ArrivalDirection,Qualifier:DirectionAngle.ElevationAngle + DISPLAY_TYPE: spectrogram FIELDNAM: Elevation Angle FILLVAL: *real_fillval FORMAT: F8.2 - LABLAXIS: Elevation Angle LABL_PTR_1: priority_label LABL_PTR_2: event_num_label SCALETYP: linear @@ -261,10 +262,10 @@ ssd_energy: DEPEND_1: priority DEPEND_2: event_num DICT_KEY: SPASE>Particle>ParticleType:Ion,ParticleQuantity:Energy + DISPLAY_TYPE: spectrogram FIELDNAM: SSD Energy FILLVAL: *real_fillval FORMAT: F12.4 - LABLAXIS: SSD Energy LABL_PTR_1: priority_label LABL_PTR_2: event_num_label SCALETYP: linear @@ -276,13 +277,13 @@ ssd_energy: energy_per_nuc: CATDESC: Energy per Nucleon DEPEND_0: epoch - DEPEND_1: priority_label + DEPEND_1: priority DEPEND_2: event_num DICT_KEY: SPASE>Support>SupportQuantity:Other + DISPLAY_TYPE: spectrogram FIELDNAM: Energy per Nucleon FILLVAL: *real_fillval FORMAT: F12.4 - LABLAXIS: Energy per Nucleon LABL_PTR_1: priority_label LABL_PTR_2: event_num_label SCALETYP: linear @@ -297,10 +298,10 @@ ssd_id: DEPEND_1: priority DEPEND_2: event_num DICT_KEY: SPASE>Support>SupportQuantity:Other + DISPLAY_TYPE: no_plot FIELDNAM: SSD ID FILLVAL: *uint8_fillval FORMAT: I2 - LABLAXIS: SSD ID LABL_PTR_1: priority_label LABL_PTR_2: event_num_label SCALETYP: linear diff --git a/imap_processing/cdf/config/imap_codice_l2-lo-direct-events_variable_attrs.yaml b/imap_processing/cdf/config/imap_codice_l2-lo-direct-events_variable_attrs.yaml index 35ffbdf76..f2ffb4ea3 100644 --- a/imap_processing/cdf/config/imap_codice_l2-lo-direct-events_variable_attrs.yaml +++ b/imap_processing/cdf/config/imap_codice_l2-lo-direct-events_variable_attrs.yaml @@ -65,10 +65,10 @@ num_events: DEPEND_0: epoch DEPEND_1: priority DICT_KEY: SPASE>Support>SupportQuantity:Other + DISPLAY_TYPE: time_series FIELDNAM: Number of Events FILLVAL: *uint16_fillval FORMAT: I5 - LABLAXIS: Number of Events LABL_PTR_1: priority_label SCALETYP: linear UNITS: " " @@ -81,6 +81,7 @@ data_quality: DEPEND_0: epoch DEPEND_1: priority DICT_KEY: SPASE>Support>SupportQuantity:DataQuality + DISPLAY_TYPE: no_plot FIELDNAM: Data Quality FILLVAL: *uint8_fillval FORMAT: I3 @@ -97,10 +98,10 @@ energy_step: DEPEND_1: priority DEPEND_2: event_num DICT_KEY: SPASE>Support>SupportQuantity:Other + DISPLAY_TYPE: no_plot FIELDNAM: Energy Step FILLVAL: *uint8_fillval FORMAT: I3 - LABLAXIS: Energy Step LABL_PTR_1: priority_label LABL_PTR_2: event_num_label SCALETYP: linear @@ -115,10 +116,10 @@ energy_per_charge: DEPEND_1: priority DEPEND_2: event_num DICT_KEY: SPASE>Particle>ParticleType:Ion,ParticleQuantity:EnergyPerCharge + DISPLAY_TYPE: spectrogram FIELDNAM: Energy per Charge FILLVAL: *real_fillval FORMAT: F12.4 - LABLAXIS: Energy per Charge LABL_PTR_1: priority_label LABL_PTR_2: event_num_label SCALETYP: linear @@ -133,10 +134,10 @@ apd_energy: DEPEND_1: priority DEPEND_2: event_num DICT_KEY: SPASE>Particle>ParticleType:Ion,ParticleQuantity:Energy + DISPLAY_TYPE: spectrogram FIELDNAM: APD Energy FILLVAL: *real_fillval FORMAT: F12.4 - LABLAXIS: APD Energy LABL_PTR_1: priority_label LABL_PTR_2: event_num_label SCALETYP: linear @@ -151,10 +152,10 @@ gain: DEPEND_1: priority DEPEND_2: event_num DICT_KEY: SPASE>Support>SupportQuantity:Other + DISPLAY_TYPE: no_plot FIELDNAM: Gain FILLVAL: *uint8_fillval FORMAT: I1 - LABLAXIS: Gain LABL_PTR_1: priority_label LABL_PTR_2: event_num_label SCALETYP: linear @@ -169,10 +170,10 @@ apd_id: DEPEND_1: priority DEPEND_2: event_num DICT_KEY: SPASE>Support>SupportQuantity:Other + DISPLAY_TYPE: no_plot FIELDNAM: APD ID FILLVAL: *uint8_fillval FORMAT: I2 - LABLAXIS: APD ID LABL_PTR_1: priority_label LABL_PTR_2: event_num_label SCALETYP: linear @@ -187,10 +188,10 @@ position: DEPEND_1: priority DEPEND_2: event_num DICT_KEY: SPASE>Support>SupportQuantity:Other + DISPLAY_TYPE: no_plot FIELDNAM: Position FILLVAL: *uint8_fillval FORMAT: I2 - LABLAXIS: Position LABL_PTR_1: priority_label LABL_PTR_2: event_num_label SCALETYP: linear @@ -205,10 +206,10 @@ multi_flag: DEPEND_1: priority DEPEND_2: event_num DICT_KEY: SPASE>Support>SupportQuantity:DataQuality + DISPLAY_TYPE: no_plot FIELDNAM: Multi Flag FILLVAL: *uint8_fillval FORMAT: I1 - LABLAXIS: Multi Flag LABL_PTR_1: priority_label LABL_PTR_2: event_num_label SCALETYP: linear @@ -223,10 +224,10 @@ type: DEPEND_1: priority DEPEND_2: event_num DICT_KEY: SPASE>Support>SupportQuantity:Other + DISPLAY_TYPE: no_plot FIELDNAM: Type FILLVAL: *uint8_fillval FORMAT: I1 - LABLAXIS: Type LABL_PTR_1: priority_label LABL_PTR_2: event_num_label SCALETYP: linear @@ -241,10 +242,10 @@ tof: DEPEND_1: priority DEPEND_2: event_num DICT_KEY: SPASE>Particle>ParticleType:Ion,ParticleQuantity:TimeOfFlight + DISPLAY_TYPE: spectrogram FIELDNAM: Time of Flight FILLVAL: *real_fillval FORMAT: F12.4 - LABLAXIS: TOF LABL_PTR_1: priority_label LABL_PTR_2: event_num_label SCALETYP: linear @@ -277,6 +278,7 @@ spin_angle: DEPEND_1: priority DEPEND_2: event_num DICT_KEY: SPASE>Particle>ParticleType:Ion,ParticleQuantity:ArrivalDirection,Qualifier:DirectionAngle.AzimuthAngle + DISPLAY_TYPE: spectrogram FIELDNAM: Spin Angle FILLVAL: *real_fillval FORMAT: F8.2 @@ -294,10 +296,10 @@ elevation_angle: DEPEND_1: priority DEPEND_2: event_num DICT_KEY: SPASE>Particle>ParticleType:Ion,ParticleQuantity:ArrivalDirection,Qualifier:DirectionAngle.ElevationAngle + DISPLAY_TYPE: spectrogram FIELDNAM: Elevation Angle FILLVAL: *real_fillval FORMAT: F8.2 - LABLAXIS: Elevation Angle LABL_PTR_1: priority_label LABL_PTR_2: event_num_label SCALETYP: linear diff --git a/imap_processing/codice/codice_l2.py b/imap_processing/codice/codice_l2.py index 656727807..650613ab1 100644 --- a/imap_processing/codice/codice_l2.py +++ b/imap_processing/codice/codice_l2.py @@ -1332,7 +1332,7 @@ def process_lo_direct_events(dependencies: ProcessingInputCollection) -> xr.Data # skip adding attributes for these variables. They should already # have attrs carried over from l1a. continue - l2_dataset[var].attrs.update(cdf_attrs.get_variable_attributes(var)) + l2_dataset[var].attrs = cdf_attrs.get_variable_attributes(var) # Update coord attributes l2_dataset["priority"].attrs.update( cdf_attrs.get_variable_attributes("priority", check_schema=False) @@ -1461,7 +1461,7 @@ def process_hi_direct_events(dependencies: ProcessingInputCollection) -> xr.Data cdf_attrs.get_global_attributes("imap_codice_l2_hi-direct-events") ) for var in l2_dataset.data_vars: - l2_dataset[var].attrs.update(cdf_attrs.get_variable_attributes(var)) + l2_dataset[var].attrs = cdf_attrs.get_variable_attributes(var) # Update coord attributes l2_dataset["priority"].attrs.update( cdf_attrs.get_variable_attributes("priority", check_schema=False) diff --git a/imap_processing/tests/codice/test_codice_l2.py b/imap_processing/tests/codice/test_codice_l2.py index ae738e00b..b4f9791ab 100644 --- a/imap_processing/tests/codice/test_codice_l2.py +++ b/imap_processing/tests/codice/test_codice_l2.py @@ -3,6 +3,7 @@ from unittest import mock from unittest.mock import MagicMock, patch +import cdflib import numpy as np import pandas as pd import pytest @@ -43,6 +44,50 @@ "imap_codice_l2_lo-direct-events", ] +DIRECT_EVENT_LUTS = { + "lo-direct-events": [ + "l2-lo-onboard-energy-table", + "l2-lo-onboard-energy-bins", + "l2-lo-onboard-mpq-cal", + "l2-lo-onboard-mpq-cal", + ], + "hi-direct-events": [ + "l2-hi-energy-table", + "l2-hi-tof-table", + ], +} + +DIRECT_EVENT_DISPLAY_TYPES = { + "lo-direct-events": { + "num_events": "time_series", + "data_quality": "no_plot", + "gain": "no_plot", + "multi_flag": "no_plot", + "spin_angle": "spectrogram", + "elevation_angle": "spectrogram", + "tof": "spectrogram", + "type": "no_plot", + "apd_energy": "spectrogram", + "apd_id": "no_plot", + "energy_per_charge": "spectrogram", + "energy_step": "no_plot", + "position": "no_plot", + }, + "hi-direct-events": { + "num_events": "time_series", + "data_quality": "no_plot", + "gain": "no_plot", + "multi_flag": "no_plot", + "spin_angle": "spectrogram", + "elevation_angle": "spectrogram", + "tof": "spectrogram", + "type": "no_plot", + "ssd_energy": "spectrogram", + "energy_per_nuc": "spectrogram", + "ssd_id": "no_plot", + }, +} + @pytest.fixture def processing_dependencies(codice_lut_path): @@ -80,6 +125,27 @@ def mock_cdf_attrs(): return cdf_attrs +def _generate_direct_events_l2_file(mock_get_file_paths, codice_lut_path, descriptor): + """Generate a fresh CoDICE direct-events L2 CDF for metadata assertions.""" + mock_get_file_paths.side_effect = [ + codice_lut_path(descriptor=descriptor, data_type="l0") + ] + l1a_cdf = process_l1a(ProcessingInputCollection())[0] + processed_l1a_file = write_cdf(l1a_cdf) + file_path = processed_l1a_file.as_posix() + mock_get_file_paths.side_effect = [ + [file_path], + [file_path], + *[ + codice_lut_path(descriptor=lut_descriptor) + for lut_descriptor in DIRECT_EVENT_LUTS[descriptor] + ], + ] + processed_l2_ds = process_codice_l2(descriptor, ProcessingInputCollection()) + processed_l2_ds.attrs["Data_version"] = "001" + return write_cdf(processed_l2_ds) + + @pytest.fixture def mock_half_spin_per_esa_step(): """ @@ -720,3 +786,29 @@ def test_codice_l2_hi_de(mock_get_file_paths, codice_lut_path): errors = CDFValidator().validate(file) assert not errors load_cdf(file) + + +@pytest.mark.parametrize("descriptor", ["lo-direct-events", "hi-direct-events"]) +@patch("imap_data_access.processing_input.ProcessingInputCollection.get_file_paths") +def test_codice_l2_direct_events_display_type_cdf_metadata( + mock_get_file_paths, codice_lut_path, descriptor +): + file = _generate_direct_events_l2_file( + mock_get_file_paths, codice_lut_path, descriptor + ) + cdf_file = cdflib.CDF(str(file)) + + for variable, expected_display_type in DIRECT_EVENT_DISPLAY_TYPES[ + descriptor + ].items(): + attrs = cdf_file.varattsget(variable) + assert "DISPLAY_TYPE" in attrs, f"{variable} is missing DISPLAY_TYPE" + assert isinstance(attrs["DISPLAY_TYPE"], str), ( + f"{variable} DISPLAY_TYPE must be stored as a string" + ) + assert attrs["DISPLAY_TYPE"] == expected_display_type + + if descriptor == "hi-direct-events": + attrs = cdf_file.varattsget("energy_per_nuc") + assert attrs["DEPEND_1"] == "priority" + assert attrs["LABL_PTR_1"] == "priority_label"