imap-processing 0.7.0__py3-none-any.whl → 0.8.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 (124) hide show
  1. imap_processing/__init__.py +1 -1
  2. imap_processing/_version.py +2 -2
  3. imap_processing/ccsds/excel_to_xtce.py +34 -2
  4. imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +1 -1
  5. imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +145 -30
  6. imap_processing/cdf/config/imap_glows_l1b_variable_attrs.yaml +36 -36
  7. imap_processing/cdf/config/imap_hi_variable_attrs.yaml +36 -8
  8. imap_processing/cdf/config/imap_hit_l1b_variable_attrs.yaml +9 -0
  9. imap_processing/cdf/config/imap_idex_global_cdf_attrs.yaml +7 -7
  10. imap_processing/cdf/config/imap_idex_l1a_variable_attrs.yaml +32 -33
  11. imap_processing/cdf/config/imap_mag_l1_variable_attrs.yaml +24 -28
  12. imap_processing/cdf/config/imap_ultra_l1a_variable_attrs.yaml +1 -0
  13. imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +133 -78
  14. imap_processing/cdf/config/imap_variable_schema.yaml +13 -0
  15. imap_processing/cdf/imap_cdf_manager.py +31 -27
  16. imap_processing/cli.py +12 -10
  17. imap_processing/codice/codice_l1a.py +151 -61
  18. imap_processing/codice/constants.py +1 -1
  19. imap_processing/codice/decompress.py +4 -9
  20. imap_processing/codice/utils.py +1 -0
  21. imap_processing/glows/l1b/glows_l1b.py +3 -3
  22. imap_processing/glows/l1b/glows_l1b_data.py +59 -37
  23. imap_processing/glows/l2/glows_l2_data.py +123 -0
  24. imap_processing/hi/l1a/histogram.py +1 -1
  25. imap_processing/hi/l1a/science_direct_event.py +1 -1
  26. imap_processing/hi/l1b/hi_l1b.py +85 -11
  27. imap_processing/hi/l1c/hi_l1c.py +23 -1
  28. imap_processing/hi/utils.py +1 -1
  29. imap_processing/hit/hit_utils.py +221 -0
  30. imap_processing/hit/l0/constants.py +118 -0
  31. imap_processing/hit/l0/decom_hit.py +186 -153
  32. imap_processing/hit/l1a/hit_l1a.py +20 -175
  33. imap_processing/hit/l1b/hit_l1b.py +33 -153
  34. imap_processing/idex/idex_l1a.py +10 -9
  35. imap_processing/lo/l0/decompression_tables/decompression_tables.py +1 -1
  36. imap_processing/lo/l0/lo_science.py +1 -1
  37. imap_processing/lo/packet_definitions/lo_xtce.xml +1 -3296
  38. imap_processing/mag/l0/decom_mag.py +4 -3
  39. imap_processing/mag/l1a/mag_l1a.py +11 -11
  40. imap_processing/mag/l1b/mag_l1b.py +89 -7
  41. imap_processing/spice/geometry.py +126 -4
  42. imap_processing/swapi/l1/swapi_l1.py +1 -1
  43. imap_processing/swapi/l2/swapi_l2.py +1 -1
  44. imap_processing/swe/l1b/swe_l1b_science.py +8 -8
  45. imap_processing/tests/ccsds/test_data/expected_output.xml +1 -0
  46. imap_processing/tests/ccsds/test_excel_to_xtce.py +4 -4
  47. imap_processing/tests/cdf/test_imap_cdf_manager.py +0 -10
  48. imap_processing/tests/codice/conftest.py +1 -17
  49. imap_processing/tests/codice/data/imap_codice_l0_raw_20241110_v001.pkts +0 -0
  50. imap_processing/tests/codice/test_codice_l0.py +8 -2
  51. imap_processing/tests/codice/test_codice_l1a.py +127 -107
  52. imap_processing/tests/codice/test_codice_l1b.py +1 -0
  53. imap_processing/tests/codice/test_decompress.py +7 -7
  54. imap_processing/tests/conftest.py +54 -15
  55. imap_processing/tests/glows/conftest.py +6 -0
  56. imap_processing/tests/glows/test_glows_l1b.py +9 -9
  57. imap_processing/tests/glows/test_glows_l1b_data.py +9 -9
  58. imap_processing/tests/glows/test_glows_l2_data.py +0 -0
  59. imap_processing/tests/hi/test_data/l1a/imap_hi_l1a_45sensor-de_20250415_v000.cdf +0 -0
  60. imap_processing/tests/hi/test_hi_l1b.py +71 -1
  61. imap_processing/tests/hi/test_hi_l1c.py +10 -2
  62. imap_processing/tests/hi/test_utils.py +4 -3
  63. imap_processing/tests/hit/{test_hit_decom.py → test_decom_hit.py} +84 -35
  64. imap_processing/tests/hit/test_hit_l1a.py +2 -197
  65. imap_processing/tests/hit/test_hit_l1b.py +156 -25
  66. imap_processing/tests/hit/test_hit_utils.py +218 -0
  67. imap_processing/tests/idex/conftest.py +1 -1
  68. imap_processing/tests/idex/imap_idex_l0_raw_20231214_v001.pkts +0 -0
  69. imap_processing/tests/idex/impact_14_tof_high_data.txt +4444 -4444
  70. imap_processing/tests/idex/test_idex_l0.py +3 -3
  71. imap_processing/tests/idex/test_idex_l1a.py +1 -1
  72. imap_processing/tests/lo/test_lo_science.py +2 -2
  73. imap_processing/tests/mag/imap_mag_l1a_norm-magi_20251017_v001.cdf +0 -0
  74. imap_processing/tests/mag/test_mag_l1b.py +59 -3
  75. imap_processing/tests/spice/test_data/imap_ena_sim_metakernel.template +3 -1
  76. imap_processing/tests/spice/test_geometry.py +84 -4
  77. imap_processing/tests/swe/conftest.py +33 -0
  78. imap_processing/tests/swe/l1_validation/swe_l0_unpacked-data_20240510_v001_VALIDATION_L1B_v3.dat +4332 -0
  79. imap_processing/tests/swe/test_swe_l1b.py +29 -8
  80. imap_processing/tests/test_utils.py +1 -1
  81. imap_processing/tests/ultra/test_data/l1/dps_exposure_helio_45_E12.cdf +0 -0
  82. imap_processing/tests/ultra/test_data/l1/dps_exposure_helio_45_E24.cdf +0 -0
  83. imap_processing/tests/ultra/unit/test_de.py +108 -0
  84. imap_processing/tests/ultra/unit/test_ultra_l1b.py +27 -3
  85. imap_processing/tests/ultra/unit/test_ultra_l1b_annotated.py +31 -10
  86. imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py +21 -11
  87. imap_processing/tests/ultra/unit/test_ultra_l1c_pset_bins.py +9 -44
  88. imap_processing/ultra/constants.py +8 -3
  89. imap_processing/ultra/l1b/de.py +174 -30
  90. imap_processing/ultra/l1b/ultra_l1b_annotated.py +24 -10
  91. imap_processing/ultra/l1b/ultra_l1b_extended.py +21 -14
  92. imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +70 -119
  93. {imap_processing-0.7.0.dist-info → imap_processing-0.8.0.dist-info}/METADATA +15 -14
  94. {imap_processing-0.7.0.dist-info → imap_processing-0.8.0.dist-info}/RECORD +98 -113
  95. imap_processing/cdf/cdf_attribute_manager.py +0 -322
  96. imap_processing/cdf/config/shared/default_global_cdf_attrs_schema.yaml +0 -246
  97. imap_processing/cdf/config/shared/default_variable_cdf_attrs_schema.yaml +0 -466
  98. imap_processing/hit/l0/data_classes/housekeeping.py +0 -240
  99. imap_processing/hit/l0/data_classes/science_packet.py +0 -259
  100. imap_processing/hit/l0/utils/hit_base.py +0 -57
  101. imap_processing/tests/cdf/shared/default_global_cdf_attrs_schema.yaml +0 -246
  102. imap_processing/tests/cdf/shared/default_variable_cdf_attrs_schema.yaml +0 -466
  103. imap_processing/tests/cdf/test_cdf_attribute_manager.py +0 -353
  104. imap_processing/tests/codice/data/imap_codice_l0_hi-counters-aggregated_20240429_v001.pkts +0 -0
  105. imap_processing/tests/codice/data/imap_codice_l0_hi-counters-singles_20240429_v001.pkts +0 -0
  106. imap_processing/tests/codice/data/imap_codice_l0_hi-omni_20240429_v001.pkts +0 -0
  107. imap_processing/tests/codice/data/imap_codice_l0_hi-pha_20240429_v001.pkts +0 -0
  108. imap_processing/tests/codice/data/imap_codice_l0_hi-sectored_20240429_v001.pkts +0 -0
  109. imap_processing/tests/codice/data/imap_codice_l0_hskp_20100101_v001.pkts +0 -0
  110. imap_processing/tests/codice/data/imap_codice_l0_lo-counters-aggregated_20240429_v001.pkts +0 -0
  111. imap_processing/tests/codice/data/imap_codice_l0_lo-counters-singles_20240429_v001.pkts +0 -0
  112. imap_processing/tests/codice/data/imap_codice_l0_lo-nsw-angular_20240429_v001.pkts +0 -0
  113. imap_processing/tests/codice/data/imap_codice_l0_lo-nsw-priority_20240429_v001.pkts +0 -0
  114. imap_processing/tests/codice/data/imap_codice_l0_lo-nsw-species_20240429_v001.pkts +0 -0
  115. imap_processing/tests/codice/data/imap_codice_l0_lo-pha_20240429_v001.pkts +0 -0
  116. imap_processing/tests/codice/data/imap_codice_l0_lo-sw-angular_20240429_v001.pkts +0 -0
  117. imap_processing/tests/codice/data/imap_codice_l0_lo-sw-priority_20240429_v001.pkts +0 -0
  118. imap_processing/tests/codice/data/imap_codice_l0_lo-sw-species_20240429_v001.pkts +0 -0
  119. imap_processing/tests/idex/imap_idex_l0_raw_20230725_v001.pkts +0 -0
  120. imap_processing/tests/mag/imap_mag_l1a_burst-magi_20231025_v001.cdf +0 -0
  121. /imap_processing/tests/hit/test_data/{imap_hit_l0_hk_20100105_v001.pkts → imap_hit_l0_raw_20100105_v001.pkts} +0 -0
  122. {imap_processing-0.7.0.dist-info → imap_processing-0.8.0.dist-info}/LICENSE +0 -0
  123. {imap_processing-0.7.0.dist-info → imap_processing-0.8.0.dist-info}/WHEEL +0 -0
  124. {imap_processing-0.7.0.dist-info → imap_processing-0.8.0.dist-info}/entry_points.txt +0 -0
