imap-processing 0.11.0__py3-none-any.whl → 0.12.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of imap-processing might be problematic. Click here for more details.

Files changed (288) hide show
  1. imap_processing/__init__.py +10 -11
  2. imap_processing/_version.py +2 -2
  3. imap_processing/ccsds/excel_to_xtce.py +65 -16
  4. imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +6 -28
  5. imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +365 -42
  6. imap_processing/cdf/config/imap_glows_global_cdf_attrs.yaml +0 -5
  7. imap_processing/cdf/config/imap_hi_global_cdf_attrs.yaml +10 -11
  8. imap_processing/cdf/config/imap_hi_variable_attrs.yaml +17 -19
  9. imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +26 -13
  10. imap_processing/cdf/config/imap_hit_l1a_variable_attrs.yaml +106 -116
  11. imap_processing/cdf/config/imap_hit_l1b_variable_attrs.yaml +120 -145
  12. imap_processing/cdf/config/imap_hit_l2_variable_attrs.yaml +14 -0
  13. imap_processing/cdf/config/imap_idex_global_cdf_attrs.yaml +6 -9
  14. imap_processing/cdf/config/imap_idex_l1a_variable_attrs.yaml +1 -1
  15. imap_processing/cdf/config/imap_lo_global_cdf_attrs.yaml +0 -12
  16. imap_processing/cdf/config/imap_lo_l1a_variable_attrs.yaml +1 -1
  17. imap_processing/cdf/config/imap_mag_global_cdf_attrs.yaml +9 -21
  18. imap_processing/cdf/config/imap_mag_l1a_variable_attrs.yaml +361 -0
  19. imap_processing/cdf/config/imap_mag_l1b_variable_attrs.yaml +160 -0
  20. imap_processing/cdf/config/imap_mag_l1c_variable_attrs.yaml +160 -0
  21. imap_processing/cdf/config/imap_spacecraft_global_cdf_attrs.yaml +18 -0
  22. imap_processing/cdf/config/imap_spacecraft_variable_attrs.yaml +40 -0
  23. imap_processing/cdf/config/imap_swapi_global_cdf_attrs.yaml +1 -5
  24. imap_processing/cdf/config/imap_swe_global_cdf_attrs.yaml +12 -4
  25. imap_processing/cdf/config/imap_swe_l1a_variable_attrs.yaml +16 -2
  26. imap_processing/cdf/config/imap_swe_l1b_variable_attrs.yaml +48 -52
  27. imap_processing/cdf/config/imap_swe_l2_variable_attrs.yaml +71 -47
  28. imap_processing/cdf/config/imap_ultra_global_cdf_attrs.yaml +2 -14
  29. imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +51 -2
  30. imap_processing/cdf/config/imap_ultra_l1c_variable_attrs.yaml +29 -14
  31. imap_processing/cdf/utils.py +13 -7
  32. imap_processing/cli.py +23 -8
  33. imap_processing/codice/codice_l1a.py +207 -85
  34. imap_processing/codice/constants.py +1322 -568
  35. imap_processing/codice/decompress.py +2 -6
  36. imap_processing/ena_maps/ena_maps.py +480 -116
  37. imap_processing/ena_maps/utils/coordinates.py +19 -0
  38. imap_processing/ena_maps/utils/map_utils.py +14 -17
  39. imap_processing/ena_maps/utils/spatial_utils.py +45 -47
  40. imap_processing/hi/l1a/hi_l1a.py +24 -18
  41. imap_processing/hi/l1a/histogram.py +0 -1
  42. imap_processing/hi/l1a/science_direct_event.py +6 -8
  43. imap_processing/hi/l1b/hi_l1b.py +31 -39
  44. imap_processing/hi/l1c/hi_l1c.py +405 -17
  45. imap_processing/hi/utils.py +58 -12
  46. imap_processing/hit/ancillary/imap_hit_l1b-to-l2-standard-dt0-factors_20250219_v002.csv +205 -0
  47. imap_processing/hit/ancillary/imap_hit_l1b-to-l2-standard-dt1-factors_20250219_v002.csv +205 -0
  48. imap_processing/hit/ancillary/imap_hit_l1b-to-l2-standard-dt2-factors_20250219_v002.csv +205 -0
  49. imap_processing/hit/ancillary/imap_hit_l1b-to-l2-standard-dt3-factors_20250219_v002.csv +205 -0
  50. imap_processing/hit/ancillary/imap_hit_l1b-to-l2-summed-dt0-factors_20250219_v002.csv +68 -0
  51. imap_processing/hit/hit_utils.py +173 -1
  52. imap_processing/hit/l0/constants.py +20 -11
  53. imap_processing/hit/l0/decom_hit.py +18 -4
  54. imap_processing/hit/l1a/hit_l1a.py +45 -54
  55. imap_processing/hit/l1b/constants.py +317 -0
  56. imap_processing/hit/l1b/hit_l1b.py +367 -18
  57. imap_processing/hit/l2/constants.py +281 -0
  58. imap_processing/hit/l2/hit_l2.py +614 -0
  59. imap_processing/hit/packet_definitions/hit_packet_definitions.xml +1323 -71
  60. imap_processing/ialirt/l0/mag_l0_ialirt_data.py +155 -0
  61. imap_processing/ialirt/l0/parse_mag.py +246 -0
  62. imap_processing/ialirt/l0/process_swe.py +252 -0
  63. imap_processing/ialirt/packet_definitions/ialirt.xml +7 -3
  64. imap_processing/ialirt/packet_definitions/ialirt_mag.xml +115 -0
  65. imap_processing/ialirt/utils/grouping.py +114 -0
  66. imap_processing/ialirt/utils/time.py +29 -0
  67. imap_processing/idex/atomic_masses.csv +22 -0
  68. imap_processing/idex/decode.py +2 -2
  69. imap_processing/idex/idex_constants.py +25 -0
  70. imap_processing/idex/idex_l1a.py +6 -7
  71. imap_processing/idex/idex_l1b.py +4 -31
  72. imap_processing/idex/idex_l2a.py +789 -0
  73. imap_processing/idex/idex_variable_unpacking_and_eu_conversion.csv +39 -33
  74. imap_processing/lo/l0/lo_science.py +6 -0
  75. imap_processing/lo/l1a/lo_l1a.py +0 -1
  76. imap_processing/lo/l1b/lo_l1b.py +177 -25
  77. imap_processing/mag/constants.py +8 -0
  78. imap_processing/mag/imap_mag_sdc-configuration_v001.yaml +6 -0
  79. imap_processing/mag/l0/decom_mag.py +10 -3
  80. imap_processing/mag/l1a/mag_l1a.py +22 -11
  81. imap_processing/mag/l1a/mag_l1a_data.py +28 -3
  82. imap_processing/mag/l1b/mag_l1b.py +190 -48
  83. imap_processing/mag/l1c/interpolation_methods.py +211 -0
  84. imap_processing/mag/l1c/mag_l1c.py +447 -9
  85. imap_processing/quality_flags.py +1 -0
  86. imap_processing/spacecraft/packet_definitions/scid_x252.xml +538 -0
  87. imap_processing/spacecraft/quaternions.py +123 -0
  88. imap_processing/spice/geometry.py +16 -19
  89. imap_processing/spice/repoint.py +120 -0
  90. imap_processing/swapi/l1/swapi_l1.py +4 -0
  91. imap_processing/swapi/l2/swapi_l2.py +0 -1
  92. imap_processing/swe/l1a/swe_l1a.py +47 -8
  93. imap_processing/swe/l1a/swe_science.py +5 -2
  94. imap_processing/swe/l1b/swe_l1b_science.py +103 -56
  95. imap_processing/swe/l2/swe_l2.py +60 -65
  96. imap_processing/swe/packet_definitions/swe_packet_definition.xml +1121 -1
  97. imap_processing/swe/utils/swe_constants.py +63 -0
  98. imap_processing/swe/utils/swe_utils.py +85 -28
  99. imap_processing/tests/ccsds/test_data/expected_output.xml +40 -1
  100. imap_processing/tests/ccsds/test_excel_to_xtce.py +23 -20
  101. imap_processing/tests/cdf/test_data/imap_instrument2_global_cdf_attrs.yaml +0 -2
  102. imap_processing/tests/codice/conftest.py +1 -1
  103. imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-counters-aggregated_20241110193700_v0.0.0.cdf +0 -0
  104. imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-counters-singles_20241110193700_v0.0.0.cdf +0 -0
  105. imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-ialirt_20241110193700_v0.0.0.cdf +0 -0
  106. imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-omni_20241110193700_v0.0.0.cdf +0 -0
  107. imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-pha_20241110193700_v0.0.0.cdf +0 -0
  108. imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-priorities_20241110193700_v0.0.0.cdf +0 -0
  109. imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-sectored_20241110193700_v0.0.0.cdf +0 -0
  110. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-counters-aggregated_20241110193700_v0.0.0.cdf +0 -0
  111. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-counters-singles_20241110193700_v0.0.0.cdf +0 -0
  112. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-ialirt_20241110193700_v0.0.0.cdf +0 -0
  113. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-nsw-angular_20241110193700_v0.0.0.cdf +0 -0
  114. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-nsw-priority_20241110193700_v0.0.0.cdf +0 -0
  115. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-nsw-species_20241110193700_v0.0.0.cdf +0 -0
  116. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-pha_20241110193700_v0.0.0.cdf +0 -0
  117. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-sw-angular_20241110193700_v0.0.0.cdf +0 -0
  118. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-sw-priority_20241110193700_v0.0.0.cdf +0 -0
  119. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-sw-species_20241110193700_v0.0.0.cdf +0 -0
  120. imap_processing/tests/codice/test_codice_l1a.py +110 -46
  121. imap_processing/tests/codice/test_decompress.py +4 -4
  122. imap_processing/tests/conftest.py +166 -10
  123. imap_processing/tests/ena_maps/conftest.py +51 -0
  124. imap_processing/tests/ena_maps/test_ena_maps.py +638 -109
  125. imap_processing/tests/ena_maps/test_map_utils.py +66 -43
  126. imap_processing/tests/ena_maps/test_spatial_utils.py +16 -20
  127. imap_processing/tests/hi/data/l0/H45_diag_fee_20250208.bin +0 -0
  128. imap_processing/tests/hi/data/l0/H45_diag_fee_20250208_verify.csv +205 -0
  129. imap_processing/tests/hi/test_hi_l1b.py +12 -15
  130. imap_processing/tests/hi/test_hi_l1c.py +234 -6
  131. imap_processing/tests/hi/test_l1a.py +30 -0
  132. imap_processing/tests/hi/test_science_direct_event.py +1 -1
  133. imap_processing/tests/hi/test_utils.py +24 -2
  134. imap_processing/tests/hit/helpers/l1_validation.py +39 -39
  135. imap_processing/tests/hit/test_data/hskp_sample.ccsds +0 -0
  136. imap_processing/tests/hit/test_data/imap_hit_l0_raw_20100105_v001.pkts +0 -0
  137. imap_processing/tests/hit/test_decom_hit.py +4 -0
  138. imap_processing/tests/hit/test_hit_l1a.py +24 -28
  139. imap_processing/tests/hit/test_hit_l1b.py +304 -40
  140. imap_processing/tests/hit/test_hit_l2.py +454 -0
  141. imap_processing/tests/hit/test_hit_utils.py +112 -2
  142. imap_processing/tests/hit/validation_data/hskp_sample_eu_3_6_2025.csv +89 -0
  143. imap_processing/tests/hit/validation_data/hskp_sample_raw.csv +89 -88
  144. imap_processing/tests/ialirt/test_data/l0/461971383-404.bin +0 -0
  145. imap_processing/tests/ialirt/test_data/l0/461971384-405.bin +0 -0
  146. imap_processing/tests/ialirt/test_data/l0/461971385-406.bin +0 -0
  147. imap_processing/tests/ialirt/test_data/l0/461971386-407.bin +0 -0
  148. imap_processing/tests/ialirt/test_data/l0/461971387-408.bin +0 -0
  149. imap_processing/tests/ialirt/test_data/l0/461971388-409.bin +0 -0
  150. imap_processing/tests/ialirt/test_data/l0/461971389-410.bin +0 -0
  151. imap_processing/tests/ialirt/test_data/l0/461971390-411.bin +0 -0
  152. imap_processing/tests/ialirt/test_data/l0/461971391-412.bin +0 -0
  153. imap_processing/tests/ialirt/test_data/l0/sample_decoded_i-alirt_data.csv +383 -0
  154. imap_processing/tests/ialirt/unit/test_grouping.py +81 -0
  155. imap_processing/tests/ialirt/unit/test_parse_mag.py +168 -0
  156. imap_processing/tests/ialirt/unit/test_process_swe.py +208 -3
  157. imap_processing/tests/ialirt/unit/test_time.py +16 -0
  158. imap_processing/tests/idex/conftest.py +62 -6
  159. imap_processing/tests/idex/test_data/imap_idex_l0_raw_20231218_v001.pkts +0 -0
  160. imap_processing/tests/idex/test_data/impact_14_tof_high_data.txt +4508 -4508
  161. imap_processing/tests/idex/test_idex_l1a.py +48 -4
  162. imap_processing/tests/idex/test_idex_l1b.py +3 -3
  163. imap_processing/tests/idex/test_idex_l2a.py +383 -0
  164. imap_processing/tests/lo/test_cdfs/imap_lo_l1a_de_20241022_v002.cdf +0 -0
  165. imap_processing/tests/lo/test_cdfs/imap_lo_l1a_spin_20241022_v002.cdf +0 -0
  166. imap_processing/tests/lo/test_lo_l1b.py +148 -4
  167. imap_processing/tests/lo/test_lo_science.py +1 -0
  168. imap_processing/tests/mag/conftest.py +69 -0
  169. imap_processing/tests/mag/test_mag_decom.py +1 -1
  170. imap_processing/tests/mag/test_mag_l1a.py +38 -0
  171. imap_processing/tests/mag/test_mag_l1b.py +34 -53
  172. imap_processing/tests/mag/test_mag_l1c.py +251 -20
  173. imap_processing/tests/mag/test_mag_validation.py +109 -25
  174. imap_processing/tests/mag/validation/L1b/T009/MAGScience-normal-(2,2)-8s-20250204-16h39.csv +17 -0
  175. imap_processing/tests/mag/validation/L1b/T009/mag-l1a-l1b-t009-magi-out.csv +16 -16
  176. imap_processing/tests/mag/validation/L1b/T009/mag-l1a-l1b-t009-mago-out.csv +16 -16
  177. imap_processing/tests/mag/validation/L1b/T010/MAGScience-normal-(2,2)-8s-20250206-12h05.csv +17 -0
  178. imap_processing/tests/mag/validation/L1b/T011/MAGScience-normal-(2,2)-8s-20250204-16h08.csv +17 -0
  179. imap_processing/tests/mag/validation/L1b/T011/mag-l1a-l1b-t011-magi-out.csv +16 -16
  180. imap_processing/tests/mag/validation/L1b/T011/mag-l1a-l1b-t011-mago-out.csv +16 -16
  181. imap_processing/tests/mag/validation/L1b/T012/MAGScience-normal-(2,2)-8s-20250204-16h08.csv +17 -0
  182. imap_processing/tests/mag/validation/L1b/T012/data.bin +0 -0
  183. imap_processing/tests/mag/validation/L1b/T012/field_like_all_ranges.txt +19200 -0
  184. imap_processing/tests/mag/validation/L1b/T012/mag-l1a-l1b-t012-cal.cdf +0 -0
  185. imap_processing/tests/mag/validation/L1b/T012/mag-l1a-l1b-t012-in.csv +17 -0
  186. imap_processing/tests/mag/validation/L1b/T012/mag-l1a-l1b-t012-magi-out.csv +17 -0
  187. imap_processing/tests/mag/validation/L1b/T012/mag-l1a-l1b-t012-mago-out.csv +17 -0
  188. imap_processing/tests/mag/validation/imap_calibration_mag_20240229_v01.cdf +0 -0
  189. imap_processing/tests/spacecraft/__init__.py +0 -0
  190. imap_processing/tests/spacecraft/data/SSR_2024_190_20_08_12_0483851794_2_DA_apid0594_1packet.pkts +0 -0
  191. imap_processing/tests/spacecraft/test_quaternions.py +71 -0
  192. imap_processing/tests/spice/test_data/fake_repoint_data.csv +5 -0
  193. imap_processing/tests/spice/test_geometry.py +6 -9
  194. imap_processing/tests/spice/test_repoint.py +111 -0
  195. imap_processing/tests/swapi/test_swapi_l1.py +7 -3
  196. imap_processing/tests/swe/l0_data/2024051010_SWE_HK_packet.bin +0 -0
  197. imap_processing/tests/swe/l0_data/2024051011_SWE_CEM_RAW_packet.bin +0 -0
  198. imap_processing/tests/swe/l0_validation_data/idle_export_eu.SWE_APP_HK_20240510_092742.csv +49 -0
  199. imap_processing/tests/swe/l0_validation_data/idle_export_eu.SWE_CEM_RAW_20240510_092742.csv +593 -0
  200. imap_processing/tests/swe/test_swe_l1a.py +18 -0
  201. imap_processing/tests/swe/test_swe_l1a_cem_raw.py +52 -0
  202. imap_processing/tests/swe/test_swe_l1a_hk.py +68 -0
  203. imap_processing/tests/swe/test_swe_l1b_science.py +23 -4
  204. imap_processing/tests/swe/test_swe_l2.py +112 -30
  205. imap_processing/tests/test_cli.py +2 -2
  206. imap_processing/tests/test_utils.py +138 -16
  207. imap_processing/tests/ultra/data/l0/FM45_UltraFM45_Functional_2024-01-22T0105_20240122T010548.CCSDS +0 -0
  208. imap_processing/tests/ultra/data/l0/ultra45_raw_sc_ultraimgrates_20220530_00.csv +164 -0
  209. imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_ultrarawimg_withFSWcalcs_FM45_40P_Phi28p5_BeamCal_LinearScan_phi2850_theta-000_20240207T102740.csv +3243 -3243
  210. imap_processing/tests/ultra/data/mock_data.py +341 -0
  211. imap_processing/tests/ultra/unit/conftest.py +69 -26
  212. imap_processing/tests/ultra/unit/test_badtimes.py +2 -0
  213. imap_processing/tests/ultra/unit/test_cullingmask.py +4 -0
  214. imap_processing/tests/ultra/unit/test_de.py +12 -4
  215. imap_processing/tests/ultra/unit/test_decom_apid_881.py +44 -0
  216. imap_processing/tests/ultra/unit/test_spacecraft_pset.py +78 -0
  217. imap_processing/tests/ultra/unit/test_ultra_l1a.py +28 -12
  218. imap_processing/tests/ultra/unit/test_ultra_l1b.py +34 -6
  219. imap_processing/tests/ultra/unit/test_ultra_l1b_culling.py +22 -26
  220. imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py +86 -51
  221. imap_processing/tests/ultra/unit/test_ultra_l1c_pset_bins.py +94 -52
  222. imap_processing/ultra/l0/decom_tools.py +6 -5
  223. imap_processing/ultra/l1a/ultra_l1a.py +28 -56
  224. imap_processing/ultra/l1b/de.py +72 -28
  225. imap_processing/ultra/l1b/extendedspin.py +12 -14
  226. imap_processing/ultra/l1b/ultra_l1b.py +34 -9
  227. imap_processing/ultra/l1b/ultra_l1b_culling.py +65 -29
  228. imap_processing/ultra/l1b/ultra_l1b_extended.py +64 -19
  229. imap_processing/ultra/l1c/spacecraft_pset.py +86 -0
  230. imap_processing/ultra/l1c/ultra_l1c.py +7 -4
  231. imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +112 -61
  232. imap_processing/ultra/lookup_tables/ultra_90_dps_exposure_compressed.cdf +0 -0
  233. imap_processing/ultra/utils/ultra_l1_utils.py +20 -2
  234. imap_processing/utils.py +68 -28
  235. {imap_processing-0.11.0.dist-info → imap_processing-0.12.0.dist-info}/METADATA +8 -5
  236. {imap_processing-0.11.0.dist-info → imap_processing-0.12.0.dist-info}/RECORD +250 -199
  237. imap_processing/cdf/config/imap_mag_l1_variable_attrs.yaml +0 -237
  238. imap_processing/hi/l1a/housekeeping.py +0 -27
  239. imap_processing/tests/codice/data/imap_codice_l1a_hi-counters-aggregated_20240429_v001.cdf +0 -0
  240. imap_processing/tests/codice/data/imap_codice_l1a_hi-counters-singles_20240429_v001.cdf +0 -0
  241. imap_processing/tests/codice/data/imap_codice_l1a_hi-omni_20240429_v001.cdf +0 -0
  242. imap_processing/tests/codice/data/imap_codice_l1a_hi-sectored_20240429_v001.cdf +0 -0
  243. imap_processing/tests/codice/data/imap_codice_l1a_hskp_20100101_v001.cdf +0 -0
  244. imap_processing/tests/codice/data/imap_codice_l1a_lo-counters-aggregated_20240429_v001.cdf +0 -0
  245. imap_processing/tests/codice/data/imap_codice_l1a_lo-counters-singles_20240429_v001.cdf +0 -0
  246. imap_processing/tests/codice/data/imap_codice_l1a_lo-nsw-angular_20240429_v001.cdf +0 -0
  247. imap_processing/tests/codice/data/imap_codice_l1a_lo-nsw-priority_20240429_v001.cdf +0 -0
  248. imap_processing/tests/codice/data/imap_codice_l1a_lo-nsw-species_20240429_v001.cdf +0 -0
  249. imap_processing/tests/codice/data/imap_codice_l1a_lo-sw-angular_20240429_v001.cdf +0 -0
  250. imap_processing/tests/codice/data/imap_codice_l1a_lo-sw-priority_20240429_v001.cdf +0 -0
  251. imap_processing/tests/codice/data/imap_codice_l1a_lo-sw-species_20240429_v001.cdf +0 -0
  252. imap_processing/tests/codice/data/imap_codice_l1b_hi-counters-aggregated_20240429_v001.cdf +0 -0
  253. imap_processing/tests/codice/data/imap_codice_l1b_hi-counters-singles_20240429_v001.cdf +0 -0
  254. imap_processing/tests/codice/data/imap_codice_l1b_hi-omni_20240429_v001.cdf +0 -0
  255. imap_processing/tests/codice/data/imap_codice_l1b_hi-sectored_20240429_v001.cdf +0 -0
  256. imap_processing/tests/codice/data/imap_codice_l1b_hskp_20100101_v001.cdf +0 -0
  257. imap_processing/tests/codice/data/imap_codice_l1b_lo-counters-aggregated_20240429_v001.cdf +0 -0
  258. imap_processing/tests/codice/data/imap_codice_l1b_lo-counters-singles_20240429_v001.cdf +0 -0
  259. imap_processing/tests/codice/data/imap_codice_l1b_lo-nsw-angular_20240429_v001.cdf +0 -0
  260. imap_processing/tests/codice/data/imap_codice_l1b_lo-nsw-priority_20240429_v001.cdf +0 -0
  261. imap_processing/tests/codice/data/imap_codice_l1b_lo-nsw-species_20240429_v001.cdf +0 -0
  262. imap_processing/tests/codice/data/imap_codice_l1b_lo-sw-angular_20240429_v001.cdf +0 -0
  263. imap_processing/tests/codice/data/imap_codice_l1b_lo-sw-priority_20240429_v001.cdf +0 -0
  264. imap_processing/tests/codice/data/imap_codice_l1b_lo-sw-species_20240429_v001.cdf +0 -0
  265. imap_processing/tests/hi/data/l1/imap_hi_l1b_45sensor-de_20250415_v999.cdf +0 -0
  266. imap_processing/tests/hit/PREFLIGHT_raw_record_2023_256_15_59_04_apid1251.pkts +0 -0
  267. imap_processing/tests/hit/PREFLIGHT_raw_record_2023_256_15_59_04_apid1252.pkts +0 -0
  268. imap_processing/tests/hit/validation_data/hskp_sample_eu.csv +0 -89
  269. imap_processing/tests/hit/validation_data/sci_sample_raw1.csv +0 -29
  270. imap_processing/tests/idex/test_data/imap_idex_l0_raw_20231214_v001.pkts +0 -0
  271. imap_processing/tests/lo/test_cdfs/imap_lo_l1a_de_20100101_v001.cdf +0 -0
  272. imap_processing/tests/lo/test_cdfs/imap_lo_l1a_spin_20100101_v001.cdf +0 -0
  273. imap_processing/tests/ultra/test_data/mock_data.py +0 -161
  274. imap_processing/ultra/l1c/pset.py +0 -40
  275. /imap_processing/tests/ultra/{test_data → data}/l0/FM45_40P_Phi28p5_BeamCal_LinearScan_phi28.50_theta-0.00_20240207T102740.CCSDS +0 -0
  276. /imap_processing/tests/ultra/{test_data → data}/l0/FM45_7P_Phi0.0_BeamCal_LinearScan_phi0.04_theta-0.01_20230821T121304.CCSDS +0 -0
  277. /imap_processing/tests/ultra/{test_data → data}/l0/FM45_TV_Cycle6_Hot_Ops_Front212_20240124T063837.CCSDS +0 -0
  278. /imap_processing/tests/ultra/{test_data → data}/l0/Ultra45_EM_SwRI_Cal_Run7_ThetaScan_20220530T225054.CCSDS +0 -0
  279. /imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_auxdata_Ultra45_EM_SwRI_Cal_Run7_ThetaScan_20220530T225054.csv +0 -0
  280. /imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_enaphxtofhangimg_FM45_TV_Cycle6_Hot_Ops_Front212_20240124T063837.csv +0 -0
  281. /imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_ultraimgrates_Ultra45_EM_SwRI_Cal_Run7_ThetaScan_20220530T225054.csv +0 -0
  282. /imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_ultrarawimgevent_FM45_7P_Phi00_BeamCal_LinearScan_phi004_theta-001_20230821T121304.csv +0 -0
  283. /imap_processing/tests/ultra/{test_data → data}/l1/dps_exposure_helio_45_E1.cdf +0 -0
  284. /imap_processing/tests/ultra/{test_data → data}/l1/dps_exposure_helio_45_E12.cdf +0 -0
  285. /imap_processing/tests/ultra/{test_data → data}/l1/dps_exposure_helio_45_E24.cdf +0 -0
  286. {imap_processing-0.11.0.dist-info → imap_processing-0.12.0.dist-info}/LICENSE +0 -0
  287. {imap_processing-0.11.0.dist-info → imap_processing-0.12.0.dist-info}/WHEEL +0 -0
  288. {imap_processing-0.11.0.dist-info → imap_processing-0.12.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,614 @@
1
+ """IMAP-HIT L2 data processing."""
2
+
3
+ import logging
4
+ from pathlib import Path
5
+ from typing import NamedTuple
6
+
7
+ import numpy as np
8
+ import pandas as pd
9
+ import xarray as xr
10
+
11
+ from imap_processing.hit.hit_utils import (
12
+ add_energy_variables,
13
+ get_attribute_manager,
14
+ initialize_particle_data_arrays,
15
+ sum_particle_data,
16
+ )
17
+ from imap_processing.hit.l2.constants import (
18
+ L2_STANDARD_ANCILLARY_PATH_PREFIX,
19
+ L2_SUMMED_ANCILLARY_PATH_PREFIX,
20
+ STANDARD_PARTICLE_ENERGY_RANGE_MAPPING,
21
+ )
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+ # TODO:
26
+ # - review logging levels to use (debug vs. info)
27
+ # - determine where to pull ancillary data. Storing it locally for now
28
+
29
+
30
+ def hit_l2(dependency: xr.Dataset, data_version: str) -> list[xr.Dataset]:
31
+ """
32
+ Will process HIT data to L2.
33
+
34
+ Processes dependencies needed to create L2 data products.
35
+
36
+ Parameters
37
+ ----------
38
+ dependency : xr.Dataset
39
+ L1B xarray science dataset that is either summed rates
40
+ standard rates or sector rates.
41
+ data_version : str
42
+ Version of the data product being created.
43
+
44
+ Returns
45
+ -------
46
+ processed_data : list[xarray.Dataset]
47
+ List of one L2 dataset.
48
+ """
49
+ logger.info("Creating HIT L2 science datasets")
50
+ # Create the attribute manager for this data level
51
+ attr_mgr = get_attribute_manager(data_version, "l2")
52
+
53
+ # TODO: Write functions to process sectored rates dataset
54
+ # with logical source: "imap_hit_l2_macropixel-intensity"
55
+
56
+ l2_datasets: dict = {}
57
+
58
+ # Process science data to L2 datasets
59
+ if "imap_hit_l1b_summed-rates" in dependency.attrs["Logical_source"]:
60
+ l2_datasets["imap_hit_l2_summed-intensity"] = process_summed_intensity_data(
61
+ dependency
62
+ )
63
+ logger.info("HIT L2 summed intensity dataset created")
64
+
65
+ if "imap_hit_l1b_standard-rates" in dependency.attrs["Logical_source"]:
66
+ l2_datasets["imap_hit_l2_standard-intensity"] = process_standard_intensity_data(
67
+ dependency
68
+ )
69
+ logger.info("HIT L2 standard intensity dataset created")
70
+
71
+ # Update attributes and dimensions
72
+ for logical_source, dataset in l2_datasets.items():
73
+ dataset.attrs = attr_mgr.get_global_attributes(logical_source)
74
+
75
+ # TODO: Add CDF attributes to yaml once they're defined for L2 science data
76
+ # consider moving attribute handling to hit_utils.py
77
+ # Assign attributes and dimensions to each data array in the Dataset
78
+ for field in dataset.data_vars.keys():
79
+ try:
80
+ # Create a dict of dimensions using the DEPEND_I keys in the
81
+ # attributes
82
+ dims = {
83
+ key: value
84
+ for key, value in attr_mgr.get_variable_attributes(field).items()
85
+ if "DEPEND" in key
86
+ }
87
+ dataset[field].attrs = attr_mgr.get_variable_attributes(field)
88
+ dataset[field].assign_coords(dims)
89
+ except KeyError:
90
+ # TODO: consider raising an error after L2 attributes are defined.
91
+ # Until then, continue with processing and log warning
92
+ logger.warning(f"Field {field} not found in attribute manager.")
93
+
94
+ # Skip schema check for epoch to prevent attr_mgr from adding the
95
+ # DEPEND_0 attribute which isn't required for epoch
96
+ dataset.epoch.attrs = attr_mgr.get_variable_attributes(
97
+ "epoch", check_schema=False
98
+ )
99
+
100
+ logger.info(f"HIT L2 dataset created for {logical_source}")
101
+
102
+ return list(l2_datasets.values())
103
+
104
+
105
+ class IntensityFactors(NamedTuple):
106
+ """A namedtuple to store factors for the intensity equation."""
107
+
108
+ delta_e_factor: np.ndarray
109
+ geometry_factor: np.ndarray
110
+ efficiency: np.ndarray
111
+ b: np.ndarray
112
+
113
+
114
+ def get_intensity_factors(
115
+ energy_min: np.ndarray, species_ancillary_data: pd.DataFrame
116
+ ) -> IntensityFactors:
117
+ """
118
+ Get the intensity factors for all energy bins of the given species ancillary data.
119
+
120
+ This function gets the factors needed for the equation to convert rates to
121
+ intensities for all energy bins for the given species.
122
+
123
+ Parameters
124
+ ----------
125
+ energy_min : np.ndarray
126
+ All energy min values for the species.
127
+ species_ancillary_data : pd.DataFrame
128
+ The subset of ancillary data for the given species.
129
+
130
+ Returns
131
+ -------
132
+ IntensityFactors
133
+ The factors needed to convert rates to intensities for all energy bins
134
+ for the given species.
135
+ """
136
+ # Get factors needed to convert rates to intensities for
137
+ # all energy bins for the given species ancillary data
138
+ intensity_factors = species_ancillary_data.set_index(
139
+ species_ancillary_data["lower energy (mev)"].astype(np.float32)
140
+ ).loc[energy_min]
141
+
142
+ return IntensityFactors(
143
+ delta_e_factor=intensity_factors["delta e (mev)"].values,
144
+ geometry_factor=intensity_factors["geometry factor (cm2 sr)"].values,
145
+ efficiency=intensity_factors["efficiency"].values,
146
+ b=intensity_factors["b"].values,
147
+ )
148
+
149
+
150
+ def calculate_intensities(
151
+ rate: xr.DataArray,
152
+ delta_e_factor: np.ndarray,
153
+ geometry_factor: np.ndarray,
154
+ efficiency: np.ndarray,
155
+ b: np.ndarray,
156
+ ) -> xr.DataArray:
157
+ """
158
+ Calculate the intensities for given arrays of rates and ancillary factors.
159
+
160
+ Uses vectorization to calculate the intensities for an array of rates
161
+ at an epoch.
162
+
163
+ This function uses equation 9 and 11 from the HIT algorithm document:
164
+ (Summed L1B Rates) / (60 * Delta E * Geometry Factor * Efficiency) - b
165
+
166
+ Parameters
167
+ ----------
168
+ rate : xr.DataArray
169
+ The L1B rates to be converted to intensities for an epoch.
170
+ delta_e_factor : np.ndarray
171
+ The energy bin width factors for an epoch.
172
+ geometry_factor : np.ndarray
173
+ The geometry factors for an epoch.
174
+ efficiency : np.ndarray
175
+ The efficiency factors for an epoch.
176
+ b : np.ndarray
177
+ The b factors for an epoch.
178
+
179
+ Returns
180
+ -------
181
+ xr.DataArray
182
+ The calculated intensities for an epoch.
183
+ """
184
+ # Calculate the intensities for this energy bin using vectorization
185
+ return (rate / (60 * delta_e_factor * geometry_factor * efficiency)) - b
186
+
187
+
188
+ def calculate_intensities_for_a_species(
189
+ species_variable: str, l2_dataset: xr.Dataset, ancillary_data_frames: dict
190
+ ) -> None:
191
+ """
192
+ Calculate the intensity for a given species in the dataset.
193
+
194
+ Parameters
195
+ ----------
196
+ species_variable : str
197
+ The species variable to calculate the intensity for which is either the species
198
+ or a statistical uncertainty. (i.e. "h", "h_delta_minus", or "h_delta_plus").
199
+ l2_dataset : xr.Dataset
200
+ The L2 dataset containing the summed L1B rates to calculate the intensity.
201
+ ancillary_data_frames : dict
202
+ Dictionary containing ancillary data for each dynamic threshold state where
203
+ the key is the dynamic threshold state and the value is a pandas DataFrame
204
+ containing the ancillary data.
205
+ """
206
+ species = (
207
+ species_variable.split("_")[0]
208
+ if "_delta_" in species_variable
209
+ else species_variable
210
+ )
211
+ energy_min = (
212
+ l2_dataset[f"{species}_energy_mean"].values
213
+ - l2_dataset[f"{species}_energy_delta_minus"].values
214
+ )
215
+ # TODO: Add check for energy max after ancillary file is updated
216
+ # to fix errors
217
+
218
+ # Calculate the intensity for each epoch and energy bin since the
219
+ # dynamic threshold state can vary by epoch and that determines the
220
+ # ancillary data to use.
221
+ for epoch in range(l2_dataset[species_variable].shape[0]):
222
+ # Get ancillary data using the dynamic threshold state for this epoch
223
+ species_ancillary_data = get_species_ancillary_data(
224
+ int(l2_dataset["dynamic_threshold_state"][epoch].values),
225
+ ancillary_data_frames,
226
+ species,
227
+ )
228
+
229
+ # Calculate the intensity for this energy bin using vectorization
230
+ # and replace rates with intensities in the dataset
231
+ factors: IntensityFactors = get_intensity_factors(
232
+ energy_min, species_ancillary_data
233
+ )
234
+ rates: xr.DataArray = l2_dataset[species_variable][epoch]
235
+
236
+ l2_dataset[species_variable][epoch] = calculate_intensities(
237
+ rates,
238
+ factors.delta_e_factor,
239
+ factors.geometry_factor,
240
+ factors.efficiency,
241
+ factors.b,
242
+ )
243
+
244
+
245
+ def calculate_intensities_for_all_species(
246
+ l2_dataset: xr.Dataset, ancillary_data_frames: dict
247
+ ) -> None:
248
+ """
249
+ Calculate the intensity for each species in the dataset.
250
+
251
+ Parameters
252
+ ----------
253
+ l2_dataset : xr.Dataset
254
+ The L2 dataset.
255
+ ancillary_data_frames : dict
256
+ Dictionary containing ancillary data for each dynamic threshold state
257
+ where the key is the dynamic threshold state and the value is a pandas
258
+ DataFrame containing the ancillary data.
259
+ """
260
+ # TODO: update to also calculate intensity for sectorates?
261
+ # List of valid species data variables to calculate intensity for
262
+ valid_data_variables = [
263
+ "h",
264
+ "he3",
265
+ "he4",
266
+ "he",
267
+ "c",
268
+ "n",
269
+ "o",
270
+ "ne",
271
+ "na",
272
+ "mg",
273
+ "al",
274
+ "si",
275
+ "s",
276
+ "ar",
277
+ "ca",
278
+ "fe",
279
+ "ni",
280
+ ]
281
+
282
+ # Add statistical uncertainty variables to the list of valid variables
283
+ valid_data_variables += [f"{var}_delta_minus" for var in valid_data_variables] + [
284
+ f"{var}_delta_plus" for var in valid_data_variables
285
+ ]
286
+
287
+ # Calculate the intensity for each valid data variable
288
+ for species_variable in valid_data_variables:
289
+ if species_variable in l2_dataset.data_vars:
290
+ calculate_intensities_for_a_species(
291
+ species_variable, l2_dataset, ancillary_data_frames
292
+ )
293
+ else:
294
+ logger.warning(
295
+ f"Variable {species_variable} not found in dataset. "
296
+ f"Skipping intensity calculation."
297
+ )
298
+
299
+
300
+ def add_systematic_uncertainties(
301
+ dataset: xr.Dataset, particle: str, energy_bins: int
302
+ ) -> None:
303
+ """
304
+ Add systematic uncertainties to the dataset.
305
+
306
+ Add systematic uncertainties to the dataset. Just zeros for now.
307
+ To change if/when HIT determines there are systematic uncertainties.
308
+
309
+ Parameters
310
+ ----------
311
+ dataset : xr.Dataset
312
+ The dataset to add the systematic uncertainties to.
313
+ particle : str
314
+ The particle name.
315
+ energy_bins : int
316
+ Number of energy bins for the particle.
317
+ """
318
+ dataset[f"{particle}_sys_delta_minus"] = xr.DataArray(
319
+ data=np.zeros(energy_bins, dtype=np.float32),
320
+ dims=[f"{particle}_energy_mean"],
321
+ name=f"{particle}_sys_delta_minus",
322
+ )
323
+ dataset[f"{particle}_sys_delta_plus"] = xr.DataArray(
324
+ data=np.zeros(energy_bins, dtype=np.float32),
325
+ dims=[f"{particle}_energy_mean"],
326
+ name=f"{particle}_sys_delta_plus",
327
+ )
328
+
329
+
330
+ def add_standard_particle_rates_to_dataset(
331
+ l2_standard_intensity_dataset: xr.Dataset,
332
+ l1b_standard_rates_dataset: xr.Dataset,
333
+ particle: str,
334
+ energy_ranges: list,
335
+ ) -> None:
336
+ """
337
+ Add summed standard particle rates to the dataset.
338
+
339
+ This function performs the following steps:
340
+ 1) sum the standard rates, including statistical uncertainties,
341
+ from the l2fgrates, l3fgrates, and penfgrates data variables in the L1B
342
+ standard rates data.
343
+ 2) add the summed rates to the L2 standard intensity dataset by particle type
344
+ and energy range.
345
+
346
+ Parameters
347
+ ----------
348
+ l2_standard_intensity_dataset : xr.Dataset
349
+ The L2 standard intensity dataset to add the rates to.
350
+ l1b_standard_rates_dataset : xr.Dataset
351
+ The L1B standard rates dataset containing rates to sum.
352
+ particle : str
353
+ The particle name.
354
+ energy_ranges : list
355
+ A list of energy range dictionaries for the particle.
356
+ For example:
357
+ {'energy_min': 1.8, 'energy_max': 2.2, "R2": [1], "R3": [], "R4": []}.
358
+ """
359
+ # Initialize arrays to store summed rates and statistical uncertainties
360
+ l2_standard_intensity_dataset = initialize_particle_data_arrays(
361
+ l2_standard_intensity_dataset,
362
+ particle,
363
+ len(energy_ranges),
364
+ l1b_standard_rates_dataset.sizes["epoch"],
365
+ )
366
+
367
+ # initialize arrays to store energy min and max values
368
+ energy_min = np.zeros(len(energy_ranges), dtype=np.float32)
369
+ energy_max = np.zeros(len(energy_ranges), dtype=np.float32)
370
+
371
+ # Sum particle rates and statistical uncertainties for each energy range
372
+ # and add them to the dataset
373
+ for i, energy_range_dict in enumerate(energy_ranges):
374
+ summed_rates, summed_rates_delta_minus, summed_rates_delta_plus = (
375
+ sum_particle_data(l1b_standard_rates_dataset, energy_range_dict)
376
+ )
377
+
378
+ l2_standard_intensity_dataset[f"{particle}"][:, i] = summed_rates.astype(
379
+ np.float32
380
+ )
381
+ l2_standard_intensity_dataset[f"{particle}_delta_minus"][:, i] = (
382
+ summed_rates_delta_minus.astype(np.float32)
383
+ )
384
+ l2_standard_intensity_dataset[f"{particle}_delta_plus"][:, i] = (
385
+ summed_rates_delta_plus.astype(np.float32)
386
+ )
387
+
388
+ # Fill energy min and max values for each energy range
389
+ energy_min[i] = energy_range_dict["energy_min"]
390
+ energy_max[i] = energy_range_dict["energy_max"]
391
+
392
+ l2_standard_intensity_dataset = add_energy_variables(
393
+ l2_standard_intensity_dataset, particle, energy_min, energy_max
394
+ )
395
+
396
+
397
+ def get_species_ancillary_data(
398
+ dynamic_threshold_state: int, ancillary_data_frames: dict, species: str
399
+ ) -> pd.DataFrame:
400
+ """
401
+ Get the ancillary data for a given species and dynamic threshold state.
402
+
403
+ Parameters
404
+ ----------
405
+ dynamic_threshold_state : int
406
+ The dynamic threshold state for the ancillary data (0-3).
407
+ ancillary_data_frames : dict
408
+ Dictionary containing ancillary data for each dynamic threshold state
409
+ where the key is the dynamic threshold state and the value is a pandas
410
+ DataFrame containing the ancillary data.
411
+ species : str
412
+ The species to get the ancillary data for.
413
+
414
+ Returns
415
+ -------
416
+ pd.DataFrame
417
+ The ancillary data for the species and dynamic threshold state.
418
+ """
419
+ ancillary_data = ancillary_data_frames[dynamic_threshold_state]
420
+
421
+ # Get the ancillary data for the species
422
+ species_ancillary_data = ancillary_data[ancillary_data["species"] == species]
423
+ return species_ancillary_data
424
+
425
+
426
+ def load_ancillary_data(dynamic_threshold_states: set, path_prefix: Path) -> dict:
427
+ """
428
+ Load ancillary data based on the dynamic threshold state.
429
+
430
+ The dynamic threshold state (0-3) determines which ancillary file to use.
431
+ This function returns a dictionary with ancillary data for each state in
432
+ the dataset.
433
+
434
+ Parameters
435
+ ----------
436
+ dynamic_threshold_states : set
437
+ A set of dynamic threshold states in the L2 dataset.
438
+ path_prefix : Path
439
+ The path prefix for ancillary data files.
440
+
441
+ Returns
442
+ -------
443
+ dict
444
+ A dictionary with ancillary data for each dynamic threshold state.
445
+ """
446
+ # Load ancillary data
447
+ ancillary_data_frames = {
448
+ int(state): pd.read_csv(f"{path_prefix}{state}-factors_20250219_v002.csv")
449
+ for state in dynamic_threshold_states
450
+ }
451
+
452
+ # Convert column names and species values to lowercase
453
+ for df in ancillary_data_frames.values():
454
+ df.columns = df.columns.str.lower().str.strip()
455
+ df["species"] = df["species"].str.lower()
456
+
457
+ return ancillary_data_frames
458
+
459
+
460
+ def process_summed_intensity_data(l1b_summed_rates_dataset: xr.Dataset) -> xr.Dataset:
461
+ """
462
+ Will process L2 HIT summed intensity data from L1B summed rates.
463
+
464
+ This function converts the L1B summed rates to L2 summed intensities
465
+ using ancillary tables containing factors needed to calculate the
466
+ intensity (energy bin width, geometry factor, efficiency, and b).
467
+
468
+ Equation 12 from the HIT algorithm document:
469
+ Summed Intensity = (L1B Summed Rate) /
470
+ (60 * Delta E * Geometry Factor * Efficiency) - b
471
+
472
+ Parameters
473
+ ----------
474
+ l1b_summed_rates_dataset : xarray.Dataset
475
+ HIT L1B summed rates dataset.
476
+
477
+ Returns
478
+ -------
479
+ xr.Dataset
480
+ The processed L2 summed intensity dataset.
481
+ """
482
+ # Create a new dataset to store the L2 summed intensity data
483
+ l2_summed_intensity_dataset = l1b_summed_rates_dataset.copy(deep=True)
484
+
485
+ # Load ancillary data for each dynamic threshold state into a dictionary
486
+ ancillary_data_frames = load_ancillary_data(
487
+ set(l2_summed_intensity_dataset["dynamic_threshold_state"].values),
488
+ L2_SUMMED_ANCILLARY_PATH_PREFIX,
489
+ )
490
+
491
+ # Add systematic uncertainties and energy variables to the dataset
492
+ for var in l2_summed_intensity_dataset.data_vars:
493
+ if "_" not in var:
494
+ particle = str(var)
495
+ # Add systematic uncertainties to the dataset. These will not have the
496
+ # intensity calculation applied to them and values will be zeros
497
+ add_systematic_uncertainties(
498
+ l2_summed_intensity_dataset,
499
+ particle,
500
+ l2_summed_intensity_dataset[var].shape[1],
501
+ )
502
+
503
+ # TODO: remove this code after L1B is updated to have energy mean and deltas
504
+ # instead of energy index, min, and max
505
+ # Add energy variables to the dataset (energy mean and deltas)
506
+ l2_summed_intensity_dataset = add_energy_variables(
507
+ l2_summed_intensity_dataset,
508
+ particle,
509
+ l2_summed_intensity_dataset[f"{particle}_energy_min"].values,
510
+ l2_summed_intensity_dataset[f"{particle}_energy_max"].values,
511
+ )
512
+
513
+ # Replace energy index with energy mean as a coordinate
514
+ l2_summed_intensity_dataset = l2_summed_intensity_dataset.assign_coords(
515
+ {
516
+ f"{particle}_energy_mean": (
517
+ f"{particle}_energy_index",
518
+ l2_summed_intensity_dataset[f"{particle}_energy_mean"].values,
519
+ )
520
+ }
521
+ ).swap_dims({f"{particle}_energy_index": f"{particle}_energy_mean"})
522
+
523
+ # Drop energy min, max, and index variables
524
+ l2_summed_intensity_dataset = l2_summed_intensity_dataset.drop_vars(
525
+ [
526
+ f"{particle}_energy_min",
527
+ f"{particle}_energy_max",
528
+ f"{particle}_energy_index",
529
+ ]
530
+ )
531
+
532
+ calculate_intensities_for_all_species(
533
+ l2_summed_intensity_dataset, ancillary_data_frames
534
+ )
535
+
536
+ return l2_summed_intensity_dataset
537
+
538
+
539
+ def process_standard_intensity_data(
540
+ l1b_standard_rates_dataset: xr.Dataset,
541
+ ) -> xr.Dataset:
542
+ """
543
+ Will process L2 standard intensity data from L1B standard rates data.
544
+
545
+ This function converts L1B standard rates to L2 standard intensities for each
546
+ particle type and energy range using ancillary tables containing factors
547
+ needed to calculate the intensity (energy bin width, geometry factor, efficiency
548
+ and b).
549
+
550
+ First, rates from the l2fgrates, l3fgrates, and penfgrates data variables
551
+ in the L1B standard rates data are summed. These variables represent rates
552
+ for different detector penetration ranges (Range 2, Range 3, and Range 4
553
+ respectively). Only the energy ranges specified in the
554
+ STANDARD_PARTICLE_ENERGY_RANGE_MAPPING dictionary are included in this
555
+ product.
556
+
557
+ Intensity is then calculated from the summed rates using the following equation:
558
+
559
+ Equation 10 from the HIT algorithm document:
560
+ Standard Intensity = (Summed L1B Standard Rates) /
561
+ (60 * Delta E * Geometry Factor * Efficiency) - b
562
+
563
+ Parameters
564
+ ----------
565
+ l1b_standard_rates_dataset : xr.Dataset
566
+ The L1B standard rates dataset.
567
+
568
+ Returns
569
+ -------
570
+ xr.Dataset
571
+ The L2 standard intensity dataset.
572
+ """
573
+ # Create a new dataset to store the L2 standard intensity data
574
+ l2_standard_intensity_dataset = xr.Dataset()
575
+
576
+ # Assign the epoch coordinate from the l1B dataset
577
+ l2_standard_intensity_dataset = l2_standard_intensity_dataset.assign_coords(
578
+ {"epoch": l1b_standard_rates_dataset.coords["epoch"]}
579
+ )
580
+
581
+ # Add dynamic threshold state to the dataset
582
+ l2_standard_intensity_dataset["dynamic_threshold_state"] = (
583
+ l1b_standard_rates_dataset["dynamic_threshold_state"]
584
+ )
585
+ l2_standard_intensity_dataset[
586
+ "dynamic_threshold_state"
587
+ ].attrs = l1b_standard_rates_dataset["dynamic_threshold_state"].attrs
588
+
589
+ # Load ancillary data for each dynamic threshold state into a dictionary
590
+ ancillary_data_frames = load_ancillary_data(
591
+ set(l2_standard_intensity_dataset["dynamic_threshold_state"].values),
592
+ L2_STANDARD_ANCILLARY_PATH_PREFIX,
593
+ )
594
+
595
+ # Process each particle type and energy range and add rates and uncertainties
596
+ # to the dataset
597
+ for particle, energy_ranges in STANDARD_PARTICLE_ENERGY_RANGE_MAPPING.items():
598
+ # Add systematic uncertainties to the dataset. These will not have the intensity
599
+ # calculation applied to them and values will be zeros
600
+ add_systematic_uncertainties(
601
+ l2_standard_intensity_dataset, particle, len(energy_ranges)
602
+ )
603
+ # Add standard particle rates and statistical uncertainties to the dataset
604
+ add_standard_particle_rates_to_dataset(
605
+ l2_standard_intensity_dataset,
606
+ l1b_standard_rates_dataset,
607
+ particle,
608
+ energy_ranges,
609
+ )
610
+ calculate_intensities_for_all_species(
611
+ l2_standard_intensity_dataset, ancillary_data_frames
612
+ )
613
+
614
+ return l2_standard_intensity_dataset