imap-processing 0.8.0__py3-none-any.whl → 0.9.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.
- imap_processing/_version.py +2 -2
- imap_processing/ccsds/excel_to_xtce.py +2 -0
- imap_processing/cdf/config/imap_hi_variable_attrs.yaml +100 -1
- imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +14 -0
- imap_processing/cdf/config/imap_hit_l1a_variable_attrs.yaml +63 -1
- imap_processing/cdf/config/imap_idex_global_cdf_attrs.yaml +7 -0
- imap_processing/cdf/config/imap_idex_l1a_variable_attrs.yaml +574 -231
- imap_processing/cdf/config/imap_idex_l1b_variable_attrs.yaml +326 -0
- imap_processing/cdf/config/imap_lo_l1a_variable_attrs.yaml +33 -23
- imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +7 -4
- imap_processing/cdf/utils.py +3 -5
- imap_processing/cli.py +13 -4
- imap_processing/codice/codice_l1a.py +5 -5
- imap_processing/codice/constants.py +9 -9
- imap_processing/codice/decompress.py +6 -2
- imap_processing/glows/l1a/glows_l1a.py +1 -2
- imap_processing/hi/l1a/hi_l1a.py +4 -4
- imap_processing/hi/l1a/histogram.py +106 -108
- imap_processing/hi/l1a/science_direct_event.py +91 -224
- imap_processing/hi/packet_definitions/TLM_HI_COMBINED_SCI.xml +3994 -0
- imap_processing/hit/l0/constants.py +2 -2
- imap_processing/hit/l0/decom_hit.py +12 -101
- imap_processing/hit/l1a/hit_l1a.py +164 -23
- imap_processing/ialirt/l0/process_codicelo.py +153 -0
- imap_processing/ialirt/l0/process_hit.py +5 -5
- imap_processing/ialirt/packet_definitions/ialirt_codicelo.xml +281 -0
- imap_processing/ialirt/process_ephemeris.py +212 -0
- imap_processing/idex/idex_l1a.py +55 -75
- imap_processing/idex/idex_l1b.py +192 -0
- imap_processing/idex/idex_variable_unpacking_and_eu_conversion.csv +33 -0
- imap_processing/idex/packet_definitions/idex_packet_definition.xml +97 -595
- imap_processing/lo/l0/decompression_tables/decompression_tables.py +16 -0
- imap_processing/lo/l0/lo_science.py +44 -12
- imap_processing/lo/l1a/lo_l1a.py +76 -8
- imap_processing/lo/packet_definitions/lo_xtce.xml +9877 -87
- imap_processing/mag/l1a/mag_l1a.py +1 -2
- imap_processing/mag/l1a/mag_l1a_data.py +1 -2
- imap_processing/mag/l1b/mag_l1b.py +2 -1
- imap_processing/spice/geometry.py +37 -19
- imap_processing/spice/time.py +144 -2
- imap_processing/swapi/l1/swapi_l1.py +3 -3
- imap_processing/swapi/packet_definitions/swapi_packet_definition.xml +1535 -446
- imap_processing/swe/l2/swe_l2.py +134 -17
- imap_processing/tests/ccsds/test_data/expected_output.xml +1 -1
- imap_processing/tests/codice/test_codice_l1a.py +8 -8
- imap_processing/tests/codice/test_decompress.py +4 -4
- imap_processing/tests/conftest.py +46 -43
- imap_processing/tests/hi/test_data/l0/H90_NHK_20241104.bin +0 -0
- imap_processing/tests/hi/test_data/l0/H90_sci_cnt_20241104.bin +0 -0
- imap_processing/tests/hi/test_data/l0/H90_sci_de_20241104.bin +0 -0
- imap_processing/tests/hi/test_hi_l1b.py +2 -2
- imap_processing/tests/hi/test_l1a.py +31 -58
- imap_processing/tests/hi/test_science_direct_event.py +58 -0
- imap_processing/tests/hit/test_data/sci_sample1.ccsds +0 -0
- imap_processing/tests/hit/test_decom_hit.py +60 -50
- imap_processing/tests/hit/test_hit_l1a.py +327 -12
- imap_processing/tests/hit/test_hit_l1b.py +76 -0
- imap_processing/tests/hit/validation_data/hskp_sample_eu.csv +89 -0
- imap_processing/tests/hit/validation_data/sci_sample_raw1.csv +29 -0
- imap_processing/tests/ialirt/test_data/l0/apid01152.tlm +0 -0
- imap_processing/tests/ialirt/test_data/l0/imap_codice_l1a_lo-ialirt_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/ialirt/unit/test_process_codicelo.py +106 -0
- imap_processing/tests/ialirt/unit/test_process_ephemeris.py +109 -0
- imap_processing/tests/ialirt/unit/test_process_hit.py +9 -6
- imap_processing/tests/idex/conftest.py +1 -1
- imap_processing/tests/idex/test_idex_l0.py +1 -1
- imap_processing/tests/idex/test_idex_l1a.py +7 -1
- imap_processing/tests/idex/test_idex_l1b.py +126 -0
- imap_processing/tests/lo/test_lo_l1a.py +7 -16
- imap_processing/tests/lo/test_lo_science.py +67 -3
- imap_processing/tests/lo/test_pkts/imap_lo_l0_raw_20240803_v002.pkts +0 -0
- imap_processing/tests/lo/validation_data/Instrument_FM1_T104_R129_20240803_ILO_SCI_DE_dec_DN_with_fills.csv +1999 -0
- imap_processing/tests/mag/test_mag_l1b.py +39 -5
- imap_processing/tests/spice/test_geometry.py +32 -6
- imap_processing/tests/spice/test_time.py +135 -6
- imap_processing/tests/swapi/test_swapi_decom.py +75 -69
- imap_processing/tests/swapi/test_swapi_l1.py +4 -4
- imap_processing/tests/swe/test_swe_l2.py +64 -8
- imap_processing/tests/test_utils.py +1 -1
- imap_processing/tests/ultra/test_data/l0/ultra45_raw_sc_ultrarawimg_withFSWcalcs_FM45_40P_Phi28p5_BeamCal_LinearScan_phi2850_theta-000_20240207T102740.csv +3314 -3314
- imap_processing/tests/ultra/unit/test_de.py +8 -3
- imap_processing/tests/ultra/unit/test_spatial_utils.py +125 -0
- imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py +39 -29
- imap_processing/tests/ultra/unit/test_ultra_l1c_pset_bins.py +2 -25
- imap_processing/ultra/constants.py +4 -0
- imap_processing/ultra/l1b/de.py +8 -14
- imap_processing/ultra/l1b/ultra_l1b_extended.py +29 -70
- imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +1 -36
- imap_processing/ultra/utils/spatial_utils.py +221 -0
- {imap_processing-0.8.0.dist-info → imap_processing-0.9.0.dist-info}/METADATA +1 -1
- {imap_processing-0.8.0.dist-info → imap_processing-0.9.0.dist-info}/RECORD +94 -76
- imap_processing/hi/l0/__init__.py +0 -0
- imap_processing/hi/l0/decom_hi.py +0 -24
- imap_processing/hi/packet_definitions/hi_packet_definition.xml +0 -482
- imap_processing/tests/hi/test_decom.py +0 -55
- imap_processing/tests/hi/test_l1a_sci_de.py +0 -72
- {imap_processing-0.8.0.dist-info → imap_processing-0.9.0.dist-info}/LICENSE +0 -0
- {imap_processing-0.8.0.dist-info → imap_processing-0.9.0.dist-info}/WHEEL +0 -0
- {imap_processing-0.8.0.dist-info → imap_processing-0.9.0.dist-info}/entry_points.txt +0 -0
|
@@ -6,6 +6,7 @@ import numpy as np
|
|
|
6
6
|
import pandas as pd
|
|
7
7
|
import pytest
|
|
8
8
|
|
|
9
|
+
from imap_processing.ultra.constants import UltraConstants
|
|
9
10
|
from imap_processing.ultra.l1b.de import calculate_de
|
|
10
11
|
|
|
11
12
|
|
|
@@ -66,9 +67,13 @@ def test_calculate_de(mock_get_annotated_particle_velocity, de_dataset, df_filt)
|
|
|
66
67
|
|
|
67
68
|
# Energies and species
|
|
68
69
|
assert np.allclose(dataset["energy"], df_filt["Energy"].astype("float"))
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
species_array = dataset["species"][
|
|
71
|
+
np.where(
|
|
72
|
+
(dataset["tof_corrected"] > UltraConstants.CTOF_SPECIES_MIN)
|
|
73
|
+
& (dataset["tof_corrected"] < UltraConstants.CTOF_SPECIES_MAX)
|
|
74
|
+
)[0]
|
|
75
|
+
]
|
|
76
|
+
assert np.all(species_array == "H")
|
|
72
77
|
|
|
73
78
|
# Velocities in various frames
|
|
74
79
|
test_tof = dataset["tof_start_stop"]
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""Test creation of solid angle map."""
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import numpy.testing as npt
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from imap_processing.ultra.utils import spatial_utils
|
|
8
|
+
|
|
9
|
+
# Parameterize with spacings (degrees here):
|
|
10
|
+
valid_spacings = [0.1, 0.25, 0.5, 1, 5, 10, 20]
|
|
11
|
+
invalid_spacings = [0, -1, 11]
|
|
12
|
+
invalid_spacings_match_str = [
|
|
13
|
+
"Spacing must be positive valued, non-zero.",
|
|
14
|
+
"Spacing must be positive valued, non-zero.",
|
|
15
|
+
"Spacing must divide evenly into pi radians.",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_build_spatial_bins():
|
|
20
|
+
"""Tests build_spatial_bins function."""
|
|
21
|
+
az_bin_edges, el_bin_edges, az_bin_midpoints, el_bin_midpoints = (
|
|
22
|
+
spatial_utils.build_spatial_bins()
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
assert az_bin_edges[0] == 0
|
|
26
|
+
assert az_bin_edges[-1] == 360
|
|
27
|
+
assert len(az_bin_edges) == 721
|
|
28
|
+
|
|
29
|
+
assert el_bin_edges[0] == -90
|
|
30
|
+
assert el_bin_edges[-1] == 90
|
|
31
|
+
assert len(el_bin_edges) == 361
|
|
32
|
+
|
|
33
|
+
assert len(az_bin_midpoints) == 720
|
|
34
|
+
np.testing.assert_allclose(az_bin_midpoints[0], 0.25, atol=1e-4)
|
|
35
|
+
np.testing.assert_allclose(az_bin_midpoints[-1], 359.75, atol=1e-4)
|
|
36
|
+
|
|
37
|
+
assert len(el_bin_midpoints) == 360
|
|
38
|
+
np.testing.assert_allclose(el_bin_midpoints[0], -89.75, atol=1e-4)
|
|
39
|
+
np.testing.assert_allclose(el_bin_midpoints[-1], 89.75, atol=1e-4)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@pytest.mark.parametrize("spacing", valid_spacings)
|
|
43
|
+
def test_build_solid_angle_map(spacing):
|
|
44
|
+
"""Test build_solid_angle_map function."""
|
|
45
|
+
solid_angle_map_steradians = spatial_utils.build_solid_angle_map(
|
|
46
|
+
spacing, input_degrees=True, output_degrees=False
|
|
47
|
+
)
|
|
48
|
+
assert np.isclose(np.sum(solid_angle_map_steradians), 4 * np.pi, atol=0, rtol=1e-9)
|
|
49
|
+
|
|
50
|
+
solid_angle_map_sqdeg = spatial_utils.build_solid_angle_map(
|
|
51
|
+
np.deg2rad(spacing), input_degrees=False, output_degrees=True
|
|
52
|
+
)
|
|
53
|
+
assert np.isclose(
|
|
54
|
+
np.sum(solid_angle_map_sqdeg), 4 * np.pi * (180 / np.pi) ** 2, atol=0, rtol=1e-9
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@pytest.mark.parametrize(
|
|
59
|
+
"spacing, match_str", zip(invalid_spacings, invalid_spacings_match_str)
|
|
60
|
+
)
|
|
61
|
+
def test_build_solid_angle_map_invalid_spacing(spacing, match_str):
|
|
62
|
+
"""Test build_solid_angle_map function raises error for invalid spacing."""
|
|
63
|
+
with pytest.raises(ValueError, match=match_str):
|
|
64
|
+
_ = spatial_utils.build_solid_angle_map(
|
|
65
|
+
spacing, input_degrees=True, output_degrees=False
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@pytest.mark.parametrize("spacing", valid_spacings)
|
|
70
|
+
def test_build_az_el_grid(spacing):
|
|
71
|
+
"""Test build_az_el_grid function."""
|
|
72
|
+
az_range, el_range, az_grid, el_grid = spatial_utils.build_az_el_grid(
|
|
73
|
+
spacing=spacing,
|
|
74
|
+
input_degrees=True,
|
|
75
|
+
output_degrees=True,
|
|
76
|
+
centered_azimuth=False,
|
|
77
|
+
centered_elevation=True,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Size checks
|
|
81
|
+
assert az_range.size == int(360 / spacing)
|
|
82
|
+
assert el_range.size == int(180 / spacing)
|
|
83
|
+
assert az_range.size == az_grid.shape[1]
|
|
84
|
+
assert el_range.size == el_grid.shape[0]
|
|
85
|
+
|
|
86
|
+
# Check grid values
|
|
87
|
+
expected_az_range = np.arange((spacing / 2), 360 + (spacing / 2), spacing)
|
|
88
|
+
expected_el_range = np.arange(-90 + (spacing / 2), 90 + (spacing / 2), spacing)[
|
|
89
|
+
::-1
|
|
90
|
+
] # Note el order is reversed
|
|
91
|
+
|
|
92
|
+
npt.assert_allclose(az_range, expected_az_range, atol=1e-12)
|
|
93
|
+
npt.assert_allclose(el_range, expected_el_range, atol=1e-12)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def test_rewrap_even_spaced_el_az_grid_1d():
|
|
97
|
+
"""Test rewrap_even_spaced_el_az_grid function, without extra axis."""
|
|
98
|
+
orig_shape = (180 * 12, 360 * 12)
|
|
99
|
+
orig_grid = np.fromfunction(lambda i, j: i**2 + j, orig_shape, dtype=int)
|
|
100
|
+
raveled_values = orig_grid.ravel(order="F")
|
|
101
|
+
rewrapped_grid_infer_shape = spatial_utils.rewrap_even_spaced_el_az_grid(
|
|
102
|
+
raveled_values
|
|
103
|
+
)
|
|
104
|
+
rewrapped_grid_known_shape = spatial_utils.rewrap_even_spaced_el_az_grid(
|
|
105
|
+
raveled_values, shape=orig_shape
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
assert np.array_equal(rewrapped_grid_infer_shape, orig_grid)
|
|
109
|
+
assert np.array_equal(rewrapped_grid_known_shape, orig_grid)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def test_rewrap_even_spaced_el_az_grid_2d():
|
|
113
|
+
"""Test rewrap_even_spaced_el_az_grid function, with extra axis."""
|
|
114
|
+
orig_shape = (180 * 12, 360 * 12, 5)
|
|
115
|
+
orig_grid = np.fromfunction(lambda i, j, k: i**2 + j + k, orig_shape, dtype=int)
|
|
116
|
+
raveled_values = orig_grid.reshape(-1, 5, order="F")
|
|
117
|
+
rewrapped_grid_infer_shape = spatial_utils.rewrap_even_spaced_el_az_grid(
|
|
118
|
+
raveled_values, extra_axis=True
|
|
119
|
+
)
|
|
120
|
+
rewrapped_grid_known_shape = spatial_utils.rewrap_even_spaced_el_az_grid(
|
|
121
|
+
raveled_values, shape=orig_shape, extra_axis=True
|
|
122
|
+
)
|
|
123
|
+
assert raveled_values.shape == (180 * 12 * 360 * 12, 5)
|
|
124
|
+
assert np.array_equal(rewrapped_grid_infer_shape, orig_grid)
|
|
125
|
+
assert np.array_equal(rewrapped_grid_known_shape, orig_grid)
|
|
@@ -4,13 +4,13 @@ import numpy as np
|
|
|
4
4
|
import pandas as pd
|
|
5
5
|
import pytest
|
|
6
6
|
|
|
7
|
+
from imap_processing.ultra.constants import UltraConstants
|
|
7
8
|
from imap_processing.ultra.l1b.ultra_l1b_extended import (
|
|
8
9
|
CoinType,
|
|
9
10
|
StartType,
|
|
10
11
|
StopType,
|
|
11
12
|
calculate_etof_xc,
|
|
12
|
-
|
|
13
|
-
determine_species_ssd,
|
|
13
|
+
determine_species,
|
|
14
14
|
get_coincidence_positions,
|
|
15
15
|
get_ctof,
|
|
16
16
|
get_energy_pulse_height,
|
|
@@ -254,7 +254,7 @@ def test_get_unit_vector(de_dataset, yf_fixture):
|
|
|
254
254
|
def test_get_ssd_tof(de_dataset, yf_fixture):
|
|
255
255
|
"""Tests get_ssd_tof function."""
|
|
256
256
|
df_filt, _, _ = yf_fixture
|
|
257
|
-
df_ssd = df_filt[df_filt["StopType"]
|
|
257
|
+
df_ssd = df_filt[np.isin(df_filt["StopType"], [StopType.SSD.value])]
|
|
258
258
|
test_xf = df_filt["Xf"].astype("float").values
|
|
259
259
|
|
|
260
260
|
ssd_tof = get_ssd_tof(de_dataset, test_xf)
|
|
@@ -267,7 +267,7 @@ def test_get_ssd_tof(de_dataset, yf_fixture):
|
|
|
267
267
|
def test_get_energy_ssd(de_dataset, yf_fixture):
|
|
268
268
|
"""Tests get_energy_ssd function."""
|
|
269
269
|
df_filt, _, _ = yf_fixture
|
|
270
|
-
df_ssd = df_filt[df_filt["StopType"]
|
|
270
|
+
df_ssd = df_filt[np.isin(df_filt["StopType"], [StopType.SSD.value])]
|
|
271
271
|
_, _, ssd_number = get_ssd_back_position_and_tof_offset(de_dataset)
|
|
272
272
|
energy = get_energy_ssd(de_dataset, ssd_number)
|
|
273
273
|
test_energy = df_ssd["Energy"].astype("float")
|
|
@@ -278,7 +278,7 @@ def test_get_energy_ssd(de_dataset, yf_fixture):
|
|
|
278
278
|
def test_get_energy_pulse_height(de_dataset, yf_fixture):
|
|
279
279
|
"""Tests get_energy_ssd function."""
|
|
280
280
|
df_filt, _, _ = yf_fixture
|
|
281
|
-
df_ph = df_filt[df_filt["StopType"]
|
|
281
|
+
df_ph = df_filt[np.isin(df_filt["StopType"], [StopType.PH.value])]
|
|
282
282
|
ph_indices = np.nonzero(
|
|
283
283
|
np.isin(de_dataset["STOP_TYPE"], [StopType.Top.value, StopType.Bottom.value])
|
|
284
284
|
)[0]
|
|
@@ -297,18 +297,19 @@ def test_get_energy_pulse_height(de_dataset, yf_fixture):
|
|
|
297
297
|
def test_get_ctof(yf_fixture):
|
|
298
298
|
"""Tests get_ctof function."""
|
|
299
299
|
df_filt, _, _ = yf_fixture
|
|
300
|
+
df_filt = df_filt[df_filt["eTOF"].astype("str") != "FILL"]
|
|
301
|
+
df_filt = df_filt[df_filt["cTOF"].astype("float") > 0]
|
|
300
302
|
|
|
301
|
-
df_ph = df_filt[df_filt["StopType"]
|
|
303
|
+
df_ph = df_filt[np.isin(df_filt["StopType"], [StopType.PH.value])]
|
|
304
|
+
df_ssd = df_filt[np.isin(df_filt["StopType"], [StopType.SSD.value])]
|
|
302
305
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
ph_ctof = get_ctof(
|
|
306
|
+
ph_ctof, ph_magnitude_v = get_ctof(
|
|
306
307
|
df_ph["TOF"].astype("float").to_numpy(),
|
|
307
308
|
df_ph["r"].astype("float").to_numpy(),
|
|
308
309
|
"PH",
|
|
309
310
|
)
|
|
310
311
|
|
|
311
|
-
ssd_ctof = get_ctof(
|
|
312
|
+
ssd_ctof, ssd_magnitude_v = get_ctof(
|
|
312
313
|
df_ssd["TOF"].astype("float").to_numpy(),
|
|
313
314
|
df_ssd["r"].astype("float").to_numpy(),
|
|
314
315
|
"SSD",
|
|
@@ -320,33 +321,42 @@ def test_get_ctof(yf_fixture):
|
|
|
320
321
|
np.testing.assert_allclose(
|
|
321
322
|
ssd_ctof, df_ssd["cTOF"].astype("float"), atol=1e-05, rtol=0
|
|
322
323
|
)
|
|
324
|
+
np.testing.assert_allclose(
|
|
325
|
+
ph_magnitude_v, df_ph["vmag"].astype("float"), atol=1e-01, rtol=0
|
|
326
|
+
)
|
|
327
|
+
np.testing.assert_allclose(
|
|
328
|
+
ssd_magnitude_v, df_ssd["vmag"].astype("float"), atol=1e-01, rtol=0
|
|
329
|
+
)
|
|
323
330
|
|
|
324
331
|
|
|
325
|
-
def
|
|
326
|
-
"""Tests
|
|
332
|
+
def test_determine_species(yf_fixture):
|
|
333
|
+
"""Tests determine_species function."""
|
|
327
334
|
df_filt, _, _ = yf_fixture
|
|
328
|
-
df_ph = df_filt[df_filt["StopType"]
|
|
335
|
+
df_ph = df_filt[np.isin(df_filt["StopType"], [StopType.PH.value])]
|
|
336
|
+
df_ssd = df_filt[np.isin(df_filt["StopType"], [StopType.SSD.value])]
|
|
329
337
|
|
|
330
|
-
|
|
331
|
-
df_ph["Energy"].astype("float").to_numpy(),
|
|
338
|
+
species_bin_ph = determine_species(
|
|
332
339
|
df_ph["TOF"].astype("float").to_numpy(),
|
|
333
340
|
df_ph["r"].astype("float").to_numpy(),
|
|
341
|
+
"PH",
|
|
334
342
|
)
|
|
335
|
-
|
|
336
|
-
# TODO: add in bin values.
|
|
337
|
-
np.testing.assert_allclose(bin, np.zeros(len(bin)), atol=1e-05, rtol=0)
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
def test_determine_species_ssd(yf_fixture):
|
|
341
|
-
"""Tests determine_species_ssd function."""
|
|
342
|
-
df_filt, _, _ = yf_fixture
|
|
343
|
-
df_ssd = df_filt[df_filt["StopType"].isin(StopType.SSD.value)]
|
|
344
|
-
|
|
345
|
-
bin = determine_species_ssd(
|
|
346
|
-
df_ssd["Energy"].astype("float").to_numpy(),
|
|
343
|
+
species_bin_ssd = determine_species(
|
|
347
344
|
df_ssd["TOF"].astype("float").to_numpy(),
|
|
348
345
|
df_ssd["r"].astype("float").to_numpy(),
|
|
346
|
+
"SSD",
|
|
349
347
|
)
|
|
350
348
|
|
|
351
|
-
|
|
352
|
-
|
|
349
|
+
h_indices_ph = np.where(species_bin_ph == "H")[0]
|
|
350
|
+
ctof_indices_ph = np.where(
|
|
351
|
+
(df_ph["cTOF"].astype("float") > UltraConstants.CTOF_SPECIES_MIN)
|
|
352
|
+
& (df_ph["cTOF"].astype("float") < UltraConstants.CTOF_SPECIES_MAX)
|
|
353
|
+
)[0]
|
|
354
|
+
|
|
355
|
+
h_indices_ssd = np.where(species_bin_ssd == "H")[0]
|
|
356
|
+
ctof_indices_ssd = np.where(
|
|
357
|
+
(df_ssd["cTOF"].astype("float") > UltraConstants.CTOF_SPECIES_MIN)
|
|
358
|
+
& (df_ssd["cTOF"].astype("float") < UltraConstants.CTOF_SPECIES_MAX)
|
|
359
|
+
)[0]
|
|
360
|
+
|
|
361
|
+
np.testing.assert_array_equal(h_indices_ph, ctof_indices_ph)
|
|
362
|
+
np.testing.assert_array_equal(h_indices_ssd, ctof_indices_ssd)
|
|
@@ -8,12 +8,12 @@ from cdflib import CDF
|
|
|
8
8
|
from imap_processing import imap_module_directory
|
|
9
9
|
from imap_processing.ultra.l1c.ultra_l1c_pset_bins import (
|
|
10
10
|
build_energy_bins,
|
|
11
|
-
build_spatial_bins,
|
|
12
11
|
get_helio_exposure_times,
|
|
13
12
|
get_histogram,
|
|
14
13
|
get_pointing_frame_exposure_times,
|
|
15
14
|
get_pointing_frame_sensitivity,
|
|
16
15
|
)
|
|
16
|
+
from imap_processing.ultra.utils.spatial_utils import build_spatial_bins
|
|
17
17
|
|
|
18
18
|
BASE_PATH = imap_module_directory / "ultra" / "lookup_tables"
|
|
19
19
|
|
|
@@ -47,29 +47,6 @@ def test_build_energy_bins():
|
|
|
47
47
|
np.testing.assert_allclose(energy_bin_end[-1], 341.989, atol=1e-4)
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
def test_build_spatial_bins():
|
|
51
|
-
"""Tests build_spatial_bins function."""
|
|
52
|
-
az_bin_edges, el_bin_edges, az_bin_midpoints, el_bin_midpoints = (
|
|
53
|
-
build_spatial_bins()
|
|
54
|
-
)
|
|
55
|
-
|
|
56
|
-
assert az_bin_edges[0] == 0
|
|
57
|
-
assert az_bin_edges[-1] == 360
|
|
58
|
-
assert len(az_bin_edges) == 721
|
|
59
|
-
|
|
60
|
-
assert el_bin_edges[0] == -90
|
|
61
|
-
assert el_bin_edges[-1] == 90
|
|
62
|
-
assert len(el_bin_edges) == 361
|
|
63
|
-
|
|
64
|
-
assert len(az_bin_midpoints) == 720
|
|
65
|
-
np.testing.assert_allclose(az_bin_midpoints[0], 0.25, atol=1e-4)
|
|
66
|
-
np.testing.assert_allclose(az_bin_midpoints[-1], 359.75, atol=1e-4)
|
|
67
|
-
|
|
68
|
-
assert len(el_bin_midpoints) == 360
|
|
69
|
-
np.testing.assert_allclose(el_bin_midpoints[0], -89.75, atol=1e-4)
|
|
70
|
-
np.testing.assert_allclose(el_bin_midpoints[-1], 89.75, atol=1e-4)
|
|
71
|
-
|
|
72
|
-
|
|
73
50
|
def test_get_histogram(test_data):
|
|
74
51
|
"""Tests get_histogram function."""
|
|
75
52
|
v, energy = test_data
|
|
@@ -109,7 +86,7 @@ def test_get_pointing_frame_exposure_times():
|
|
|
109
86
|
|
|
110
87
|
@pytest.mark.external_kernel()
|
|
111
88
|
@pytest.mark.use_test_metakernel("imap_ena_sim_metakernel.template")
|
|
112
|
-
def
|
|
89
|
+
def test_get_helio_exposure_times():
|
|
113
90
|
"""Tests get_helio_exposure_times function."""
|
|
114
91
|
|
|
115
92
|
constant_exposure = BASE_PATH / "dps_grid45_compressed.cdf"
|
imap_processing/ultra/l1b/de.py
CHANGED
|
@@ -10,8 +10,7 @@ from imap_processing.ultra.l1b.ultra_l1b_annotated import (
|
|
|
10
10
|
)
|
|
11
11
|
from imap_processing.ultra.l1b.ultra_l1b_extended import (
|
|
12
12
|
StopType,
|
|
13
|
-
|
|
14
|
-
determine_species_ssd,
|
|
13
|
+
determine_species,
|
|
15
14
|
get_coincidence_positions,
|
|
16
15
|
get_ctof,
|
|
17
16
|
get_energy_pulse_height,
|
|
@@ -57,9 +56,8 @@ def calculate_de(de_dataset: xr.Dataset, name: str) -> xr.Dataset:
|
|
|
57
56
|
etof = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
|
|
58
57
|
ctof = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
|
|
59
58
|
energy = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
|
|
60
|
-
#
|
|
61
|
-
|
|
62
|
-
species_bin = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.uint8)
|
|
59
|
+
# TODO: Confirm with Ultra team what fill values and dtype we want.
|
|
60
|
+
species_bin = np.full(len(de_dataset["epoch"]), "UNKNOWN", dtype="U10")
|
|
63
61
|
t2 = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
|
|
64
62
|
|
|
65
63
|
# Drop events with invalid start type.
|
|
@@ -95,13 +93,11 @@ def calculate_de(de_dataset: xr.Dataset, name: str) -> xr.Dataset:
|
|
|
95
93
|
(xb[ph_indices], yb[ph_indices]),
|
|
96
94
|
d[ph_indices],
|
|
97
95
|
)
|
|
98
|
-
species_bin[ph_indices] =
|
|
99
|
-
energy[ph_indices], tof[ph_indices], r[ph_indices]
|
|
100
|
-
)
|
|
96
|
+
species_bin[ph_indices] = determine_species(tof[ph_indices], r[ph_indices], "PH")
|
|
101
97
|
etof[ph_indices], xc[ph_indices] = get_coincidence_positions(
|
|
102
98
|
de_dataset.isel(epoch=ph_indices), t2[ph_indices], f"ultra{sensor}"
|
|
103
99
|
)
|
|
104
|
-
ctof[ph_indices] = get_ctof(tof[ph_indices], r[ph_indices], "PH")
|
|
100
|
+
ctof[ph_indices], _ = get_ctof(tof[ph_indices], r[ph_indices], "PH")
|
|
105
101
|
|
|
106
102
|
# SSD
|
|
107
103
|
ssd_indices = np.nonzero(np.isin(de_dataset["STOP_TYPE"], StopType.SSD.value))[0]
|
|
@@ -119,12 +115,10 @@ def calculate_de(de_dataset: xr.Dataset, name: str) -> xr.Dataset:
|
|
|
119
115
|
(xb[ssd_indices], yb[ssd_indices]),
|
|
120
116
|
d[ssd_indices],
|
|
121
117
|
)
|
|
122
|
-
species_bin[ssd_indices] =
|
|
123
|
-
|
|
124
|
-
tof[ssd_indices],
|
|
125
|
-
r[ssd_indices],
|
|
118
|
+
species_bin[ssd_indices] = determine_species(
|
|
119
|
+
tof[ssd_indices], r[ssd_indices], "SSD"
|
|
126
120
|
)
|
|
127
|
-
ctof[ssd_indices] = get_ctof(tof[ssd_indices], r[ssd_indices], "SSD")
|
|
121
|
+
ctof[ssd_indices], _ = get_ctof(tof[ssd_indices], r[ssd_indices], "SSD")
|
|
128
122
|
|
|
129
123
|
# Combine ph_yb and ssd_yb along with their indices
|
|
130
124
|
de_dict["x_front"] = xf.astype(np.float32)
|
|
@@ -648,9 +648,11 @@ def get_energy_ssd(de_dataset: xarray.Dataset, ssd: np.ndarray) -> NDArray[np.fl
|
|
|
648
648
|
return energy_norm
|
|
649
649
|
|
|
650
650
|
|
|
651
|
-
def get_ctof(
|
|
651
|
+
def get_ctof(
|
|
652
|
+
tof: np.ndarray, path_length: np.ndarray, type: str
|
|
653
|
+
) -> tuple[NDArray, NDArray]:
|
|
652
654
|
"""
|
|
653
|
-
Calculate the corrected TOF.
|
|
655
|
+
Calculate the corrected TOF and the magnitude of the particle velocity.
|
|
654
656
|
|
|
655
657
|
The corrected TOF (ctof) is the TOF normalized with respect
|
|
656
658
|
to a fixed distance dmin between the front and back detectors.
|
|
@@ -666,33 +668,35 @@ def get_ctof(tof: np.ndarray, path_length: np.ndarray, type: str) -> NDArray:
|
|
|
666
668
|
path_length : np.ndarray
|
|
667
669
|
Path length (r) (hundredths of a millimeter).
|
|
668
670
|
type : str
|
|
669
|
-
Type of event, either "
|
|
671
|
+
Type of event, either "PH" or "SSD".
|
|
670
672
|
|
|
671
673
|
Returns
|
|
672
674
|
-------
|
|
673
675
|
ctof : np.ndarray
|
|
674
676
|
Corrected TOF (tenths of a ns).
|
|
677
|
+
magnitude_v : np.ndarray
|
|
678
|
+
Magnitude of the particle velocity (km/s).
|
|
675
679
|
"""
|
|
676
680
|
dmin_ctof = getattr(UltraConstants, f"DMIN_{type}_CTOF")
|
|
677
681
|
|
|
678
682
|
# Multiply times 100 to convert to hundredths of a millimeter.
|
|
679
683
|
ctof = tof * dmin_ctof * 100 / path_length
|
|
680
684
|
|
|
681
|
-
|
|
685
|
+
# Convert from mm/0.1ns to km/s.
|
|
686
|
+
magnitude_v = dmin_ctof / ctof * 1e4
|
|
682
687
|
|
|
688
|
+
return ctof, magnitude_v
|
|
683
689
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
) -> NDArray:
|
|
690
|
+
|
|
691
|
+
def determine_species(tof: np.ndarray, path_length: np.ndarray, type: str) -> NDArray:
|
|
687
692
|
"""
|
|
688
693
|
Determine the species for pulse-height events.
|
|
689
694
|
|
|
690
|
-
Species is determined from the particle
|
|
695
|
+
Species is determined from the particle velocity.
|
|
691
696
|
For velocity, the particle TOF is normalized with respect
|
|
692
697
|
to a fixed distance dmin between the front and back detectors.
|
|
693
698
|
The normalized TOF is termed the corrected TOF (ctof).
|
|
694
|
-
Particle species are determined from
|
|
695
|
-
the energy and ctof using a lookup table.
|
|
699
|
+
Particle species are determined from ctof using thresholds.
|
|
696
700
|
|
|
697
701
|
Further description is available on pages 42-44 of
|
|
698
702
|
IMAP-Ultra Flight Software Specification document
|
|
@@ -700,72 +704,27 @@ def determine_species_pulse_height(
|
|
|
700
704
|
|
|
701
705
|
Parameters
|
|
702
706
|
----------
|
|
703
|
-
energy : np.ndarray
|
|
704
|
-
Energy from the SSD event (keV).
|
|
705
|
-
tof : np.ndarray
|
|
706
|
-
Time of flight of the SSD event (tenths of a nanosecond).
|
|
707
|
-
path_length : np.ndarray
|
|
708
|
-
Path length (r) (hundredths of a millimeter).
|
|
709
|
-
|
|
710
|
-
Returns
|
|
711
|
-
-------
|
|
712
|
-
bin : np.array
|
|
713
|
-
Species bin.
|
|
714
|
-
"""
|
|
715
|
-
# PH event TOF normalization to Z axis
|
|
716
|
-
ctof = get_ctof(tof, path_length, "PH")
|
|
717
|
-
# TODO: need lookup tables
|
|
718
|
-
# placeholder
|
|
719
|
-
bin = np.zeros(len(ctof))
|
|
720
|
-
# bin = PHxTOFSpecies[ctof, energy]
|
|
721
|
-
|
|
722
|
-
return bin
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
def determine_species_ssd(
|
|
726
|
-
energy: np.ndarray, tof: np.ndarray, path_length: np.ndarray
|
|
727
|
-
) -> NDArray:
|
|
728
|
-
"""
|
|
729
|
-
Determine the species for SSD events.
|
|
730
|
-
|
|
731
|
-
Species is determined from the particle's energy and velocity.
|
|
732
|
-
For velocity, the particle's TOF is normalized with respect
|
|
733
|
-
to a fixed distance dmin between the front and back detectors.
|
|
734
|
-
For SSD events, an adjustment is also made to the path length
|
|
735
|
-
to account for the shorter distances that such events
|
|
736
|
-
travel to reach the detector. The normalized TOF is termed
|
|
737
|
-
the corrected tof (ctof). Particle species are determined from
|
|
738
|
-
the energy and cTOF using a lookup table.
|
|
739
|
-
|
|
740
|
-
Further description is available on pages 42-44 of
|
|
741
|
-
IMAP-Ultra Flight Software Specification document
|
|
742
|
-
(7523-9009_Rev_-.pdf).
|
|
743
|
-
|
|
744
|
-
Parameters
|
|
745
|
-
----------
|
|
746
|
-
energy : np.ndarray
|
|
747
|
-
Energy from the SSD event (keV).
|
|
748
707
|
tof : np.ndarray
|
|
749
708
|
Time of flight of the SSD event (tenths of a nanosecond).
|
|
750
709
|
path_length : np.ndarray
|
|
751
710
|
Path length (r) (hundredths of a millimeter).
|
|
711
|
+
type : str
|
|
712
|
+
Type of data (PH or SSD).
|
|
752
713
|
|
|
753
714
|
Returns
|
|
754
715
|
-------
|
|
755
|
-
|
|
716
|
+
species_bin : np.array
|
|
756
717
|
Species bin.
|
|
757
718
|
"""
|
|
758
|
-
#
|
|
759
|
-
ctof = get_ctof(tof, path_length,
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
#
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
return bin
|
|
719
|
+
# Event TOF normalization to Z axis
|
|
720
|
+
ctof, _ = get_ctof(tof, path_length, type)
|
|
721
|
+
# Initialize bin array
|
|
722
|
+
species_bin = np.full(len(ctof), "UNKNOWN", dtype="U10")
|
|
723
|
+
|
|
724
|
+
# Assign "H" to bins where cTOF is within the specified range
|
|
725
|
+
species_bin[
|
|
726
|
+
(ctof > UltraConstants.CTOF_SPECIES_MIN)
|
|
727
|
+
& (ctof < UltraConstants.CTOF_SPECIES_MAX)
|
|
728
|
+
] = "H"
|
|
729
|
+
|
|
730
|
+
return species_bin
|
|
@@ -13,6 +13,7 @@ from imap_processing.spice.geometry import (
|
|
|
13
13
|
spherical_to_cartesian,
|
|
14
14
|
)
|
|
15
15
|
from imap_processing.ultra.constants import UltraConstants
|
|
16
|
+
from imap_processing.ultra.utils.spatial_utils import build_spatial_bins
|
|
16
17
|
|
|
17
18
|
# TODO: add species binning.
|
|
18
19
|
|
|
@@ -47,42 +48,6 @@ def build_energy_bins() -> tuple[list[tuple[float, float]], np.ndarray]:
|
|
|
47
48
|
return intervals, energy_midpoints
|
|
48
49
|
|
|
49
50
|
|
|
50
|
-
def build_spatial_bins(
|
|
51
|
-
az_spacing: float = 0.5,
|
|
52
|
-
el_spacing: float = 0.5,
|
|
53
|
-
) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
|
|
54
|
-
"""
|
|
55
|
-
Build spatial bin boundaries for azimuth and elevation.
|
|
56
|
-
|
|
57
|
-
Parameters
|
|
58
|
-
----------
|
|
59
|
-
az_spacing : float, optional
|
|
60
|
-
The azimuth bin spacing in degrees (default is 0.5 degrees).
|
|
61
|
-
el_spacing : float, optional
|
|
62
|
-
The elevation bin spacing in degrees (default is 0.5 degrees).
|
|
63
|
-
|
|
64
|
-
Returns
|
|
65
|
-
-------
|
|
66
|
-
az_bin_edges : np.ndarray
|
|
67
|
-
Array of azimuth bin boundary values.
|
|
68
|
-
el_bin_edges : np.ndarray
|
|
69
|
-
Array of elevation bin boundary values.
|
|
70
|
-
az_bin_midpoints : np.ndarray
|
|
71
|
-
Array of azimuth bin midpoint values.
|
|
72
|
-
el_bin_midpoints : np.ndarray
|
|
73
|
-
Array of elevation bin midpoint values.
|
|
74
|
-
"""
|
|
75
|
-
# Azimuth bins from 0 to 360 degrees.
|
|
76
|
-
az_bin_edges = np.arange(0, 360 + az_spacing, az_spacing)
|
|
77
|
-
az_bin_midpoints = az_bin_edges[:-1] + az_spacing / 2 # Midpoints between edges
|
|
78
|
-
|
|
79
|
-
# Elevation bins from -90 to 90 degrees.
|
|
80
|
-
el_bin_edges = np.arange(-90, 90 + el_spacing, el_spacing)
|
|
81
|
-
el_bin_midpoints = el_bin_edges[:-1] + el_spacing / 2 # Midpoints between edges
|
|
82
|
-
|
|
83
|
-
return az_bin_edges, el_bin_edges, az_bin_midpoints, el_bin_midpoints
|
|
84
|
-
|
|
85
|
-
|
|
86
51
|
def get_histogram(
|
|
87
52
|
vhat: tuple[np.ndarray, np.ndarray, np.ndarray],
|
|
88
53
|
energy: np.ndarray,
|