@@ -6,6 +6,10 @@ from imap_processing.cdf.utils import load_cdf, write_cdf
6
6
  from imap_processing.swe.l1a.swe_l1a import swe_l1a
7
7
  from imap_processing.swe.l1a.swe_science import swe_science
8
8
  from imap_processing.swe.l1b.swe_l1b import swe_l1b
9
+ from imap_processing.swe.l1b.swe_l1b_science import (
10
+ convert_counts_to_rate,
11
+ deadtime_correction,
12
+ )
9
13
 
10
14
 
11
15
  def test_swe_l1b(decom_test_data_derived):
@@ -41,19 +45,36 @@ def test_swe_l1b(decom_test_data_derived):
41
45
  )
42
46
 
43
47
 
44
- def test_cdf_creation():
48
+ def test_cdf_creation(l1b_validation_df):
45
49
  """Test that CDF file is created and has the correct name."""
46
50
  test_data_path = "tests/swe/l0_data/2024051010_SWE_SCIENCE_packet.bin"
47
51
  l1a_datasets = swe_l1a(imap_module_directory / test_data_path, "002")
48
52
 
49
- sci_l1a_filepath = write_cdf(l1a_datasets)
50
-
51
- assert sci_l1a_filepath.name == "imap_swe_l1a_sci_20240510_v002.cdf"
52
-
53
- # reads data from CDF file and passes to l1b
54
- l1a_cdf_dataset = load_cdf(sci_l1a_filepath)
55
- l1b_dataset = swe_l1b(l1a_cdf_dataset, "002")
53
+ l1b_dataset = swe_l1b(l1a_datasets, "002")
56
54
 
