diff --git a/imap_processing/cdf/config/imap_ialirt_global_cdf_attrs.yaml b/imap_processing/cdf/config/imap_ialirt_global_cdf_attrs.yaml index 0b300fa93d..bab66ac8a7 100644 --- a/imap_processing/cdf/config/imap_ialirt_global_cdf_attrs.yaml +++ b/imap_processing/cdf/config/imap_ialirt_global_cdf_attrs.yaml @@ -9,10 +9,10 @@ instrument_base: &instrument_base HIT (High-energy Ion Telescope), MAG (Magnetometer), and SWAPI (Solar Wind and Pickup Ion). See https://imap.princeton.edu for more details. - Instrument_type: "Multiple Instruments" + Instrument_type: Particles (space), Magnetic Fields (space), Plasma and Solar Wind (space), Ephemeris/Attitude/Ancillary imap_ialirt_l1_realtime: <<: *instrument_base Data_type: L1_REALTIME>Level-1 Realtime Logical_source: imap_ialirt_l1_realtime - Logical_source_description: IMAP-IALiRT Instrument Level-1 Realtime Data. + Logical_source_description: IMAP Active Link for Real-Time (I-ALiRT) Level-1 Data. diff --git a/imap_processing/cdf/config/imap_ialirt_l1_variable_attrs.yaml b/imap_processing/cdf/config/imap_ialirt_l1_variable_attrs.yaml index 5340694276..bfa9083a81 100644 --- a/imap_processing/cdf/config/imap_ialirt_l1_variable_attrs.yaml +++ b/imap_processing/cdf/config/imap_ialirt_l1_variable_attrs.yaml @@ -1,6 +1,62 @@ +# Epochs +instrument_epoch: &instrument_epoch + CATDESC: Time, number of nanoseconds since J2000 with leap seconds included + FIELDNAM: epoch + LABLAXIS: epoch + FILLVAL: -9223372036854775808 + FORMAT: I20 + VALIDMIN: 315576066184000000 # 2010-01-01T00:00:00 mission start (APL 0 epoch) + VALIDMAX: 3155716869184000000 # 2100-01-01T00:00:00 mission end + UNITS: ns + VAR_TYPE: support_data + SCALETYP: linear + MONOTON: INCREASE + TIME_BASE: J2000 + TIME_SCALE: Terrestrial Time + REFERENCE_POSITION: Rotating Earth Geoid + RESOLUTION: ' ' + DICT_KEY: "SPASE>Support>SupportQuantity:Temporal" + dtype: int64 + +codice_hi_epoch: + <<: *instrument_epoch + CATDESC: Time CoDICE-Hi instrument took the measurement, number of nanoseconds since J2000 with leap seconds included + FIELDNAM: CoDICE-Hi epoch + +codice_lo_epoch: + <<: *instrument_epoch + CATDESC: Time CoDICE-Lo instrument took the measurement, number of nanoseconds since J2000 with leap seconds included + FIELDNAM: CoDICE-Lo epoch + +hit_epoch: + <<: *instrument_epoch + CATDESC: Time HIT instrument took the measurement, number of nanoseconds since J2000 with leap seconds included + FIELDNAM: HIT epoch + +mag_epoch: + <<: *instrument_epoch + CATDESC: Time MAG instrument took the measurement, number of nanoseconds since J2000 with leap seconds included + FIELDNAM: MAG epoch + +swapi_epoch: + <<: *instrument_epoch + CATDESC: Time SWAPI instrument took the measurement, number of nanoseconds since J2000 with leap seconds included + FIELDNAM: SWAPI epoch + +swe_epoch: + <<: *instrument_epoch + CATDESC: Time SWE instrument took the measurement, number of nanoseconds since J2000 with leap seconds included + FIELDNAM: SWE epoch + +ephemeris_epoch: + <<: *instrument_epoch + CATDESC: Time of ephemeris measurement, number of nanoseconds since J2000 with leap seconds included + FIELDNAM: Ephemeris epoch + +# Defaults default_attrs: &default # Assumed values for all variable attrs unless overwritten - DEPEND_0: epoch + DEPEND_0: ephemeris_epoch DISPLAY_TYPE: time_series FILLVAL: -9223372036854775808 FORMAT: I12 @@ -9,62 +65,6 @@ default_attrs: &default VAR_TYPE: data UNITS: " " -component: - CATDESC: Component direction (x, y, z) - FIELDNAM: component - LABLAXIS: component - FORMAT: A3 - VAR_TYPE: metadata - UNITS: " " - -RTN_component: - CATDESC: Component direction (radial, tangential, normal) - FIELDNAM: RTN_component - LABLAXIS: RTN_component - FORMAT: A3 - VAR_TYPE: metadata - UNITS: " " - -esa_step: - CATDESC: ESA step number. The nominal central energies for the 8 i-ALiRT channels are 100.4, 140, 194, 270, 376, 523, 727, and 1011 eV. - FIELDNAM: esa_step - LABLAXIS: esa_step - FORMAT: I2 - VALIDMIN: 0 - VALIDMAX: 7 - VAR_TYPE: metadata - UNITS: " " - -codice_hi_h_energy_ranges: - CATDESC: CoDICE-Hi energy ranges - FIELDNAM: codice_hi_h_energy_ranges - LABLAXIS: codice_hi_h_energy_ranges - FORMAT: I3 - VALIDMIN: 0 - VALIDMAX: 14 - VAR_TYPE: metadata - UNITS: " " - -codice_hi_h_elevation: - CATDESC: CoDICE-Hi elevation - FIELDNAM: codice_hi_h_elevation - LABLAXIS: codice_hi_h_elevation - FORMAT: I3 - VALIDMIN: 0 - VALIDMAX: 4 - VAR_TYPE: metadata - UNITS: " " - -codice_hi_h_spin_angle: - CATDESC: CoDICE-Hi spin_angle - FIELDNAM: codice_hi_h_spin_angle - LABLAXIS: codice_hi_h_spin_angle - FORMAT: I3 - VALIDMIN: 0 - VALIDMAX: 4 - VAR_TYPE: metadata - UNITS: " " - default_uint8_attrs: &default_uint8 <<: *default FILLVAL: 255 @@ -121,276 +121,552 @@ default_float64_attrs: &default_float64 VALIDMAX: 1.7976931348623157e+308 dtype: float64 +# Labels +B_GSM_labels: + CATDESC: B GSM component labels + FIELDNAM: B GSM component labels + FORMAT: A8 + VAR_TYPE: metadata + UNITS: " " + +B_GSE_labels: + CATDESC: B GSE component labels + FIELDNAM: B GSE component labels + FORMAT: A8 + VAR_TYPE: metadata + UNITS: " " + +B_RTN_labels: + CATDESC: B RTN component labels B radial (RTN), B tangential (RTN), B normal (RTN) + FIELDNAM: B RTN component labels + FORMAT: A18 + VAR_TYPE: metadata + UNITS: " " + +sc_GSM_position_labels: + CATDESC: Spacecraft position labels in GSM coordinates + FIELDNAM: Spacecraft GSM position labels + FORMAT: A8 + VAR_TYPE: metadata + UNITS: " " + +sc_GSM_velocity_labels: + CATDESC: Spacecraft velocity labels in GSM coordinates + FIELDNAM: Spacecraft GSM velocity labels + FORMAT: A8 + VAR_TYPE: metadata + UNITS: " " + +sc_GSE_position_labels: + CATDESC: Spacecraft position labels in GSE coordinates + FIELDNAM: Spacecraft GSE position labels + FORMAT: A8 + VAR_TYPE: metadata + UNITS: " " + +sc_GSE_velocity_labels: + CATDESC: Spacecraft velocity labels in GSE coordinates + FIELDNAM: Spacecraft GSE velocity labels + FORMAT: A8 + VAR_TYPE: metadata + UNITS: " " + +codice_hi_energy_center: + CATDESC: CoDICE-Hi proton energy bin (MeV) + FIELDNAM: CoDICE-Hi proton energy bin + VAR_TYPE: support_data + UNITS: "MeV" + VALIDMAX: 20.0 + VALIDMIN: 0.0 + DELTA_PLUS_VAR: codice_hi_energy_plus + DELTA_MINUS_VAR: codice_hi_energy_minus + FILLVAL: -1.0e31 + FORMAT: F12.6 + dtype: float32 + +codice_hi_energy_minus: + CATDESC: CoDICE-Hi proton energy bin lower delta (MeV) + FIELDNAM: CoDICE-Hi proton energy bin lower delta + VAR_TYPE: support_data + UNITS: "MeV" + VALIDMAX: 20.0 + VALIDMIN: 0.0 + FILLVAL: -1.0e31 + FORMAT: F12.6 + dtype: float32 + +codice_hi_energy_plus: + CATDESC: CoDICE-Hi proton energy bin upper delta (MeV) + FIELDNAM: CoDICE-Hi proton energy bin upper delta + VAR_TYPE: support_data + UNITS: "MeV" + VALIDMAX: 20.0 + VALIDMIN: 0.0 + FILLVAL: -1.0e31 + FORMAT: F12.6 + dtype: float32 + +codice_hi_elevation: + CATDESC: Elevation Angle + FIELDNAM: Elevation Angle + LABLAXIS: Elevation Angle + SCALETYP: linear + UNITS: degrees + VALIDMIN: 0.0 + VALIDMAX: 180.0 + VAR_TYPE: support_data + FILLVAL: -1.0e31 + FORMAT: F12.6 + dtype: float32 + +codice_hi_elevation_labels: + CATDESC: CoDICE-Hi elevation labels + FIELDNAM: CoDICE-Hi elevation labels + FORMAT: A12 + VAR_TYPE: metadata + UNITS: " " + +codice_hi_spin_sector: + CATDESC: CoDICE-Hi spin sector index + FIELDNAM: CoDICE-Hi spin sector index + FORMAT: I2 + VAR_TYPE: support_data + UNITS: " " + VALIDMIN: 0 + VALIDMAX: 3 + dtype: int32 + +codice_hi_spin_sector_labels: + CATDESC: CoDICE-Hi spin sector labels + FIELDNAM: CoDICE-Hi spin sector labels + FORMAT: A8 + VAR_TYPE: metadata + UNITS: " " + +codice_hi_spin_angle: + <<: *default_float32 + VAR_TYPE: support_data + CATDESC: Spin Angle + FIELDNAM: Spin Angle + SCALETYP: linear + UNITS: degrees + VALIDMAX: 360.0 + VALIDMIN: 0.0 + DEPEND_0: codice_hi_spin_sector + DEPEND_1: codice_hi_elevation + LABL_PTR_1: codice_hi_spin_sector_labels + LABL_PTR_2: codice_hi_elevation_labels + +swe_electron_energy: + CATDESC: SWE electron channels (nominal central energies) + FIELDNAM: SWE electron channels + LABLAXIS: SWE electron channels + VAR_TYPE: support_data + UNITS: "eV" + VALIDMIN: 0.0 + VALIDMAX: 2000.0 + FILLVAL: -1.0e31 + FORMAT: F12.6 + dtype: float32 + +# Variables codice_hi_h: <<: *default_float32 - CATDESC: H intensities in 15 energy ranges and binned into 4 azimuths and 4 spin angle bins - FIELDNAM: H intensities - LABLAXIS: Diff. Intensity - UNITS: "# / cm2-sr-s- MeV" + CATDESC: Proton intensity in 4 epochs, 15 energy ranges, 4 spin sectors, and 4 elevations + FIELDNAM: H+ intensity + LABLAXIS: H+ intensity + DEPEND_0: codice_hi_epoch + UNITS: "counts / cm2-sr-s-MeV" VALIDMIN: 0 VALIDMAX: 100000000.0 - DEPEND_1: codice_hi_h_energy_ranges - DEPEND_2: codice_hi_h_elevation - DEPEND_3: codice_hi_h_spin_angle + DEPEND_1: codice_hi_energy_center + DEPEND_2: codice_hi_spin_sector + DEPEND_3: codice_hi_elevation + DISPLAY_TYPE: no_plot + FORMAT: F16.6 + VAR_NOTES: Energy ranges (MeV) are 0.0200 to 0.0283, 0.0283 to 0.0400, 0.0400 to 0.0566, 0.0566 to 0.0800, 0.0800 to 0.113, 0.113 to 0.160, 0.160 to 0.226, 0.226 to 0.320, 0.320 to 0.453, 0.453 to 0.640, 0.640 to 0.905, 0.905 to 1.28, 1.28 to 1.81, 1.81 to 2.56, 2.56 to 3.62. codice_lo_c_over_o_abundance: <<: *default_float32 CATDESC: C/O abundance ratio - FIELDNAM: C/O - LABLAXIS: ratio + FIELDNAM: C/O abundance ratio + LABLAXIS: C/O ratio + DEPEND_0: codice_lo_epoch UNITS: " " VALIDMIN: 0 VALIDMAX: 100000000.0 + FORMAT: F16.6 codice_lo_mg_over_o_abundance: <<: *default_float32 CATDESC: Mg/O abundance ratio - FIELDNAM: Mg/O - LABLAXIS: ratio + FIELDNAM: Mg/O abundance ratio + LABLAXIS: Mg/O ratio + DEPEND_0: codice_lo_epoch UNITS: " " VALIDMIN: 0 VALIDMAX: 100000000.0 + FORMAT: F16.6 codice_lo_fe_over_o_abundance: <<: *default_float32 CATDESC: Fe/O abundance ratio - FIELDNAM: Fe/O - LABLAXIS: ratio + FIELDNAM: Fe/O abundance ratio + LABLAXIS: Fe/O ratio + DEPEND_0: codice_lo_epoch UNITS: " " VALIDMIN: 0 VALIDMAX: 100000000.0 + FORMAT: F16.6 -codice_lo_c_plus_6_over_c_plus_5_ratio: +codice_lo_c_plus_6_over_c_plus_5: <<: *default_float32 CATDESC: C+6/C+5 charge state ratio - FIELDNAM: C+6/C+5 - LABLAXIS: ratio + FIELDNAM: C+6/C+5 charge state ratio + LABLAXIS: C+6/C+5 ratio + DEPEND_0: codice_lo_epoch UNITS: " " VALIDMIN: 0 VALIDMAX: 100000000.0 + FORMAT: F16.6 -codice_lo_o_plus_7_over_o_plus_6_ratio: +codice_lo_o_plus_7_over_o_plus_6: <<: *default_float32 CATDESC: O+7/O+6 charge state ratio - FIELDNAM: O+7/O+6 - LABLAXIS: ratio + FIELDNAM: O7+/O6+ charge state ratio + LABLAXIS: O7+/O6+ ratio + DEPEND_0: codice_lo_epoch UNITS: " " VALIDMIN: 0 VALIDMAX: 100000000.0 + FORMAT: F16.6 -codice_lo_fe_low_over_fe_high_ratio: +codice_lo_fe_low_over_fe_high: <<: *default_float32 - CATDESC: Fe low/Fe high charge state ratio, low charge state = 6-12, high charge state = 13-17 - FIELDNAM: Fe low/Fe high - LABLAXIS: ratio + CATDESC: Fe low/high charge state ratio (low=6 to 12, high=13 to 17) + FIELDNAM: Fe low/high charge state ratio + LABLAXIS: Fe low/high ratio + DEPEND_0: codice_lo_epoch UNITS: " " VALIDMIN: 0 VALIDMAX: 100000000.0 + FORMAT: F16.6 hit_e_a_side_low_en: <<: *default_uint32 - CATDESC: Low energy (~300 keV) electrons (A-side) - FIELDNAM: Low energy (~300 keV) electrons (A-side) - LABLAXIS: hit_e_a_side_low_en + CATDESC: Low energy (>0.5 MeV) electron count rate (A-side, anti-sunward) + FIELDNAM: Low energy electron count rate (A-side) + LABLAXIS: A-side e- >0.5 MeV + DEPEND_0: hit_epoch UNITS: counts per second + VALIDMIN: 0 + VALIDMAX: 1000000000 + VAR_NOTES: A-side indicates the HIT aperture that is nominally looking in the anti-sunward direction. B-side indicates the aperture looking nominally in the sunward direction. hit_e_a_side_med_en: <<: *default_uint32 - CATDESC: Medium energy (~3 MeV) electrons (A-side) - FIELDNAM: Medium energy (~3 MeV) electrons (A-side) - LABLAXIS: hit_e_a_side_med_en + CATDESC: Medium energy (<1 MeV) electron count rate (A-side, anti-sunward) + FIELDNAM: Medium energy electron count rate (A-side) + LABLAXIS: A-side e- <1 MeV + DEPEND_0: hit_epoch UNITS: counts per second + VALIDMIN: 0 + VALIDMAX: 1000000000 + VAR_NOTES: A-side indicates the HIT aperture that is nominally looking in the anti-sunward direction. B-side indicates the aperture looking nominally in the sunward direction. hit_e_a_side_high_en: <<: *default_uint32 - CATDESC: High energy (>3 MeV) electrons (A-side) - FIELDNAM: High energy (>3 MeV) electrons (A-side) - LABLAXIS: hit_e_a_side_high_en + CATDESC: High energy (>3 MeV) electron count rate (A-side, anti-sunward) + FIELDNAM: High energy electron count rate (A-side) + LABLAXIS: A-side e- >3 MeV + DEPEND_0: hit_epoch UNITS: counts per second + VALIDMIN: 0 + VALIDMAX: 1000000000 + VAR_NOTES: A-side indicates the HIT aperture that is nominally looking in the anti-sunward direction. B-side indicates the aperture looking nominally in the sunward direction. hit_e_b_side_low_en: <<: *default_uint32 - CATDESC: Low energy (~300 keV) electrons (B-side) - FIELDNAM: Low energy (~300 keV) electrons (B-side) - LABLAXIS: hit_e_b_side_low_en + CATDESC: Low energy (>0.5 MeV) electron count rate (B-side, sunward) + FIELDNAM: Low energy electron count rate (B-side) + LABLAXIS: B-side e- >0.5 MeV + DEPEND_0: hit_epoch UNITS: counts per second + VALIDMIN: 0 + VALIDMAX: 1000000000 + VAR_NOTES: A-side indicates the HIT aperture that is nominally looking in the anti-sunward direction. B-side indicates the aperture looking nominally in the sunward direction. hit_e_b_side_med_en: <<: *default_uint32 - CATDESC: Medium energy (~3 MeV) electrons (B-side) - FIELDNAM: Medium energy (~3 MeV) electrons (B-side) - LABLAXIS: hit_e_b_side_med_en + CATDESC: Medium energy (<1 MeV) electron count rate (B-side, sunward) + FIELDNAM: Medium energy electron count rate (B-side) + LABLAXIS: B-side e- <1 MeV + DEPEND_0: hit_epoch UNITS: counts per second + VALIDMIN: 0 + VALIDMAX: 1000000000 + VAR_NOTES: A-side indicates the HIT aperture that is nominally looking in the anti-sunward direction. B-side indicates the aperture looking nominally in the sunward direction. hit_e_b_side_high_en: <<: *default_uint32 - CATDESC: High energy (>3 MeV) electrons (B-side) - FIELDNAM: High energy (>3 MeV) electrons (B-side) - LABLAXIS: hit_e_b_side_high_en + CATDESC: High energy (>3 MeV) electron count rate (B-side, sunward) + FIELDNAM: High energy electron count rate (B-side) + LABLAXIS: B-side e- >3 MeV + DEPEND_0: hit_epoch UNITS: counts per second + VALIDMIN: 0 + VALIDMAX: 1000000000 + VAR_NOTES: A-side indicates the HIT aperture that is nominally looking in the anti-sunward direction. B-side indicates the aperture looking nominally in the sunward direction. + +hit_h_omni_low_en: + <<: *default_uint32 + CATDESC: Low energy (6 to 8 MeV) proton count rate (omnidirectional) + FIELDNAM: Proton count rate 6 to 8 MeV (omni) + LABLAXIS: Omni H+ 6 to 8 MeV + DEPEND_0: hit_epoch + UNITS: counts per second + VALIDMIN: 0 + VALIDMAX: 1000000000 + VAR_NOTES: Omni indicates the sum of the number of particles divided by the area of the full sky. hit_h_omni_med_en: <<: *default_uint32 - CATDESC: Medium energy (12–70 MeV) protons (Omnidirectional) - FIELDNAM: Medium energy (12–70 MeV) protons (Omnidirectional) - LABLAXIS: hit_h_omni_med_en + CATDESC: Medium energy (12 to 15 MeV) proton count rate (omnidirectional) + FIELDNAM: Proton count rate 12 to 15 MeV (omni) + LABLAXIS: Omni H+ 12 to 15 MeV + DEPEND_0: hit_epoch UNITS: counts per second + VALIDMIN: 0 + VALIDMAX: 1000000000 + VAR_NOTES: Omni indicates the sum of the number of particles divided by the area of the full sky. hit_h_a_side_high_en: <<: *default_uint32 - CATDESC: High energy (>70 MeV) protons (A-side) - FIELDNAM: High energy (>70 MeV) protons (A-side) - LABLAXIS: hit_h_a_side_high_en + CATDESC: High energy (>70 MeV) proton count rate (A-side, anti-sunward) + FIELDNAM: Proton count rate >70 MeV (A-side) + LABLAXIS: A-side H >70 MeV + DEPEND_0: hit_epoch UNITS: counts per second + VALIDMIN: 0 + VALIDMAX: 1000000000 + VAR_NOTES: A-side indicates the HIT aperture that is nominally looking in the anti-sunward direction. B-side indicates the aperture looking nominally in the sunward direction. hit_h_b_side_high_en: <<: *default_uint32 - CATDESC: High energy (>70 MeV) protons (B-side) - FIELDNAM: High energy (>70 MeV) protons (B-side) - LABLAXIS: hit_h_b_side_high_en + CATDESC: High energy (>70 MeV) proton count rate (B-side, sunward) + FIELDNAM: Proton count rate >70 MeV (B-side) + LABLAXIS: B-side H >70 MeV + DEPEND_0: hit_epoch UNITS: counts per second + VALIDMIN: 0 + VALIDMAX: 1000000000 + VAR_NOTES: A-side indicates the HIT aperture that is nominally looking in the anti-sunward direction. B-side indicates the aperture looking nominally in the sunward direction. hit_he_omni_low_en: <<: *default_uint32 - CATDESC: Low energy (6–8 MeV/nuc) He (Omnidirectional) - FIELDNAM: Low energy (6–8 MeV/nuc) He (Omnidirectional) - LABLAXIS: hit_he_omni_low_en + CATDESC: Low energy (6 to 8 MeV/nuc) helium count rate (omnidirectional) + FIELDNAM: Helium count rate 6 to 8 MeV/nuc (omni) + LABLAXIS: Omni He 6 to 8 MeV/nuc + DEPEND_0: hit_epoch UNITS: counts per second + VALIDMIN: 0 + VALIDMAX: 1000000000 + VAR_NOTES: Omni indicates the sum of the number of particles divided by the area of the full sky. hit_he_omni_high_en: <<: *default_uint32 - CATDESC: High energy (15–70 MeV/nuc) He (Omnidirectional) - FIELDNAM: High energy (15–70 MeV/nuc) He (Omnidirectional) - LABLAXIS: hit_he_omni_high_en + CATDESC: High energy (15 to 70 MeV/nuc) helium count rate (omnidirectional) + FIELDNAM: Helium count rate 15 to 70 MeV/nuc (omni) + LABLAXIS: Omni He 15 to 70 MeV/nuc + DEPEND_0: hit_epoch UNITS: counts per second + VALIDMIN: 0 + VALIDMAX: 1000000000 + VAR_NOTES: Omni indicates the sum of the number of particles divided by the area of the full sky. -mag_epoch: - <<: *default_float32 - CATDESC: MAG instrument epoch - FIELDNAM: mag_epoch - LABLAXIS: mag_epoch - UNITS: ns - VALIDMIN: 0.0 - VALIDMAX: 9223372036854775807 - -mag_B_GSE: +mag_B_magnitude: <<: *default_float32 - CATDESC: Magnetic field vector in GSE coordinates - FIELDNAM: mag_B_GSE - LABL_PTR_1: component + CATDESC: Magnitude of the magnetic field vector + FIELDNAM: B vector magnitude + LABLAXIS: B Magnitude + DEPEND_0: mag_epoch UNITS: nT - DEPEND_1: component - VALIDMIN: -65000 - VALIDMAX: 65000 + VALIDMIN: 0.0 + VALIDMAX: 65000.0 -mag_B_GSM: +mag_theta_B_GSE: <<: *default_float32 - CATDESC: Magnetic field vector in GSM coordinates - FIELDNAM: mag_B_GSM - LABL_PTR_1: component - UNITS: nT - DEPEND_1: component - VALIDMIN: -65000 - VALIDMAX: 65000 + CATDESC: Elevation angle of the magnetic field in GSE coordinates + FIELDNAM: B elevation angle in GSE coordinates + LABLAXIS: B elevation (GSE) + DEPEND_0: mag_epoch + UNITS: degrees + VALIDMIN: -90.0 + VALIDMAX: 90.0 -mag_B_RTN: +mag_phi_B_GSE: <<: *default_float32 - CATDESC: Magnetic field vector in RTN coordinates - FIELDNAM: mag_B_RTN - LABL_PTR_1: RTN_component - UNITS: nT - DEPEND_1: RTN_component - VALIDMIN: -65000 - VALIDMAX: 65000 + CATDESC: Azimuth angle of the magnetic field in GSE coordinates + FIELDNAM: B azimuth angle in GSE coordinates + LABLAXIS: B azimuth (GSE) + DEPEND_0: mag_epoch + UNITS: degrees + VALIDMIN: 0.0 + VALIDMAX: 360.0 -mag_B_magnitude: +mag_theta_B_GSM: <<: *default_float32 - CATDESC: Magnitude of the magnetic field vector - FIELDNAM: mag_B_magnitude - LABLAXIS: "|B|" - UNITS: nT - VALIDMIN: 0 - VALIDMAX: 65000 + CATDESC: Elevation angle of the magnetic field in GSM coordinates + FIELDNAM: B elevation angle in GSM coordinates + LABLAXIS: B elevation (GSM) + DEPEND_0: mag_epoch + UNITS: degrees + VALIDMIN: -90.0 + VALIDMAX: 90.0 mag_phi_B_GSM: <<: *default_float32 - CATDESC: Azimuth angle (φ) of the magnetic field in GSM coordinates - FIELDNAM: mag_phi_B_GSM - LABLAXIS: mag_phi_B_GSM + CATDESC: Azimuth angle of the magnetic field in GSM coordinates + FIELDNAM: B azimuth angle in GSM coordinates + LABLAXIS: B azimuth (GSM) + DEPEND_0: mag_epoch UNITS: degrees - VALIDMIN: 0 - VALIDMAX: 360 + VALIDMIN: 0.0 + VALIDMAX: 360.0 -mag_theta_B_GSM: +mag_B_GSE: <<: *default_float32 - CATDESC: Elevation angle (θ) of the magnetic field in GSM coordinates - FIELDNAM: mag_theta_B_GSM - LABLAXIS: mag_theta_B_GSM - UNITS: degrees - VALIDMIN: -90 - VALIDMAX: 90 + CATDESC: Magnetic field vector in GSE coordinates + FIELDNAM: B vector in GSE coordinates + LABLAXIS: B (GSE) + DEPEND_0: mag_epoch + LABL_PTR_1: B_GSE_labels + UNITS: nT + FORMAT: F13.6 + VALIDMIN: -65000.0 + VALIDMAX: 65000.0 -mag_phi_B_GSE: +mag_B_GSM: <<: *default_float32 - CATDESC: Azimuth angle (φ) of the magnetic field in GSE coordinates - FIELDNAM: mag_phi_B_GSE - LABLAXIS: mag_phi_B_GSE - UNITS: degrees - VALIDMIN: 0 - VALIDMAX: 360 + CATDESC: Magnetic field vector in GSM coordinates + FIELDNAM: B vector in GSM coordinates + LABLAXIS: B (GSM) + DEPEND_0: mag_epoch + LABL_PTR_1: B_GSM_labels + UNITS: nT + FORMAT: F13.6 + VALIDMIN: -65000.0 + VALIDMAX: 65000.0 -mag_theta_B_GSE: +mag_B_RTN: <<: *default_float32 - CATDESC: Elevation angle (θ) of the magnetic field in GSE coordinates - FIELDNAM: mag_theta_B_GSE - LABLAXIS: mag_theta_B_GSE - UNITS: degrees - VALIDMIN: -90 - VALIDMAX: 90 + CATDESC: Magnetic field vector in RTN coordinates + FIELDNAM: B vector in RTN coordinates + LABLAXIS: B (RTN) + DEPEND_0: mag_epoch + LABL_PTR_1: B_RTN_labels + UNITS: nT + FORMAT: F13.6 + VALIDMIN: -65000.0 + VALIDMAX: 65000.0 swapi_pseudo_proton_density: <<: *default_float32 CATDESC: Pseudo density of solar wind protons - FIELDNAM: swapi_pseudo_proton_density - LABLAXIS: swapi_pseudo_proton_density + FIELDNAM: SW proton pseudo density + LABLAXIS: SW p pseudo N + DEPEND_0: swapi_epoch UNITS: 1/cm^3 VALIDMIN: 0.0 VALIDMAX: 1000.0 + VAR_NOTES: The pseudo density is derived using a simplified analytical model of solar wind proton density. swapi_pseudo_proton_speed: <<: *default_float32 CATDESC: Pseudo speed of solar wind protons in solar inertial frame - FIELDNAM: swapi_pseudo_proton_speed - LABLAXIS: swapi_pseudo_proton_speed + FIELDNAM: SW proton pseudo V in solar inertial frame + LABLAXIS: SW p pseudo V + DEPEND_0: swapi_epoch UNITS: km/sec VALIDMIN: 0.0 VALIDMAX: 10000.0 + VAR_NOTES: The pseudo speed is derived using a simplified analytical model of solar wind proton speed. swapi_pseudo_proton_temperature: <<: *default_float32 CATDESC: Pseudo temperature of solar wind protons in plasma frame - FIELDNAM: swapi_pseudo_proton_temperature - LABLAXIS: swapi_pseudo_proton_temperature + FIELDNAM: SW proton pseudo temperature + LABLAXIS: SW p pseudo T + DEPEND_0: swapi_epoch UNITS: Kelvin VALIDMIN: 0.0 VALIDMAX: 50000000.0 + VAR_NOTES: The pseudo temperature is derived using a simplified analytical model of solar wind proton speed. + FORMAT: F15.6 # SWE Normalized Counts swe_normalized_counts: <<: *default_uint32 CATDESC: Normalized electron counts - FIELDNAM: swe_normalized_counts - LABLAXIS: swe_normalized_counts - UNITS: Normalized counts + FIELDNAM: Normalized electron counts + UNITS: normalized counts VALIDMIN: 0 VALIDMAX: 500000 - DEPEND_1: esa_step + DEPEND_1: swe_electron_energy + DISPLAY_TYPE: stack_plot + LABLAXIS: Electron counts + DEPEND_0: swe_epoch + VAR_NOTES: The nominal central energies for the 8 i-ALiRT channels are 100.4, 140, 194, 270, 376, 523, 727, and 1011 eV. swe_counterstreaming_electrons: - <<: *default_uint8 - CATDESC: Counterstreaming electrons - FIELDNAM: swe_counterstreaming_electrons - LABLAXIS: swe_counterstreaming_electrons - UNITS: "" - VALIDMIN: 0 - VALIDMAX: 1 + <<: *default_uint8 + CATDESC: Counterstreaming electrons flag (1=counterstreaming, 0=unidirectional) + FIELDNAM: Counterstreaming e- (1=counterstreaming) + LABLAXIS: Counterstreaming e- + DEPEND_0: swe_epoch + UNITS: "1=counterstreaming" + VALIDMIN: 0 + VALIDMAX: 1 + VAR_NOTES: A value of 1 indicates that counterstreaming electrons were observed, a value of 0 indicates unidirectional electron flow. + +sc_position_GSE: + <<: *default_float32 + CATDESC: Spacecraft position vector in GSE coordinates + FIELDNAM: Spacecraft position in GSE + LABLAXIS: SC position (GSE) + UNITS: km + LABL_PTR_1: sc_GSE_position_labels + VALIDMIN: -3000000.0 + VALIDMAX: 3000000.0 + FORMAT: F15.6 + +sc_velocity_GSE: + <<: *default_float32 + CATDESC: Spacecraft velocity vector in GSE coordinates + FIELDNAM: Spacecraft velocity in GSE + LABLAXIS: SC velocity (GSE) + UNITS: km/s + LABL_PTR_1: sc_GSE_velocity_labels + VALIDMIN: -1000.0 + VALIDMAX: 1000.0 + +sc_position_GSM: + <<: *default_float32 + CATDESC: Spacecraft position vector in GSM coordinates + FIELDNAM: Spacecraft position in GSM coordinates + LABLAXIS: SC position (GSM) + UNITS: km + LABL_PTR_1: sc_GSM_position_labels + VALIDMIN: -3000000.0 + VALIDMAX: 3000000.0 + FORMAT: F15.6 + +sc_velocity_GSM: + <<: *default_float32 + CATDESC: Spacecraft velocity vector in GSM coordinates + FIELDNAM: Spacecraft velocity in GSM + LABLAXIS: SC velocity (GSM) + UNITS: km/s + LABL_PTR_1: sc_GSM_velocity_labels + VALIDMIN: -1000.0 + VALIDMAX: 1000.0 diff --git a/imap_processing/codice/constants.py b/imap_processing/codice/constants.py index f108779196..670a1873ea 100644 --- a/imap_processing/codice/constants.py +++ b/imap_processing/codice/constants.py @@ -76,6 +76,24 @@ "fe_hiq": 7.25, } +HI_IALIRT_ELEVATION_ANGLE = np.array( + [ + 132.8, + 65.7, + 47.1, + 114.3, + ], + dtype=np.float32, +) +HI_IALIRT_REF_SPIN_ANGLE = np.array( + [ + 286.85, + 264.55, + 343.16, + 5.44, + ], + dtype=float, +) # Define the packet fields needed to be stored in segmented data and their # corresponding bit lengths for direct event data products diff --git a/imap_processing/ialirt/l0/parse_mag.py b/imap_processing/ialirt/l0/parse_mag.py index a0500c07dd..9a1e750142 100644 --- a/imap_processing/ialirt/l0/parse_mag.py +++ b/imap_processing/ialirt/l0/parse_mag.py @@ -16,7 +16,7 @@ Packet2, Packet3, ) -from imap_processing.ialirt.utils.grouping import find_groups +from imap_processing.ialirt.utils.grouping import find_groups, _populate_instrument_header_items from imap_processing.ialirt.utils.time import calculate_time from imap_processing.mag.l1a.mag_l1a_data import TimeTuple from imap_processing.mag.l1b.mag_l1b import ( @@ -716,7 +716,7 @@ def process_packet( ) met = grouped_data["met"][(grouped_data["group"] == group).values] - met_all.append(met.values[0]) + met_all.append(met) mago_times_all.append(time_data["primary_epoch"]) mago_vectors_all.append(mago_inertial_vector) magi_vectors_all.append(magi_inertial_vector) @@ -755,11 +755,8 @@ def process_packet( continue mag_data.append( - { - "apid": 478, - "met": int(met_all[i]), - "met_in_utc": met_to_utc(met_all[i]).split(".")[0], - "ttj2000ns": int(met_to_ttj2000ns(met_all[i])), + _populate_instrument_header_items(met_all[i]) | + { "instrument": "mag", "mag_epoch": int(mago_times_all[i]), "mag_B_GSE": [Decimal(f"{v:.3f}") for v in gse_vector[i]], diff --git a/imap_processing/ialirt/l0/process_codice.py b/imap_processing/ialirt/l0/process_codice.py index 401d428390..1372478c8e 100644 --- a/imap_processing/ialirt/l0/process_codice.py +++ b/imap_processing/ialirt/l0/process_codice.py @@ -21,7 +21,7 @@ get_geometric_factor_lut, process_lo_species_intensity, ) -from imap_processing.ialirt.utils.grouping import find_groups +from imap_processing.ialirt.utils.grouping import find_groups, _populate_instrument_header_items from imap_processing.ialirt.utils.time import calculate_time from imap_processing.spice.time import met_to_ttj2000ns, met_to_utc @@ -472,11 +472,7 @@ def process_codice( l2_lo = calculate_ratios(l1b_lo, l2_lut_path, l2_geometric_factor_path) codice_lo_data.append( - { - "apid": 478, - "met": int(met[0]), - "met_in_utc": met_to_utc(met[0]).split(".")[0], - "ttj2000ns": int(met_to_ttj2000ns(met[0])), + _populate_instrument_header_items(met) | { "instrument": f"{sensor}", f"{sensor}_c_over_o_abundance": l2_lo.c_over_o_abundance, f"{sensor}_mg_over_o_abundance": l2_lo.mg_over_o_abundance, @@ -514,11 +510,7 @@ def process_codice( ).tolist() codice_hi_data.append( - { - "apid": 478, - "met": int(met[0]), - "met_in_utc": met_to_utc(met[0]).split(".")[0], - "ttj2000ns": int(met_to_ttj2000ns(met[0])), + _populate_instrument_header_items(met) | { "instrument": f"{sensor}", f"{sensor}_epoch": [int(epoch) for epoch in l1b_hi["epoch"]], f"{sensor}_h": dec_l2_hi, diff --git a/imap_processing/ialirt/l0/process_hit.py b/imap_processing/ialirt/l0/process_hit.py index 8aaf42a87c..36143b01d9 100644 --- a/imap_processing/ialirt/l0/process_hit.py +++ b/imap_processing/ialirt/l0/process_hit.py @@ -5,7 +5,7 @@ import numpy as np import xarray as xr -from imap_processing.ialirt.utils.grouping import find_groups +from imap_processing.ialirt.utils.grouping import find_groups, _populate_instrument_header_items from imap_processing.ialirt.utils.time import calculate_time from imap_processing.spice.time import met_to_ttj2000ns, met_to_utc @@ -161,16 +161,11 @@ def process_hit(xarray_data: xr.Dataset) -> list[dict]: slow_rate = grouped_data["hit_slow_rate"][ (grouped_data["group"] == group).values ] - met = int(grouped_data["met"][(grouped_data["group"] == group).values][0]) - + met = grouped_data["met"][(grouped_data["group"] == group).values] l1 = create_l1(fast_rate_1, fast_rate_2, slow_rate) hit_data.append( - { - "apid": 478, - "met": int(met), - "met_in_utc": met_to_utc(met).split(".")[0], - "ttj2000ns": int(met_to_ttj2000ns(met)), + _populate_instrument_header_items(met) | { "instrument": "hit", "hit_e_a_side_low_en": int(l1["IALRT_RATE_1"] + l1["IALRT_RATE_2"]), "hit_e_a_side_med_en": int(l1["IALRT_RATE_5"] + l1["IALRT_RATE_6"]), diff --git a/imap_processing/ialirt/l0/process_swapi.py b/imap_processing/ialirt/l0/process_swapi.py index 239eccdbbe..e8fb070584 100644 --- a/imap_processing/ialirt/l0/process_swapi.py +++ b/imap_processing/ialirt/l0/process_swapi.py @@ -10,7 +10,7 @@ from scipy.special import erf from imap_processing.ialirt.constants import IalirtSwapiConstants as Consts -from imap_processing.ialirt.utils.grouping import find_groups +from imap_processing.ialirt.utils.grouping import find_groups, _populate_instrument_header_items from imap_processing.ialirt.utils.time import calculate_time from imap_processing.spice.time import met_to_ttj2000ns, met_to_utc from imap_processing.swapi.l1.swapi_l1 import process_sweep_data @@ -162,9 +162,7 @@ def process_swapi_ialirt( (grouped_dataset["group"] == group) ] - met_values = int( - grouped_dataset["met"][(grouped_dataset["group"] == group).values][0] - ) + met = grouped_dataset["met"][(grouped_dataset["group"] == group).values] # Ensure no duplicates and all values from 0 to 11 are present if not np.array_equal(seq_values.values.astype(int), np.arange(12)): @@ -206,11 +204,7 @@ def process_swapi_ialirt( ) swapi_data.append( - { - "apid": 478, - "met": int(met_values), - "met_in_utc": met_to_utc(met_values).split(".")[0], - "ttj2000ns": int(met_to_ttj2000ns(met_values)), + _populate_instrument_header_items(met) | { "instrument": "swapi", "swapi_pseudo_proton_speed": Decimal(f"{pseudo_speed:.3f}"), "swapi_pseudo_proton_density": Decimal(f"{pseudo_density:.3f}"), diff --git a/imap_processing/ialirt/l0/process_swe.py b/imap_processing/ialirt/l0/process_swe.py index 763b731e44..29b10fea4f 100644 --- a/imap_processing/ialirt/l0/process_swe.py +++ b/imap_processing/ialirt/l0/process_swe.py @@ -7,7 +7,7 @@ import xarray as xr from numpy.typing import NDArray -from imap_processing.ialirt.utils.grouping import find_groups +from imap_processing.ialirt.utils.grouping import find_groups, _populate_instrument_header_items from imap_processing.ialirt.utils.time import calculate_time from imap_processing.spice.time import met_to_ttj2000ns, met_to_utc from imap_processing.swe.l1a.swe_science import decompressed_counts @@ -548,30 +548,21 @@ def process_swe(accumulated_data: xr.Dataset, in_flight_cal_files: list) -> list summed_first = normalized_first_half.sum(axis=(1, 2)) summed_second = normalized_second_half.sum(axis=(1, 2)) - met_first_half = int( - grouped["met"].where(grouped["swe_seq"] == 0, drop=True).values[0] - ) - met_second_half = int( - grouped["met"].where(grouped["swe_seq"] == 30, drop=True).values[0] + met_first_half = grouped["met"].where(grouped["swe_seq"] == 0, drop=True).values + met_second_half = ( + grouped["met"].where(grouped["swe_seq"] == 30, drop=True).values ) swe_data.append( - { - "apid": 478, - "met": met_first_half, - "met_in_utc": met_to_utc(met_first_half).split(".")[0], - "ttj2000ns": int(met_to_ttj2000ns(met_first_half)), + _populate_instrument_header_items(met_first_half) |{ "instrument": "swe", "swe_normalized_counts": [int(val) for val in summed_first], "swe_counterstreaming_electrons": bde_first_half, }, ) swe_data.append( + _populate_instrument_header_items(met_second_half) | { - "apid": 478, - "met": met_second_half, - "met_in_utc": met_to_utc(met_second_half).split(".")[0], - "ttj2000ns": int(met_to_ttj2000ns(met_second_half)), "instrument": "swe", "swe_normalized_counts": [int(val) for val in summed_second], "swe_counterstreaming_electrons": bde_second_half, diff --git a/imap_processing/ialirt/utils/constants.py b/imap_processing/ialirt/utils/constants.py index a8264be80c..295a8da5a9 100644 --- a/imap_processing/ialirt/utils/constants.py +++ b/imap_processing/ialirt/utils/constants.py @@ -1,68 +1,203 @@ """Keys for I-ALiRT data products.""" -IALIRT_KEYS = [ +import numpy as np + +from imap_processing.codice.constants import ( + HI_IALIRT_ELEVATION_ANGLE, + HI_IALIRT_REF_SPIN_ANGLE, +) + +IALIRT_DIMS = { # H intensities in 15 energy ranges and binned into 4 azimuths and 4 spin angle bins - "codice_hi_h", + "codice_hi_h": [ + "codice_hi_epoch", + "codice_hi_energy_center", + "codice_hi_spin_sector", + "codice_hi_elevation", + ], # C/O abundance ratio - "codice_lo_c_over_o_abundance", + "codice_lo_c_over_o_abundance": ["codice_lo_epoch"], # Mg/O abundance ratio - "codice_lo_mg_over_o_abundance", + "codice_lo_mg_over_o_abundance": ["codice_lo_epoch"], # Fe/O abundance ratio - "codice_lo_fe_over_o_abundance", + "codice_lo_fe_over_o_abundance": ["codice_lo_epoch"], # C+6/C+5 charge state ratio - "codice_lo_c_plus_6_over_c_plus_5_ratio", + "codice_lo_c_plus_6_over_c_plus_5": ["codice_lo_epoch"], # O+7/O+6 charge state ratio - "codice_lo_o_plus_7_over_o_plus_6_ratio", + "codice_lo_o_plus_7_over_o_plus_6": ["codice_lo_epoch"], # Fe low/Fe high charge state ratio - "codice_lo_fe_low_over_fe_high_ratio", - # Low energy (~300 keV) electrons (A-side) - "hit_e_a_side_low_en", - # Medium energy (~3 MeV) electrons (A-side) - "hit_e_a_side_med_en", - # High energy (>3 MeV) electrons (A-side) - "hit_e_a_side_high_en", - # Low energy (~300 keV) electrons (B-side) - "hit_e_b_side_low_en", - # Medium energy (~3 MeV) electrons (B-side) - "hit_e_b_side_med_en", - # High energy (>3 MeV) electrons (B-side) - "hit_e_b_side_high_en", - # Medium energy (12 to 70 MeV) protons (Omnidirectional) - "hit_h_omni_med_en", - # High energy (>70 MeV) protons (A-side) - "hit_h_a_side_high_en", - # High energy (>70 MeV) protons (B-side) - "hit_h_b_side_high_en", + "codice_lo_fe_low_over_fe_high": ["codice_lo_epoch"], + # Low energy (>0.5 MeV) electrons (A-side) + "hit_e_a_side_low_en": ["hit_epoch"], + # Medium energy (<1 MeV) electrons (A-side) + "hit_e_a_side_med_en": ["hit_epoch"], + # Low energy (>0.5 MeV) electrons (B-side) + "hit_e_b_side_low_en": ["hit_epoch"], + # Medium energy (<1 MeV) electrons (B-side) + "hit_e_b_side_med_en": ["hit_epoch"], + # Low energy (6 to 8 MeV) protons (Omnidirectional) + "hit_h_omni_low_en": ["hit_epoch"], + # Medium energy (12 to 15 MeV) protons (Omnidirectional) + "hit_h_omni_med_en": ["hit_epoch"], # Low energy (6 to 8 MeV/nuc) He (Omnidirectional) - "hit_he_omni_low_en", + "hit_he_omni_low_en": ["hit_epoch"], # High energy (15 to 70 MeV/nuc) He (Omnidirectional) - "hit_he_omni_high_en", - # MAG instrument epoch - "mag_epoch", + "hit_he_omni_high_en": ["hit_epoch"], + # Magnitude of the magnetic field vector + "mag_B_magnitude": ["mag_epoch"], + # Elevation angle (θ) of the magnetic field in GSE coordinates + "mag_theta_B_GSE": ["mag_epoch"], + # Azimuth angle (φ) of the magnetic field in GSE coordinates + "mag_phi_B_GSE": ["mag_epoch"], + # Elevation angle (θ) of the magnetic field in GSM coordinates + "mag_theta_B_GSM": ["mag_epoch"], + # Azimuth angle (φ) of the magnetic field in GSM coordinates + "mag_phi_B_GSM": ["mag_epoch"], # Magnetic field vector in GSE coordinates - "mag_B_GSE", + "mag_B_GSE": ["mag_epoch", "B_GSE_labels"], # Magnetic field vector in GSM coordinates - "mag_B_GSM", + "mag_B_GSM": ["mag_epoch", "B_GSM_labels"], # Magnetic field vector in RTN coordinates - "mag_B_RTN", - # Magnitude of the magnetic field vector - "mag_B_magnitude", - # Azimuth angle (φ) of the magnetic field in GSM coordinates - "mag_phi_B_GSM", - # Elevation angle (θ) of the magnetic field in GSM coordinates - "mag_theta_B_GSM", - # Azimuth angle (φ) of the magnetic field in GSE coordinates - "mag_phi_B_GSE", - # Elevation angle (θ) of the magnetic field in GSE coordinates - "mag_theta_B_GSE", + "mag_B_RTN": ["mag_epoch", "B_RTN_labels"], # Pseudo density of solar wind protons - "swapi_pseudo_proton_density", + "swapi_pseudo_proton_density": ["swapi_epoch"], # Pseudo speed of solar wind protons in solar inertial frame - "swapi_pseudo_proton_speed", + "swapi_pseudo_proton_speed": ["swapi_epoch"], # Pseudo temperature of solar wind protons in plasma frame - "swapi_pseudo_proton_temperature", + "swapi_pseudo_proton_temperature": ["swapi_epoch"], # SWE Normalized Counts - "swe_normalized_counts", + "swe_normalized_counts": ["swe_epoch", "swe_electron_energy"], # SWE Counterstreaming flag - "swe_counterstreaming_electrons", + "swe_counterstreaming_electrons": ["swe_epoch"], + # Spacecraft position in GSE coordinates + "sc_position_GSE": ["ephemeris_epoch", "sc_GSE_position_labels"], + # Spacecraft velocity in GSE coordinates + "sc_velocity_GSE": ["ephemeris_epoch", "sc_GSE_velocity_labels"], + # Spacecraft position in GSM coordinates + "sc_position_GSM": ["ephemeris_epoch", "sc_GSM_position_labels"], + # Spacecraft velocity in GSM coordinates + "sc_velocity_GSM": ["ephemeris_epoch", "sc_GSM_velocity_labels"], +} + +IALIRT_DTYPES = { + # H intensities in 15 energy ranges and binned into 4 azimuths and 4 spin angle bins + "codice_hi_h": np.float32, + # CoDICE-Lo abundance / ratios + "codice_lo_c_over_o_abundance": np.float32, + "codice_lo_mg_over_o_abundance": np.float32, + "codice_lo_fe_over_o_abundance": np.float32, + "codice_lo_c_plus_6_over_c_plus_5": np.float32, + "codice_lo_o_plus_7_over_o_plus_6": np.float32, + "codice_lo_fe_low_over_fe_high": np.float32, + # HIT scalars + "hit_e_a_side_low_en": np.uint32, + "hit_e_a_side_med_en": np.uint32, + "hit_e_b_side_low_en": np.uint32, + "hit_e_b_side_med_en": np.uint32, + "hit_h_omni_low_en": np.uint32, + "hit_h_omni_med_en": np.uint32, + "hit_he_omni_low_en": np.uint32, + "hit_he_omni_high_en": np.uint32, + # MAG + "mag_epoch": np.int64, # if you are treating this as a data variable + "mag_B_magnitude": np.float32, + "mag_B_RTN": np.float32, + "mag_B_GSE": np.float32, + "mag_theta_B_GSE": np.float32, + "mag_phi_B_GSE": np.float32, + "mag_B_GSM": np.float32, + "mag_theta_B_GSM": np.float32, + "mag_phi_B_GSM": np.float32, + # SWAPI + "swapi_pseudo_proton_density": np.float32, + "swapi_pseudo_proton_speed": np.float32, + "swapi_pseudo_proton_temperature": np.float32, + # SWE + "swe_normalized_counts": np.uint32, + "swe_counterstreaming_electrons": np.uint8, + # Spacecraft vectors + "sc_position_GSM": np.float32, + "sc_velocity_GSM": np.float32, + "sc_position_GSE": np.float32, + "sc_velocity_GSE": np.float32, +} + +hit_restricted_fields = { + "hit_e_a_side_high_en", + "hit_e_b_side_high_en", + "hit_h_a_side_high_en", + "hit_h_b_side_high_en", +} + +codice_hi_energy_center = [ + 0.02378414, + 0.03363586, + 0.04756828, + 0.06727171, + 0.09513657, + 0.13454343, + 0.19027314, + 0.26908685, + 0.38054628, + 0.53817371, + 0.76109255, + 1.07634741, + 1.52218511, + 2.15269482, + 3.04437021, +] +codice_hi_energy_minus = [ + 0.00378414, + 0.00535159, + 0.00756828, + 0.01070317, + 0.01513657, + 0.02140634, + 0.03027314, + 0.04281268, + 0.06054628, + 0.08562537, + 0.12109255, + 0.17125073, + 0.24218511, + 0.34250146, + 0.48437021, +] +codice_hi_energy_plus = [ + 0.00450013, + 0.00636414, + 0.00900026, + 0.01272829, + 0.01800052, + 0.02545657, + 0.03600103, + 0.05091315, + 0.07200206, + 0.10182629, + 0.14400413, + 0.20365259, + 0.28800825, + 0.40730518, + 0.57601651, +] + +# Calculate spin angle +# Formula: +# θ_(g,n) = (θ_(g,0)+90°* n) mod 360° +# where +# n is number of sectored angles, 0 to 3, +# g is size of the group (inst_az), 0 to 3, +HI_IALIRT_SPIN_ANGLE = ( + HI_IALIRT_REF_SPIN_ANGLE[:, np.newaxis] + np.array([0, 1, 2, 3]) * 90 +) % 360.0 + +swe_energy = [ + 100.4, + 140.0, + 194.0, + 270.0, + 376.0, + 523.0, + 727.0, + 1011.0, ] diff --git a/imap_processing/ialirt/utils/create_xarray.py b/imap_processing/ialirt/utils/create_xarray.py index fd28e9c6b3..c97656300e 100644 --- a/imap_processing/ialirt/utils/create_xarray.py +++ b/imap_processing/ialirt/utils/create_xarray.py @@ -1,10 +1,26 @@ """Creates xarray based on structure of queried DynamoDB.""" +from collections import defaultdict + import numpy as np import xarray as xr from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes -from imap_processing.ialirt.utils.constants import IALIRT_KEYS +from imap_processing.codice.constants import ( + HI_IALIRT_ELEVATION_ANGLE, + HI_IALIRT_REF_SPIN_ANGLE, +) +from imap_processing.ialirt.utils.constants import ( + IALIRT_DIMS, + IALIRT_DTYPES, + codice_hi_energy_center, + codice_hi_energy_minus, + codice_hi_energy_plus, + HI_IALIRT_SPIN_ANGLE, + hit_restricted_fields, + swe_energy, +) +from imap_processing.spice.time import TTJ2000_EPOCH def create_xarray_from_records(records: list[dict]) -> xr.Dataset: # noqa: PLR0912 @@ -25,147 +41,259 @@ def create_xarray_from_records(records: list[dict]) -> xr.Dataset: # noqa: PLR0 cdf_manager.add_instrument_global_attrs("ialirt") cdf_manager.add_instrument_variable_attrs("ialirt", "l1") - instrument_keys: set[str] = set(IALIRT_KEYS) - n = len(records) - attrs = cdf_manager.get_variable_attributes("default_int64_attrs") - fillval = attrs.get("FILLVAL") - ttj2000ns_values = np.full(n, fillval, dtype=np.int64) + one_epoch = {"codice_lo", "hit", "swapi", "swe", "spacecraft", "mag"} + multi_epoch = {"codice_hi"} + + epochs: dict[str, list[int]] = {inst: [] for inst in (one_epoch | multi_epoch)} + by_inst: dict[str, list[dict]] = defaultdict(list) + + for record in records: + inst = record.get("instrument") + by_inst[record["instrument"]].append(record) + if inst == "spacecraft": + epochs[inst].append(record["ttj2000ns"]) + elif inst in one_epoch: + epochs[inst].append(record[f"{inst}_epoch"]) + elif inst in multi_epoch: + epochs[inst].extend(record[f"{inst}_epoch"]) + + epoch_arrays = {} + + for inst, arr in epochs.items(): + if inst == "spacecraft": + coord = "ephemeris_epoch" + attr = cdf_manager.get_variable_attributes("ephemeris_epoch", check_schema=False) + else: + coord = f"{inst}_epoch" + attr = cdf_manager.get_variable_attributes( + f"{inst}_epoch", check_schema=False + ) + epoch_arrays[coord] = xr.DataArray( + data=np.array(arr, dtype=np.int64), name=coord, dims=[coord], attrs=attr + ) + + sc_gsm_position_component = xr.DataArray( + ["sc X (GSM)", "sc Y (GSM)", "sc Z (GSM)"], + name="sc_GSM_position_labels", + dims=["sc_GSM_position_labels"], + attrs=cdf_manager.get_variable_attributes("sc_GSM_position_labels", check_schema=False), + ) + sc_gsm_velocity_component = xr.DataArray( + ["sc Vx (GSM)", "sc Vy (GSM)", "sc Vz (GSM)"], + name="sc_GSM_velocity_labels", + dims=["sc_GSM_velocity_labels"], + attrs=cdf_manager.get_variable_attributes("sc_GSM_velocity_labels", check_schema=False), + ) - # Collect all keys that start with the instrument prefixes. - for i, record in enumerate(records): - ttj2000ns_values[i] = record["ttj2000ns"] + sc_gse_position_component = xr.DataArray( + ["sc X (GSE)", "sc Y (GSE)", "sc Z (GSE)"], + name="sc_GSE_position_labels", + dims=["sc_GSE_position_labels"], + attrs=cdf_manager.get_variable_attributes("sc_GSE_position_labels", check_schema=False), + ) + sc_gse_velocity_component = xr.DataArray( + ["sc Vx (GSE)", "sc Vy (GSE)", "sc Vz (GSE)"], + name="sc_GSE_velocity_labels", + dims=["sc_GSE_velocity_labels"], + attrs=cdf_manager.get_variable_attributes("sc_GSE_velocity_labels", check_schema=False), + ) - epoch = xr.DataArray( - data=ttj2000ns_values, - name="epoch", - dims=["epoch"], - attrs=cdf_manager.get_variable_attributes("epoch", check_schema=False), + gsm_component = xr.DataArray( + ["Bx (GSM)", "By (GSM)", "Bz (GSM)"], + name="B_GSM_labels", + dims=["B_GSM_labels"], + attrs=cdf_manager.get_variable_attributes("B_GSM_labels", check_schema=False), ) - component = xr.DataArray( - ["x", "y", "z"], - name="component", - dims=["component"], - attrs=cdf_manager.get_variable_attributes("component", check_schema=False), + + gse_component = xr.DataArray( + ["Bx (GSE)", "By (GSE)", "Bz (GSE)"], + name="B_GSE_labels", + dims=["B_GSE_labels"], + attrs=cdf_manager.get_variable_attributes("B_GSE_labels", check_schema=False), ) rtn_component = xr.DataArray( - ["radial", "tangential", "normal"], - name="RTN_component", - dims=["RTN_component"], - attrs=cdf_manager.get_variable_attributes("RTN_component", check_schema=False), + ["B radial (RTN)", "B tangential (RTN)", "B normal (RTN)"], + name="B_RTN_labels", + dims=["B_RTN_labels"], + attrs=cdf_manager.get_variable_attributes("B_RTN_labels", check_schema=False), ) - esa_step = xr.DataArray( - data=np.arange(8, dtype=np.uint8), - name="esa_step", - dims=["esa_step"], - attrs=cdf_manager.get_variable_attributes("esa_step", check_schema=False), + swe_electron_energy = xr.DataArray( + data=np.float32(swe_energy), + name="swe_electron_energy", + dims=["swe_electron_energy"], + attrs=cdf_manager.get_variable_attributes( + "swe_electron_energy", check_schema=False + ), + ) + + codice_hi_energy_centers = xr.DataArray( + data=np.array(codice_hi_energy_center, dtype=np.float32), + name="codice_hi_energy_center", + dims=["codice_hi_energy_center"], + attrs=cdf_manager.get_variable_attributes( + "codice_hi_energy_center", check_schema=False + ), ) - energy_ranges = xr.DataArray( - data=np.arange(15, dtype=np.uint8), - name="codice_hi_h_energy_ranges", - dims=["codice_hi_h_energy_ranges"], + codice_energy_minus = xr.DataArray( + data=np.array(codice_hi_energy_minus, dtype=np.float32), + name="codice_hi_energy_minus", + dims=["codice_hi_energy_center"], attrs=cdf_manager.get_variable_attributes( - "codice_hi_h_energy_ranges", check_schema=False + "codice_hi_energy_minus", check_schema=False + ), + ) + + codice_energy_plus = xr.DataArray( + data=np.array(codice_hi_energy_plus, dtype=np.float32), + name="codice_hi_energy_plus", + dims=["codice_hi_energy_center"], + attrs=cdf_manager.get_variable_attributes( + "codice_hi_energy_plus", check_schema=False ), ) elevation = xr.DataArray( - data=np.arange(4, dtype=np.uint8), - name="codice_hi_h_elevation", - dims=["codice_hi_h_elevation"], + HI_IALIRT_ELEVATION_ANGLE, + name="codice_hi_elevation", + dims=["codice_hi_elevation"], attrs=cdf_manager.get_variable_attributes( - "codice_hi_h_elevation", check_schema=False + "codice_hi_elevation", check_schema=False + ), + ) + + elevation_labels = xr.DataArray( + [f"{float(v):.1f}deg" for v in elevation.values], + name="codice_hi_elevation_labels", + dims=["codice_hi_elevation"], + attrs=cdf_manager.get_variable_attributes( + "codice_hi_elevation_labels", check_schema=False ), ) spin_angle = xr.DataArray( + data=HI_IALIRT_SPIN_ANGLE.astype(np.float32), + name="codice_hi_spin_angle", + dims=["codice_hi_spin_sector", "codice_hi_elevation"], + attrs=cdf_manager.get_variable_attributes( + "codice_hi_spin_angle", check_schema=False + ), + ) + + spin_sector = xr.DataArray( data=np.arange(4, dtype=np.uint8), - name="codice_hi_h_spin_angle", - dims=["codice_hi_h_spin_angle"], + name="codice_hi_spin_sector", + dims=["codice_hi_spin_sector"], + attrs=cdf_manager.get_variable_attributes( + "codice_hi_spin_sector", check_schema=False + ), + ) + + spin_sector_labels = xr.DataArray( + [ + "0", + "1", + "2", + "3", + ], + name="codice_hi_spin_sector_labels", + dims=["codice_hi_spin_sector"], attrs=cdf_manager.get_variable_attributes( - "codice_hi_h_spin_angle", check_schema=False + "codice_hi_spin_sector_labels", check_schema=False ), ) coords = { - "epoch": epoch, - "component": component, - "RTN_component": rtn_component, - "esa_step": esa_step, - "codice_hi_h_energy_ranges": energy_ranges, - "codice_hi_h_elevation": elevation, - "codice_hi_h_spin_angle": spin_angle, + "codice_hi_epoch": epoch_arrays["codice_hi_epoch"], + "codice_lo_epoch": epoch_arrays["codice_lo_epoch"], + "hit_epoch": epoch_arrays["hit_epoch"], + "mag_epoch": epoch_arrays["mag_epoch"], + "swapi_epoch": epoch_arrays["swapi_epoch"], + "swe_epoch": epoch_arrays["swe_epoch"], + "ephemeris_epoch": epoch_arrays["ephemeris_epoch"], + "B_GSM_labels": gsm_component, + "B_GSE_labels": gse_component, + "B_RTN_labels": rtn_component, + "sc_GSM_position_labels": sc_gsm_position_component, + "sc_GSM_velocity_labels": sc_gsm_velocity_component, + "sc_GSE_position_labels": sc_gse_position_component, + "sc_GSE_velocity_labels": sc_gse_velocity_component, + "codice_hi_energy_center": codice_hi_energy_centers, + "codice_hi_energy_minus": codice_energy_minus, + "codice_hi_energy_plus": codice_energy_plus, + "codice_hi_elevation": elevation, + "codice_hi_elevation_labels": elevation_labels, + "codice_hi_spin_angle": spin_angle, + "codice_hi_spin_sector": spin_sector, + "codice_hi_spin_sector_labels": spin_sector_labels, + "swe_electron_energy": swe_electron_energy, } dataset = xr.Dataset( coords=coords, attrs=cdf_manager.get_global_attributes("imap_ialirt_l1_realtime"), ) - # Create empty dataset for each key. - for key in instrument_keys: + # Create variables with fill values. + for key in IALIRT_DIMS: + dims = IALIRT_DIMS[key] attrs = cdf_manager.get_variable_attributes(key, check_schema=False) - fillval = attrs.get("FILLVAL") - if key in ["mag_B_GSE", "mag_B_GSM"]: - data = np.full((n, 3), fillval, dtype=np.float32) - dims = ["epoch", "component"] - dataset[key] = xr.DataArray(data, dims=dims, attrs=attrs) - elif key == "mag_B_RTN": - data = np.full((n, 3), fillval, dtype=np.float32) - dims = ["epoch", "RTN_component"] - dataset[key] = xr.DataArray(data, dims=dims, attrs=attrs) - elif key.startswith("codice_hi"): - data = np.full((n, 15, 4, 4), fillval, dtype=np.float32) - dims = [ - "epoch", - "codice_hi_h_energy_ranges", - "codice_hi_h_elevation", - "codice_hi_h_spin_angle", - ] - dataset[key] = xr.DataArray(data, dims=dims, attrs=attrs) - elif key == "swe_counterstreaming_electrons": - data = np.full(n, fillval, dtype=np.uint8) - dims = ["epoch"] - dataset[key] = xr.DataArray(data, dims=dims, attrs=attrs) - elif key.startswith("swe"): - data = np.full((n, 8), fillval, dtype=np.uint32) - dims = ["epoch", "esa_step"] - dataset[key] = xr.DataArray(data, dims=dims, attrs=attrs) - elif key.startswith("hit"): - data = np.full(n, fillval, dtype=np.uint32) - dims = ["epoch"] - dataset[key] = xr.DataArray(data, dims=dims, attrs=attrs) - else: - data = np.full(n, fillval, dtype=np.float32) - dims = ["epoch"] - dataset[key] = xr.DataArray(data, dims=dims, attrs=attrs) + fill = attrs["FILLVAL"] + dtype = IALIRT_DTYPES[key] + + shape = [dataset.dims[d] for d in dims] + + data = np.full(shape, fill, dtype=dtype) + dataset[key] = xr.DataArray(data, dims=dims, attrs=attrs) + + for i, record in enumerate(by_inst.get("mag", [])): + for key in IALIRT_DIMS.keys(): + if key in ["mag_B_GSE", "mag_B_GSM", "mag_B_RTN"]: + dataset[key].data[i, :] = record[key] - # Populate the dataset variables - for i, record in enumerate(records): - for key, val in record.items(): if key in [ - "apid", - "met", - "met_in_utc", - "ttj2000ns", - "last_modified", - "sc_position_GSM", - "sc_position_GSE", - "sc_velocity_GSM", - "sc_velocity_GSE", - "mag_hk_status", - "spice_kernels", - "instrument", + "mag_B_magnitude", + "mag_theta_B_GSE", + "mag_phi_B_GSE", + "mag_theta_B_GSM", + "mag_phi_B_GSM", ]: - continue - elif key in ["mag_B_GSE", "mag_B_GSM", "mag_B_RTN"]: - dataset[key].data[i, :] = val - elif key.startswith("swe_normalized_counts"): - dataset[key].data[i, :] = val - elif key.startswith("codice_hi"): - dataset[key].data[i, :, :, :] = val - else: - dataset[key].data[i] = val + dataset[key].data[i] = np.float32(record[key]) + + for i, record in enumerate(by_inst.get("codice_hi", [])): + # 4 codice-hi epochs per record + t0 = 4 * i + t1 = t0 + 4 + hi = np.asarray(record["codice_hi_h"], dtype=np.float32) + dataset["codice_hi_h"].data[t0:t1, :, :, :] = hi + + for i, record in enumerate(by_inst.get("codice_lo", [])): + for key in IALIRT_DIMS.keys(): + if key.startswith("codice_lo_"): + dataset[key].data[i] = np.float32(record[key]) + + for i, record in enumerate(by_inst.get("hit", [])): + for key in IALIRT_DIMS.keys(): + if key.startswith("hit_") and key not in hit_restricted_fields: + dataset[key].data[i] = np.uint32(record[key]) + + for i, record in enumerate(by_inst.get("swapi", [])): + for key in IALIRT_DIMS.keys(): + if key.startswith("swapi_"): + dataset[key].data[i] = np.float32(record[key]) + + for i, record in enumerate(by_inst.get("swe", [])): + dataset["swe_normalized_counts"].data[i, :] = np.asarray( + record["swe_normalized_counts"], dtype=np.uint32 + ) + dataset["swe_counterstreaming_electrons"].data[i] = np.uint8( + record["swe_counterstreaming_electrons"] + ) + + for i, record in enumerate(by_inst.get("spacecraft", [])): + for key in IALIRT_DIMS.keys(): + if key.startswith("sc_"): + dataset[key].data[i, :] = np.asarray(record[key], dtype=np.float32) return dataset diff --git a/imap_processing/ialirt/utils/grouping.py b/imap_processing/ialirt/utils/grouping.py index ca0ae6ac28..6cdad041b2 100644 --- a/imap_processing/ialirt/utils/grouping.py +++ b/imap_processing/ialirt/utils/grouping.py @@ -4,6 +4,7 @@ import numpy as np import xarray as xr +from imap_processing.spice.time import met_to_ttj2000ns, met_to_utc logger = logging.getLogger(__name__) @@ -124,3 +125,25 @@ def find_groups( filtered_data = grouped_data return filtered_data + + +def _populate_instrument_header_items(met: np.ndarray) -> dict: + """ + Create header values. + + Parameters + ---------- + met : np.ndarray + Mission elapsed time. + + Returns + ------- + header : dict + Header for each instrument. + """ + sc_met = (met[0] + met[-1]) // 2 + header = {"apid": 478, + "met": sc_met, + "met_in_utc": met_to_utc(sc_met).split(".")[0], + "ttj2000ns": int(met_to_ttj2000ns(sc_met))} + return header diff --git a/imap_processing/tests/ialirt/unit/test_create_xarray.py b/imap_processing/tests/ialirt/unit/test_create_xarray.py index 123ee2b188..5e8c3d65bf 100644 --- a/imap_processing/tests/ialirt/unit/test_create_xarray.py +++ b/imap_processing/tests/ialirt/unit/test_create_xarray.py @@ -6,6 +6,7 @@ import numpy.testing as npt from imap_processing.cdf.utils import write_cdf +from imap_processing.ialirt.utils.constants import swe_energy from imap_processing.ialirt.utils.create_xarray import create_xarray_from_records @@ -13,119 +14,157 @@ def test_create_dataset(): """Tests create_dataset function.""" records = [ { - "apid": 478, - "met": 123456789, - "met_in_utc": "2025-06-20T08:00:00", + "instrument": "mag", + "time_utc": "2025-06-20T08:00:00", + "ttj2000ns": 123456789000001, + "mag_epoch": 123456789000001, + "mag_B_GSE": [Decimal("5.0"), Decimal("-3.2"), Decimal("1.1")], + "mag_B_GSM": [Decimal("4.8"), Decimal("-3.0"), Decimal("1.0")], + "mag_B_RTN": [Decimal("5.1"), Decimal("-3.3"), Decimal("1.2")], + "mag_B_magnitude": Decimal("6.0"), + "mag_phi_B_GSM": Decimal("45.0"), + "mag_theta_B_GSM": Decimal("30.0"), + "mag_phi_B_GSE": Decimal("50.0"), + "mag_theta_B_GSE": Decimal("35.0"), + }, + { + "instrument": "codice_hi", + "time_utc": "2025-06-20T08:00:00", "ttj2000ns": 123456789000000, - "swe_normalized_counts": [Decimal("0.0") for _ in range(8)], - "swe_counterstreaming_electrons": Decimal("0.0"), - "swapi_pseudo_proton_speed": Decimal("0.0"), - "swapi_pseudo_proton_density": Decimal("0.0"), - "swapi_pseudo_proton_temperature": Decimal("0.0"), + "codice_hi_epoch": [ + 123456789000000, + 123456789000000, + 123456789000000, + 123456789000000, + ], + "codice_hi_h": [ + [ + [[Decimal("1.0") for _ in range(4)] for _ in range(4)] + for _ in range(15) + ] + for _ in range(4) + ], + }, + { + "instrument": "codice_lo", + "time_utc": "2025-06-20T08:00:00", + "ttj2000ns": 123456789000000, + "codice_lo_epoch": 123456789000000, + "codice_lo_c_over_o_abundance": Decimal("0.5"), + "codice_lo_mg_over_o_abundance": Decimal("0.3"), + "codice_lo_fe_over_o_abundance": Decimal("0.2"), + "codice_lo_c_plus_6_over_c_plus_5": Decimal("0.7"), + "codice_lo_o_plus_7_over_o_plus_6": Decimal("0.6"), + "codice_lo_fe_low_over_fe_high": Decimal("0.4"), + }, + { + "instrument": "hit", + "time_utc": "2025-06-20T08:00:00", + "ttj2000ns": 123456789000002, + "hit_epoch": 123456789000002, "hit_e_a_side_low_en": Decimal("0.0"), "hit_e_a_side_med_en": Decimal("0.0"), "hit_e_a_side_high_en": Decimal("0.0"), "hit_e_b_side_low_en": Decimal("0.0"), "hit_e_b_side_med_en": Decimal("0.0"), "hit_e_b_side_high_en": Decimal("0.0"), + "hit_h_omni_low_en": Decimal("0.0"), "hit_h_omni_med_en": Decimal("0.0"), "hit_h_a_side_high_en": Decimal("0.0"), "hit_h_b_side_high_en": Decimal("0.0"), "hit_he_omni_low_en": Decimal("0.0"), "hit_he_omni_high_en": Decimal("0.0"), - "mag_epoch": Decimal("0.0"), - "mag_B_GSE": [Decimal("0.0"), Decimal("0.0"), Decimal("0.0")], - "mag_B_GSM": [Decimal("0.0"), Decimal("0.0"), Decimal("0.0")], - "mag_B_RTN": [Decimal("0.0"), Decimal("0.0"), Decimal("0.0")], - "mag_B_magnitude": Decimal("0.0"), - "mag_phi_B_GSM": Decimal("0.0"), - "mag_theta_B_GSM": Decimal("0.0"), - "mag_phi_B_GSE": Decimal("0.0"), - "mag_theta_B_GSE": Decimal("0.0"), - "codice_lo_c_over_o_abundance": Decimal("0.0"), - "codice_lo_mg_over_o_abundance": Decimal("0.0"), - "codice_lo_fe_over_o_abundance": Decimal("0.0"), - "codice_lo_c_plus_6_over_c_plus_5_ratio": Decimal("0.0"), - "codice_lo_o_plus_7_over_o_plus_6_ratio": Decimal("0.0"), - "codice_lo_fe_low_over_fe_high_ratio": Decimal("0.0"), - "codice_hi_h": [ - [[Decimal("0.0") for _ in range(4)] for _ in range(4)] - for _ in range(15) - ], }, { - "apid": 478, - "met": 123456789, - "met_in_utc": "2025-06-20T08:00:00", - "ttj2000ns": 123456789000001, - # Only MAG is present - "mag_epoch": Decimal("0.0"), - "mag_B_GSE": [Decimal("0.0"), Decimal("0.0"), Decimal("0.0")], - "mag_B_GSM": [Decimal("0.0"), Decimal("0.0"), Decimal("0.0")], - "mag_B_RTN": [Decimal("0.0"), Decimal("0.0"), Decimal("0.0")], - "mag_B_magnitude": Decimal("0.0"), - "mag_phi_B_GSM": Decimal("0.0"), - "mag_theta_B_GSM": Decimal("0.0"), - "mag_phi_B_GSE": Decimal("0.0"), - "mag_theta_B_GSE": Decimal("0.0"), + "instrument": "swapi", + "time_utc": "2025-06-20T08:00:00", + "ttj2000ns": 123456789000002, + "swapi_epoch": 123456789000002, + "swapi_pseudo_proton_speed": Decimal("400.0"), + "swapi_pseudo_proton_density": Decimal("5.0"), + "swapi_pseudo_proton_temperature": Decimal("100000.0"), }, { - "apid": 478, - "met": 123456789, - "met_in_utc": "2025-06-20T08:00:00", + "instrument": "swe", + "time_utc": "2025-06-20T08:00:00", "ttj2000ns": 123456789000002, - # Only SWAPI is present - "swapi_pseudo_proton_speed": Decimal("0.0"), - "swapi_pseudo_proton_density": Decimal("0.0"), - "swapi_pseudo_proton_temperature": Decimal("0.0"), + "swe_epoch": 123456789000002, + "swe_normalized_counts": [Decimal("0.0") for _ in range(8)], + "swe_counterstreaming_electrons": Decimal("1.0"), + }, + { + "instrument": "spacecraft", + "time_utc": "2025-10-29T18:55:02", + "ttj2000ns": 123456789000002, + "sc_position_GSE": [ + Decimal("1373251.6968303905"), + Decimal("-431299.0150430931"), + Decimal("73446.43257187483"), + ], + "sc_position_GSM": [ + Decimal("1373251.6968303905"), + Decimal("-400988.5784292875"), + Decimal("174989.6534196707"), + ], + "sc_velocity_GSE": [ + Decimal("0.03919581036966908"), + Decimal("-0.21796820670587755"), + Decimal("-0.019698638532273577"), + ], + "sc_velocity_GSM": [ + Decimal("0.03919581036966908"), + Decimal("-1.9156200243319468"), + Decimal("-3.8606800975317896"), + ], }, ] dataset = create_xarray_from_records(records) - assert (dataset["component"].values == ["x", "y", "z"]).all() - assert (dataset["RTN_component"].values == ["radial", "tangential", "normal"]).all() - npt.assert_array_equal(dataset["esa_step"].values, np.arange(8)) + assert ( + dataset["B_GSM_labels"].values == ["Bx (GSM)", "By (GSM)", "Bz (GSM)"] + ).all() + assert ( + dataset["B_RTN_labels"].values + == ["B radial (RTN)", "B tangential (RTN)", "B normal (RTN)"] + ).all() + np.testing.assert_allclose( + dataset["swe_electron_energy"].values, + np.array(swe_energy), + rtol=1e-7, + atol=1e-6, + ) npt.assert_array_equal( dataset["swe_normalized_counts"].values[0], np.zeros(8, dtype=np.uint32), ) - npt.assert_array_equal( - dataset["swe_normalized_counts"].values[1], - np.full(8, 4294967295, dtype=np.uint32), - ) np.testing.assert_allclose( dataset["hit_e_a_side_low_en"].values, - [0, 4294967295, 4294967295], - ) - np.testing.assert_allclose( - dataset["mag_B_GSE"].isel(epoch=0).values, - [0, 0, 0], + [0.0], ) np.testing.assert_allclose( - dataset["mag_B_GSE"].isel(epoch=1).values, - [0, 0, 0], + dataset["mag_B_GSE"].sel(mag_epoch=123456789000001).values, + [5.0, -3.2, 1.1], ) - expected_zeros = np.zeros((15, 4, 4), dtype=np.float32) - expected_fill = np.full((15, 4, 4), -1e31, dtype=np.float32) - - npt.assert_array_equal(dataset["codice_hi_h"].isel(epoch=0).values, expected_zeros) - - npt.assert_array_equal(dataset["codice_hi_h"].isel(epoch=1).values, expected_fill) + expected_zeros = np.ones((15, 4, 4), dtype=np.float32) + npt.assert_array_equal( + dataset["codice_hi_h"].isel(codice_hi_epoch=0).values, expected_zeros + ) - assert dataset["mag_B_GSE"].dims == ("epoch", "component") - assert dataset["swe_normalized_counts"].dims == ("epoch", "esa_step") + assert dataset["mag_B_GSE"].dims == ("mag_epoch", "B_GSE_labels") + assert dataset["swe_normalized_counts"].dims == ("swe_epoch", "swe_electron_energy") assert dataset["codice_hi_h"].dims == ( - "epoch", - "codice_hi_h_energy_ranges", - "codice_hi_h_elevation", - "codice_hi_h_spin_angle", + "codice_hi_epoch", + "codice_hi_energy_center", + "codice_hi_spin_sector", + "codice_hi_elevation", ) # Tests that you can write to a cdf. dataset.attrs["Data_version"] = "001" - test_data_path = write_cdf(dataset, istp=True) + dataset.attrs["Start_date"] = "20260114" + test_data_path = write_cdf(dataset, istp=True, compression=None) assert test_data_path.exists() diff --git a/imap_processing/tests/ialirt/unit/test_grouping.py b/imap_processing/tests/ialirt/unit/test_grouping.py index a578713d62..aba11fac42 100644 --- a/imap_processing/tests/ialirt/unit/test_grouping.py +++ b/imap_processing/tests/ialirt/unit/test_grouping.py @@ -4,7 +4,7 @@ import pytest import xarray as xr -from imap_processing.ialirt.utils.grouping import filter_valid_groups, find_groups +from imap_processing.ialirt.utils.grouping import filter_valid_groups, find_groups, _populate_instrument_header_items @pytest.fixture @@ -96,3 +96,16 @@ def test_find_groups_no_valid(test_data): grouped_data = find_groups(nominal_data, (0, 3), "sequence", "time_seconds") assert np.all(np.unique(grouped_data["group"]) == np.array([1])) + + +def test_populate_instrument_header_items_from_met_array(): + """Test _populate_instrument_header_items.""" + + met = np.array([100.0, 101.0, 102.0, 103.0], dtype=float) + + header = _populate_instrument_header_items(met) + + expected_keys = {"apid", "met", "met_in_utc", "ttj2000ns"} + assert expected_keys.issubset(header.keys()) + + assert header["met"] == (met[0] + met[-1]) // 2