57
55
  sci_l1b_filepath = write_cdf(l1b_dataset)
58
56
 
59
57
  assert sci_l1b_filepath.name == "imap_swe_l1b_sci_20240510_v002.cdf"
58
+ # load the CDF file and compare the values
59
+ l1b_cdf_dataset = load_cdf(sci_l1b_filepath)
60
+ processed_science = l1b_cdf_dataset["science_data"].data
61
+ validation_science = l1b_validation_df.values[:, 1:].reshape(6, 24, 30, 7)
62
+ np.testing.assert_allclose(processed_science, validation_science, rtol=1e-7)
63
+
64
+
65
+ def test_count_rate():
66
+ x = np.array([1, 10, 100, 1000, 10000, 38911, 65535])
67
+ acq_duration = 80000
68
+ deatime_corrected = deadtime_correction(x, acq_duration)
69
+ count_rate = convert_counts_to_rate(deatime_corrected, acq_duration)
70
+ # Ruth provided the expected output for this test
71
+ expected_output = [
72
+ 12.50005653,
73
+ 125.00562805,
74
+ 1250.56278121,
75
+ 12556.50455087,
76
+ 130890.05519127,
77
+ 589631.73670132,
78
+ 1161815.68783304,
79
+ ]
80
+ np.testing.assert_allclose(count_rate, expected_output, rtol=1e-7)
@@ -99,7 +99,7 @@ def test_packet_file_to_datasets(use_derived_value, expected_mode):
99
99
 
100
100
 
101
101
  def test_packet_file_to_datasets_flat_definition():
102
- test_file = "tests/idex/imap_idex_l0_raw_20230725_v001.pkts"
102
+ test_file = "tests/idex/imap_idex_l0_raw_20231214_v001.pkts"
103
103
  packet_files = imap_module_directory / test_file
104
104
  packet_definition = (
105
105
  imap_module_directory / "idex/packet_definitions/idex_packet_definition.xml"
@@ -0,0 +1,108 @@
1
+ """Tests Extended Raw Events for ULTRA L1b."""
2
+
3
+ from unittest import mock
4
+
5
+ import numpy as np
6
+ import pandas as pd
7
+ import pytest
8
+
9
+ from imap_processing.ultra.l1b.de import calculate_de
10
+
11
+
12
+ @pytest.fixture()
13
+ def df_filt(de_dataset, events_fsw_comparison_theta_0):
14
+ """Fixture to import test dataset."""
15
+ df = pd.read_csv(events_fsw_comparison_theta_0)
16
+ df_filt = df[df["StartType"] != -1]
17
+ df_filt = df_filt.replace("FILL", 0)
18
+
19
+ return df_filt
20
+
21
+
22
+ @mock.patch("imap_processing.ultra.l1b.de.get_annotated_particle_velocity")
23
+ def test_calculate_de(mock_get_annotated_particle_velocity, de_dataset, df_filt):
24
+ """Tests calculate_de function."""
25
+
26
+ # Mock get_annotated_particle_velocity to avoid needing kernels
27
+ def side_effect_func(event_times, position, ultra_frame, dps_frame, sc_frame):
28
+ """
29
+ Mock behavior of get_annotated_particle_velocity.
30
+
31
+ Returns NaN-filled arrays matching the expected output shape.
32
+ """
33
+ num_events = event_times.size
34
+ return (
35
+ np.full((num_events, 3), np.nan), # sc_velocity
36
+ np.full((num_events, 3), np.nan), # sc_dps_velocity
37
+ np.full((num_events, 3), np.nan), # helio_velocity
38
+ )
39
+
40
+ mock_get_annotated_particle_velocity.side_effect = side_effect_func
41
+
42
+ dataset = calculate_de(de_dataset, "imap_ultra_l1b_45sensor-de")
43
+
44
+ # Front and back positions
45
+ assert np.allclose(dataset["x_front"].data, df_filt["Xf"].astype("float"))
46
+ assert np.allclose(dataset["y_front"], df_filt["Yf"].astype("float"))
47
+ assert np.allclose(dataset["x_back"], df_filt["Xb"].astype("float"))
48
+ assert np.allclose(dataset["y_back"], df_filt["Yb"].astype("float"))
49
+
50
+ # Coincidence positions
51
+ assert np.allclose(dataset["x_coin"], df_filt["Xc"].astype("float"))
52
+
53
+ # Time of flight
54
+ assert np.allclose(dataset["tof_start_stop"], df_filt["TOF"].astype("float"))
55
+ assert np.allclose(dataset["tof_stop_coin"], df_filt["eTOF"].astype("float"))
56
+ assert np.allclose(dataset["tof_corrected"], df_filt["cTOF"].astype("float"))
57
+
58
+ # Distances and path lengths
59
+ assert np.allclose(dataset["front_back_distance"], df_filt["d"].astype("float"))
60
+ assert np.allclose(dataset["path_length"], df_filt["r"].astype("float"))
61
+
62
+ # Coincidence, start, and event types
63
+ assert np.allclose(dataset["coincidence_type"], df_filt["CoinType"].astype("float"))
64
+ assert np.allclose(dataset["start_type"], df_filt["StartType"].astype("float"))
65
+ assert np.allclose(dataset["event_type"], df_filt["StopType"].astype("float"))
66
+
67
+ # Energies and species
68
+ assert np.allclose(dataset["energy"], df_filt["Energy"].astype("float"))
69
+ assert np.allclose(
70
+ dataset["species"], np.full(len(de_dataset["epoch"]), np.nan, dtype=np.uint8)
71
+ )
72
+
73
+ # Velocities in various frames
74
+ test_tof = dataset["tof_start_stop"]
75
+ assert np.allclose(
76
+ dataset["vx_ultra"][test_tof > 0],
77
+ -df_filt["vhatX"].astype("float").values[test_tof > 0],
78
+ rtol=1e-2,
79
+ )
80
+ assert np.allclose(
81
+ dataset["vy_ultra"][test_tof > 0],
82
+ -df_filt["vhatY"].astype("float").values[test_tof > 0],
83
+ rtol=1e-2,
84
+ )
85
+ assert np.allclose(
86
+ dataset["vz_ultra"][test_tof > 0],
87
+ -df_filt["vhatZ"].astype("float").values[test_tof > 0],
88
+ rtol=1e-2,
89
+ )
90
+
91
+ assert dataset["vx_sc"].shape == (len(de_dataset["epoch"]),)
92
+ assert dataset["vy_sc"].shape == (len(de_dataset["epoch"]),)
93
+ assert dataset["vz_sc"].shape == (len(de_dataset["epoch"]),)
94
+
95
+ assert dataset["vx_dps_sc"].shape == (len(de_dataset["epoch"]),)
96
+ assert dataset["vy_dps_sc"].shape == (len(de_dataset["epoch"]),)
97
+ assert dataset["vz_dps_sc"].shape == (len(de_dataset["epoch"]),)
98
+
99
+ assert dataset["vx_dps_helio"].shape == (len(de_dataset["epoch"]),)
100
+ assert dataset["vy_dps_helio"].shape == (len(de_dataset["epoch"]),)
101
+ assert dataset["vz_dps_helio"].shape == (len(de_dataset["epoch"]),)
102
+
103
+ # Event efficiency
104
+ assert np.allclose(
105
+ dataset["event_efficiency"],
106
+ np.full(len(de_dataset["epoch"]), np.nan),
107
+ equal_nan=True,
108
+ )
@@ -1,3 +1,5 @@
1
+ from unittest import mock
2
+
1
3
  import numpy as np
2
4
  import pytest
3
5
  import xarray as xr
@@ -73,7 +75,7 @@ def test_create_dataset(mock_data_l1b_dict):
73
75
  assert "epoch" in dataset.coords
74
76
  assert dataset.coords["epoch"].dtype == "datetime64[ns]"
75
77
  assert dataset.attrs["Logical_source"] == "imap_ultra_l1b_45sensor-de"
76
- assert dataset["x_front"].attrs["UNITS"] == "mm"
78
+ assert dataset["x_front"].attrs["UNITS"] == "mm / 100"
77
79
  np.testing.assert_array_equal(dataset["x_front"], np.zeros(3))
78
80
 
79
81
 
@@ -99,9 +101,31 @@ def test_ultra_l1b_rates(mock_data_l1a_rates_dict):
99
101
  )
100
102
 
101
103
 
102
- def test_ultra_l1b_de(mock_data_l1a_de_aux_dict):
104
+ @pytest.mark.external_kernel()
105
+ @pytest.mark.use_test_metakernel("imap_ena_sim_metakernel.template")
106
+ @mock.patch("imap_processing.ultra.l1b.de.get_annotated_particle_velocity")
107
+ def test_ultra_l1b_de(mock_get_annotated_particle_velocity, de_dataset):
103
108
  """Tests that L1b data is created."""
104
- output_datasets = ultra_l1b(mock_data_l1a_de_aux_dict, data_version="001")
109
+ data_dict = {}
110
+ data_dict[de_dataset.attrs["Logical_source"]] = de_dataset
111
+ data_dict["imap_ultra_l1a_45sensor-aux"] = de_dataset
112
+
113
+ # Mock get_annotated_particle_velocity to avoid needing kernels
114
+ def side_effect_func(event_times, position, ultra_frame, dps_frame, sc_frame):
115
+ """
116
+ Mock behavior of get_annotated_particle_velocity.
117
+
118
+ Returns NaN-filled arrays matching the expected output shape.
119
+ """
120
+ num_events = event_times.size
121
+ return (
122
+ np.full((num_events, 3), np.nan), # sc_velocity
123
+ np.full((num_events, 3), np.nan), # sc_dps_velocity
124
+ np.full((num_events, 3), np.nan), # helio_velocity
125
+ )
126
+
127
+ mock_get_annotated_particle_velocity.side_effect = side_effect_func
128
+ output_datasets = ultra_l1b(data_dict, data_version="001")
105
129
 
106
130
  assert len(output_datasets) == 1
107
131
  assert output_datasets[0].attrs["Logical_source"] == "imap_ultra_l1b_45sensor-de"
@@ -5,7 +5,9 @@ import pytest
5
5
  import spiceypy as spice
6
6
 
7
7
  from imap_processing.spice.geometry import SpiceFrame
8
- from imap_processing.ultra.l1b.ultra_l1b_annotated import get_particle_velocity
8
+ from imap_processing.ultra.l1b.ultra_l1b_annotated import (
9
+ get_annotated_particle_velocity,
10
+ )
9
11
 
10
12
 
11
13
  @pytest.fixture()
@@ -45,18 +47,37 @@ def test_get_particle_velocity(spice_test_data_path, kernels):
45
47
  times = np.array([start])
46
48
  instrument_velocity = np.array([[41.18609, -471.24467, -832.8784]])
47
49
 
48
- sc_velocity_45, helio_velocity_45 = get_particle_velocity(
49
- times, instrument_velocity, SpiceFrame.IMAP_ULTRA_45, SpiceFrame.IMAP_DPS
50
+ sc_velocity_45, sc_dps_velocity_45, helio_velocity_45 = (
51
+ get_annotated_particle_velocity(
52
+ times,
53
+ instrument_velocity,
54
+ SpiceFrame.IMAP_ULTRA_45,
55
+ SpiceFrame.IMAP_DPS,
56
+ SpiceFrame.IMAP_SPACECRAFT,
57
+ )
50
58
  )
51
- sc_velocity_90, helio_velocity_90 = get_particle_velocity(
52
- times, instrument_velocity, SpiceFrame.IMAP_ULTRA_90, SpiceFrame.IMAP_DPS
59
+ sc_velocity_90, sc_dps_velocity_90, helio_velocity_90 = (
60
+ get_annotated_particle_velocity(
61
+ times,
62
+ instrument_velocity,
63
+ SpiceFrame.IMAP_ULTRA_90,
64
+ SpiceFrame.IMAP_DPS,
65
+ SpiceFrame.IMAP_SPACECRAFT,
66
+ )
53
67
  )
54
68
 
55
69
  # Compute the magnitude of the velocity vectors in both frames
56
- magnitude_45 = np.linalg.norm(sc_velocity_45)
57
- magnitude_90 = np.linalg.norm(sc_velocity_90)
70
+ magnitude_sc_45 = np.linalg.norm(sc_velocity_45)
71
+ magnitude_sc_90 = np.linalg.norm(sc_velocity_90)
72
+ magnitude_dps_45 = np.linalg.norm(sc_dps_velocity_45)
73
+ magnitude_dps_90 = np.linalg.norm(sc_dps_velocity_90)
58
74
  state, lt = spice.spkezr("IMAP", times, "IMAP_DPS", "NONE", "SUN")
59
75
 
60
- assert np.allclose(magnitude_45, magnitude_90, atol=1e-6)
61
- assert np.array_equal((helio_velocity_45 - state[0][3:6]).flatten(), sc_velocity_45)
62
- assert np.array_equal((helio_velocity_90 - state[0][3:6]).flatten(), sc_velocity_90)
76
+ assert np.allclose(magnitude_sc_45, magnitude_sc_90, atol=1e-6)
77
+ assert np.allclose(magnitude_dps_45, magnitude_dps_90, atol=1e-6)
78
+ assert np.array_equal(
79
+ (helio_velocity_45 - state[0][3:6]).flatten(), sc_dps_velocity_45
80
+ )
81
+ assert np.array_equal(
82
+ (helio_velocity_90 - state[0][3:6]).flatten(), sc_dps_velocity_90
83
+ )
@@ -17,11 +17,11 @@ from imap_processing.ultra.l1b.ultra_l1b_extended import (
17
17
  get_energy_ssd,
18
18
  get_front_x_position,
19
19
  get_front_y_position,
20
- get_particle_velocity,
21
20
  get_path_length,
22
21
  get_ph_tof_and_back_positions,
23
22
  get_ssd_back_position_and_tof_offset,
24
23
  get_ssd_tof,
24
+ get_unit_vector,
25
25
  )
26
26
 
27
27
 
@@ -213,8 +213,8 @@ def test_calculate_etof_xc(de_dataset, yf_fixture):
213
213
  )
214
214
 
215
215
 
216
- def test_get_particle_velocity(de_dataset, yf_fixture):
217
- """Tests get_particle_velocity function."""
216
+ def test_get_unit_vector(de_dataset, yf_fixture):
217
+ """Tests get_unit_vector function."""
218
218
  df_filt, _, _ = yf_fixture
219
219
 
220
220
  ph_indices = np.nonzero(
@@ -229,7 +229,7 @@ def test_get_particle_velocity(de_dataset, yf_fixture):
229
229
  test_d = ph_rows["d"].astype("float").values
230
230
  test_tof = ph_rows["TOF"].astype("float").values
231
231
 
232
- vhat_x, vhat_y, vhat_z = get_particle_velocity(
232
+ vhat_x, vhat_y, vhat_z = get_unit_vector(
233
233
  (test_xf, test_yf),
234
234
  (test_xb, test_yb),
235
235
  test_d,
@@ -298,17 +298,27 @@ def test_get_ctof(yf_fixture):
298
298
  """Tests get_ctof function."""
299
299
  df_filt, _, _ = yf_fixture
300
300
 
301
- df_ph_ssd = df_filt[
302
- df_filt["StopType"].isin([StopType.SSD.value, StopType.PH.value])
303
- ]
301
+ df_ph = df_filt[df_filt["StopType"].isin([StopType.PH.value])]
302
+
303
+ df_ssd = df_filt[df_filt["StopType"].isin([StopType.SSD.value])]
304
+
305
+ ph_ctof = get_ctof(
306
+ df_ph["TOF"].astype("float").to_numpy(),
307
+ df_ph["r"].astype("float").to_numpy(),
308
+ "PH",
309
+ )
304
310
 
305
- ctof = get_ctof(
306
- df_ph_ssd["TOF"].astype("float").to_numpy(),
307
- df_ph_ssd["r"].astype("float").to_numpy(),
311
+ ssd_ctof = get_ctof(
312
+ df_ssd["TOF"].astype("float").to_numpy(),
313
+ df_ssd["r"].astype("float").to_numpy(),
314
+ "SSD",
308
315
  )
309
316
 
310
317
  np.testing.assert_allclose(
311
- ctof, df_ph_ssd["cTOF"].astype("float"), atol=1e-05, rtol=0
318
+ ph_ctof, df_ph["cTOF"].astype("float"), atol=1e-05, rtol=0
319
+ )
320
+ np.testing.assert_allclose(
321
+ ssd_ctof, df_ssd["cTOF"].astype("float"), atol=1e-05, rtol=0
312
322
  )
313
323
 
314
324
 
@@ -3,14 +3,12 @@
3
3
  import cdflib
4
4
  import numpy as np
5
5
  import pytest
6
- import spiceypy as spice
7
6
  from cdflib import CDF
8
7
 
9
8
  from imap_processing import imap_module_directory
10
9
  from imap_processing.ultra.l1c.ultra_l1c_pset_bins import (
11
10
  build_energy_bins,
12
11
  build_spatial_bins,
13
- cartesian_to_spherical,
14
12
  get_helio_exposure_times,
15
13
  get_histogram,
16
14
  get_pointing_frame_exposure_times,
@@ -32,33 +30,15 @@ def test_data():
32
30
  return v, energy
33
31
 
34
32
 
35
- @pytest.fixture()
36
- def kernels(spice_test_data_path):
37
- """List SPICE kernels."""
38
- required_kernels = [
39
- "imap_science_0001.tf",
40
- "imap_sclk_0000.tsc",
41
- "sim_1yr_imap_attitude.bc",
42
- "imap_wkcp.tf",
43
- "naif0012.tls",
44
- "sim_1yr_imap_pointing_frame.bc",
45
- "de440s.bsp",
46
- "imap_spk_demo.bsp",
47
- ]
48
- kernels = [str(spice_test_data_path / kernel) for kernel in required_kernels]
49
-
50
- return kernels
51
-
52
-
53
33
  def test_build_energy_bins():
54
34
  """Tests build_energy_bins function."""
55
35
  energy_bin_edges, energy_midpoints = build_energy_bins()
56
- energy_bin_start = energy_bin_edges[:-1]
57
- energy_bin_end = energy_bin_edges[1:]
36
+ energy_bin_start = [interval[0] for interval in energy_bin_edges]
37
+ energy_bin_end = [interval[1] for interval in energy_bin_edges]
58
38
 
59
39
  assert energy_bin_start[0] == 0
60
40
  assert energy_bin_start[1] == 3.385
61
- assert len(energy_bin_edges) == 25
41
+ assert len(energy_bin_edges) == 24
62
42
  assert energy_midpoints[0] == (energy_bin_start[0] + energy_bin_end[0]) / 2
63
43
 
64
44
  # Comparison to expected values.
@@ -90,21 +70,6 @@ def test_build_spatial_bins():
90
70
  np.testing.assert_allclose(el_bin_midpoints[-1], 89.75, atol=1e-4)
91
71
 
92
72
 
93
- def test_cartesian_to_spherical(test_data):
94
- """Tests cartesian_to_spherical function."""
95
- v, _ = test_data
96
-
97
- az_sc, el_sc, r = cartesian_to_spherical(v)
98
-
99
- # MATLAB code outputs:
100
- np.testing.assert_allclose(
101
- np.unique(np.radians(az_sc)), np.array([1.31300, 2.34891]), atol=1e-05, rtol=0
102
- )
103
- np.testing.assert_allclose(
104
- np.unique(np.radians(el_sc)), np.array([-0.88901, -0.70136]), atol=1e-05, rtol=0
105
- )
106
-
107
-
108
73
  def test_get_histogram(test_data):
109
74
  """Tests get_histogram function."""
110
75
  v, energy = test_data
@@ -119,7 +84,7 @@ def test_get_histogram(test_data):
119
84
  assert hist.shape == (
120
85
  len(az_bin_edges) - 1,
121
86
  len(el_bin_edges) - 1,
122
- len(energy_bin_edges) - 1,
87
+ len(energy_bin_edges),
123
88
  )
124
89
 
125
90
 
@@ -143,10 +108,10 @@ def test_get_pointing_frame_exposure_times():
143
108
 
144
109
 
145
110
  @pytest.mark.external_kernel()
146
- def test_et_helio_exposure_times(kernels):
111
+ @pytest.mark.use_test_metakernel("imap_ena_sim_metakernel.template")
112
+ def test_et_helio_exposure_times():
147
113
  """Tests get_helio_exposure_times function."""
148
114
 
149
- spice.furnsh(kernels)
150
115
  constant_exposure = BASE_PATH / "dps_grid45_compressed.cdf"
151
116
  start_time = 829485054.185627
152
117
  end_time = 829567884.185627
@@ -185,9 +150,9 @@ def test_et_helio_exposure_times(kernels):
185
150
  transposed_exposure = np.transpose(exposure_data, (2, 1, 0))
186
151
  exposures.append(transposed_exposure)
187
152
 
188
- np.array_equal(exposures[0], exposure_3d[:, :, 0])
189
- np.array_equal(exposures[1], exposure_3d[:, :, 1])
190
- np.array_equal(exposures[2], exposure_3d[:, :, 2])
153
+ assert np.array_equal(np.squeeze(exposures[0]), exposure_3d[:, :, 0])
154
+ assert np.array_equal(np.squeeze(exposures[1]), exposure_3d[:, :, 11])
155
+ assert np.array_equal(np.squeeze(exposures[2]), exposure_3d[:, :, 23])
191
156
 
192
157
 
193
158
  def test_get_pointing_frame_sensitivity():
@@ -52,13 +52,18 @@ class UltraConstants:
52
52
  DF: float = 3.39 # Distance from slit to foil [mm]
53
53
 
54
54
  # Derived constants
55
- DMIN: float = (
55
+ DMIN_PH_CTOF: float = (
56
56
  Z_DS - (2**0.5) * DF
57
57
  ) # Minimum distance between front and back detectors [mm]
58
- DMIN_SSD_CTOF: float = (DMIN**2) / (
59
- DMIN - Z_DSTOP
58
+ DMIN_SSD_CTOF: float = (DMIN_PH_CTOF**2) / (
59
+ DMIN_PH_CTOF - Z_DSTOP
60
60
  ) # SSD-specific correction to DMIN [mm]
61
61
 
62
62
  # Conversion factors
63
63
  KEV_J = 1.602180000000000e-16 # 1.6021766339999e-16 # keV to joules
64
64
  MASS_H = 1.6735575e-27 # Mass of a hydrogen atom in kilograms.
65
+
66
+ # Energy bin constants
67
+ ALPHA = 0.2 # deltaE/E
68
+ ENERGY_START = 3.385 # energy start for the Ultra grids
69
+ N_BINS = 23 # number of energy bins