imap-processing 0.19.0__py3-none-any.whl → 0.19.3__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/cdf/config/imap_codice_global_cdf_attrs.yaml +6 -0
- imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +31 -894
- imap_processing/cdf/config/imap_codice_l1b_variable_attrs.yaml +279 -255
- imap_processing/cdf/config/imap_enamaps_l2-common_variable_attrs.yaml +55 -0
- imap_processing/cdf/config/imap_enamaps_l2-healpix_variable_attrs.yaml +29 -0
- imap_processing/cdf/config/imap_enamaps_l2-rectangular_variable_attrs.yaml +32 -0
- imap_processing/cdf/config/imap_glows_l1b_variable_attrs.yaml +3 -1
- imap_processing/cdf/config/imap_lo_global_cdf_attrs.yaml +5 -4
- imap_processing/cdf/config/imap_ultra_global_cdf_attrs.yaml +28 -16
- imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +33 -31
- imap_processing/cdf/config/imap_ultra_l1c_variable_attrs.yaml +61 -1
- imap_processing/cli.py +62 -71
- imap_processing/codice/codice_l0.py +2 -1
- imap_processing/codice/codice_l1a.py +47 -49
- imap_processing/codice/codice_l1b.py +42 -32
- imap_processing/codice/codice_l2.py +105 -7
- imap_processing/codice/constants.py +50 -8
- imap_processing/codice/data/lo_stepping_values.csv +1 -1
- imap_processing/ena_maps/ena_maps.py +39 -18
- imap_processing/ena_maps/utils/corrections.py +291 -0
- imap_processing/ena_maps/utils/map_utils.py +20 -4
- imap_processing/glows/l1b/glows_l1b.py +38 -23
- imap_processing/glows/l1b/glows_l1b_data.py +10 -11
- imap_processing/hi/hi_l1c.py +4 -109
- imap_processing/hi/hi_l2.py +34 -23
- imap_processing/hi/utils.py +109 -0
- imap_processing/ialirt/l0/ialirt_spice.py +1 -1
- imap_processing/ialirt/l0/parse_mag.py +18 -4
- imap_processing/ialirt/l0/process_hit.py +9 -4
- imap_processing/ialirt/l0/process_swapi.py +9 -4
- imap_processing/ialirt/l0/process_swe.py +9 -4
- imap_processing/ialirt/utils/create_xarray.py +1 -1
- imap_processing/lo/ancillary_data/imap_lo_hydrogen-geometric-factor_v001.csv +75 -0
- imap_processing/lo/ancillary_data/imap_lo_oxygen-geometric-factor_v001.csv +75 -0
- imap_processing/lo/l1b/lo_l1b.py +90 -16
- imap_processing/lo/l1c/lo_l1c.py +164 -50
- imap_processing/lo/l2/lo_l2.py +941 -127
- imap_processing/mag/l1d/mag_l1d_data.py +36 -3
- imap_processing/mag/l2/mag_l2.py +2 -0
- imap_processing/mag/l2/mag_l2_data.py +4 -3
- imap_processing/quality_flags.py +14 -0
- imap_processing/spice/geometry.py +13 -8
- imap_processing/spice/pointing_frame.py +4 -2
- imap_processing/spice/repoint.py +49 -0
- imap_processing/ultra/constants.py +29 -0
- imap_processing/ultra/l0/decom_tools.py +58 -46
- imap_processing/ultra/l0/decom_ultra.py +21 -9
- imap_processing/ultra/l0/ultra_utils.py +4 -4
- imap_processing/ultra/l1b/badtimes.py +35 -11
- imap_processing/ultra/l1b/de.py +15 -9
- imap_processing/ultra/l1b/extendedspin.py +24 -12
- imap_processing/ultra/l1b/goodtimes.py +112 -0
- imap_processing/ultra/l1b/lookup_utils.py +1 -1
- imap_processing/ultra/l1b/ultra_l1b.py +7 -7
- imap_processing/ultra/l1b/ultra_l1b_culling.py +8 -4
- imap_processing/ultra/l1b/ultra_l1b_extended.py +79 -43
- imap_processing/ultra/l1c/helio_pset.py +68 -39
- imap_processing/ultra/l1c/l1c_lookup_utils.py +45 -12
- imap_processing/ultra/l1c/spacecraft_pset.py +81 -37
- imap_processing/ultra/l1c/ultra_l1c.py +27 -22
- imap_processing/ultra/l1c/ultra_l1c_culling.py +7 -0
- imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +41 -41
- imap_processing/ultra/l2/ultra_l2.py +75 -18
- imap_processing/ultra/utils/ultra_l1_utils.py +10 -5
- {imap_processing-0.19.0.dist-info → imap_processing-0.19.3.dist-info}/METADATA +2 -2
- {imap_processing-0.19.0.dist-info → imap_processing-0.19.3.dist-info}/RECORD +71 -69
- imap_processing/ultra/l1b/cullingmask.py +0 -90
- imap_processing/ultra/l1c/histogram.py +0 -36
- /imap_processing/glows/ancillary/{imap_glows_pipeline_settings_20250923_v002.json → imap_glows_pipeline-settings_20250923_v002.json} +0 -0
- {imap_processing-0.19.0.dist-info → imap_processing-0.19.3.dist-info}/LICENSE +0 -0
- {imap_processing-0.19.0.dist-info → imap_processing-0.19.3.dist-info}/WHEEL +0 -0
- {imap_processing-0.19.0.dist-info → imap_processing-0.19.3.dist-info}/entry_points.txt +0 -0
imap_processing/ultra/l1b/de.py
CHANGED
|
@@ -42,6 +42,7 @@ from imap_processing.ultra.l1b.ultra_l1b_extended import (
|
|
|
42
42
|
from imap_processing.ultra.utils.ultra_l1_utils import create_dataset
|
|
43
43
|
|
|
44
44
|
FILLVAL_UINT8 = 255
|
|
45
|
+
FILLVAL_UINT32 = 4294967295
|
|
45
46
|
FILLVAL_FLOAT32 = -1.0e31
|
|
46
47
|
|
|
47
48
|
|
|
@@ -82,7 +83,6 @@ def calculate_de(
|
|
|
82
83
|
"event_type",
|
|
83
84
|
"de_event_met",
|
|
84
85
|
"phase_angle",
|
|
85
|
-
"spin",
|
|
86
86
|
]
|
|
87
87
|
dataset_keys = [
|
|
88
88
|
"coin_type",
|
|
@@ -90,7 +90,6 @@ def calculate_de(
|
|
|
90
90
|
"stop_type",
|
|
91
91
|
"shcoarse",
|
|
92
92
|
"phase_angle",
|
|
93
|
-
"spin",
|
|
94
93
|
]
|
|
95
94
|
|
|
96
95
|
de_dict.update(
|
|
@@ -127,6 +126,7 @@ def calculate_de(
|
|
|
127
126
|
magnitude_v = np.full(len(de_dataset["epoch"]), FILLVAL_FLOAT32, dtype=np.float32)
|
|
128
127
|
energy = np.full(len(de_dataset["epoch"]), FILLVAL_FLOAT32, dtype=np.float32)
|
|
129
128
|
e_bin = np.full(len(de_dataset["epoch"]), FILLVAL_UINT8, dtype=np.uint8)
|
|
129
|
+
e_bin_l1a = np.full(len(de_dataset["epoch"]), FILLVAL_UINT8, dtype=np.uint8)
|
|
130
130
|
species_bin = np.full(len(de_dataset["epoch"]), FILLVAL_UINT8, dtype=np.uint8)
|
|
131
131
|
t2 = np.full(len(de_dataset["epoch"]), FILLVAL_FLOAT32, dtype=np.float32)
|
|
132
132
|
event_times = np.full(len(de_dataset["epoch"]), FILLVAL_FLOAT32, dtype=np.float32)
|
|
@@ -143,6 +143,7 @@ def calculate_de(
|
|
|
143
143
|
quality_flags = np.full(
|
|
144
144
|
de_dataset["epoch"].shape, ImapDEOutliersUltraFlags.NONE.value, dtype=np.uint16
|
|
145
145
|
)
|
|
146
|
+
|
|
146
147
|
scattering_quality_flags = np.full(
|
|
147
148
|
de_dataset["epoch"].shape,
|
|
148
149
|
ImapDEScatteringUltraFlags.NONE.value,
|
|
@@ -196,7 +197,6 @@ def calculate_de(
|
|
|
196
197
|
(xb[ph_indices], yb[ph_indices]),
|
|
197
198
|
d[ph_indices],
|
|
198
199
|
)
|
|
199
|
-
species_bin[ph_indices] = determine_species(tof[ph_indices], r[ph_indices], "PH")
|
|
200
200
|
etof[ph_indices], xc[ph_indices] = get_coincidence_positions(
|
|
201
201
|
de_dataset.isel(epoch=ph_indices),
|
|
202
202
|
t2[ph_indices],
|
|
@@ -213,8 +213,13 @@ def calculate_de(
|
|
|
213
213
|
etof[ph_indices],
|
|
214
214
|
xc[ph_indices],
|
|
215
215
|
xb[ph_indices],
|
|
216
|
+
de_dataset["stop_north_tdc"][ph_indices].values,
|
|
217
|
+
de_dataset["stop_south_tdc"][ph_indices].values,
|
|
218
|
+
de_dataset["stop_east_tdc"][ph_indices].values,
|
|
219
|
+
de_dataset["stop_west_tdc"][ph_indices].values,
|
|
216
220
|
f"ultra{sensor}",
|
|
217
221
|
ancillary_files,
|
|
222
|
+
quality_flags[ph_indices],
|
|
218
223
|
)
|
|
219
224
|
e_bin[ph_indices] = determine_ebin_pulse_height(
|
|
220
225
|
energy[ph_indices],
|
|
@@ -224,6 +229,7 @@ def calculate_de(
|
|
|
224
229
|
coinphvalid,
|
|
225
230
|
ancillary_files,
|
|
226
231
|
)
|
|
232
|
+
species_bin[ph_indices] = determine_species(e_bin[ph_indices], "PH")
|
|
227
233
|
ctof[ph_indices], magnitude_v[ph_indices] = get_ctof(
|
|
228
234
|
tof[ph_indices], r[ph_indices], "PH"
|
|
229
235
|
)
|
|
@@ -257,9 +263,7 @@ def calculate_de(
|
|
|
257
263
|
f"ultra{sensor}",
|
|
258
264
|
ancillary_files,
|
|
259
265
|
)
|
|
260
|
-
species_bin[ssd_indices] = determine_species(
|
|
261
|
-
tof[ssd_indices], r[ssd_indices], "SSD"
|
|
262
|
-
)
|
|
266
|
+
species_bin[ssd_indices] = determine_species(e_bin[ssd_indices], "SSD")
|
|
263
267
|
ctof[ssd_indices], magnitude_v[ssd_indices] = get_ctof(
|
|
264
268
|
tof[ssd_indices], r[ssd_indices], "SSD"
|
|
265
269
|
)
|
|
@@ -289,7 +293,6 @@ def calculate_de(
|
|
|
289
293
|
de_dict["tof_start_stop"][valid_indices],
|
|
290
294
|
)
|
|
291
295
|
)
|
|
292
|
-
de_dict["direct_event_velocity"] = velocities.astype(np.float32)
|
|
293
296
|
de_dict["direct_event_unit_velocity"] = v_hat.astype(np.float32)
|
|
294
297
|
de_dict["direct_event_unit_position"] = r_hat.astype(np.float32)
|
|
295
298
|
|
|
@@ -298,7 +301,10 @@ def calculate_de(
|
|
|
298
301
|
)
|
|
299
302
|
de_dict["tof_energy"] = tof_energy
|
|
300
303
|
de_dict["energy"] = energy
|
|
301
|
-
de_dict["
|
|
304
|
+
de_dict["computed_ebin"] = e_bin
|
|
305
|
+
valid_ebin = de_dataset["bin"].values != FILLVAL_UINT32
|
|
306
|
+
e_bin_l1a[valid_ebin] = de_dataset["bin"].values[valid_ebin]
|
|
307
|
+
de_dict["ebin"] = e_bin_l1a
|
|
302
308
|
de_dict["species"] = species_bin
|
|
303
309
|
|
|
304
310
|
# Annotated Events.
|
|
@@ -313,7 +319,7 @@ def calculate_de(
|
|
|
313
319
|
helio_velocity[valid_events],
|
|
314
320
|
) = get_annotated_particle_velocity(
|
|
315
321
|
event_times[valid_events],
|
|
316
|
-
|
|
322
|
+
velocities.astype(np.float32)[valid_events],
|
|
317
323
|
ultra_frame,
|
|
318
324
|
SpiceFrame.IMAP_DPS,
|
|
319
325
|
SpiceFrame.IMAP_SPACECRAFT,
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import numpy as np
|
|
4
4
|
import xarray as xr
|
|
5
|
+
from numpy.typing import NDArray
|
|
5
6
|
|
|
6
7
|
from imap_processing.ultra.l1b.ultra_l1b_culling import (
|
|
7
8
|
count_rejected_events_per_spin,
|
|
@@ -15,6 +16,7 @@ from imap_processing.ultra.l1b.ultra_l1b_culling import (
|
|
|
15
16
|
from imap_processing.ultra.utils.ultra_l1_utils import create_dataset
|
|
16
17
|
|
|
17
18
|
FILLVAL_UINT16 = 65535
|
|
19
|
+
FILLVAL_FLOAT32 = -1.0e31
|
|
18
20
|
|
|
19
21
|
|
|
20
22
|
def calculate_extendedspin(
|
|
@@ -44,7 +46,7 @@ def calculate_extendedspin(
|
|
|
44
46
|
de_dataset = dict_datasets[f"imap_ultra_l1b_{instrument_id}sensor-de"]
|
|
45
47
|
|
|
46
48
|
extendedspin_dict = {}
|
|
47
|
-
rates_qf, spin,
|
|
49
|
+
rates_qf, spin, energy_bin_geometric_mean, n_sigma_per_energy = flag_rates(
|
|
48
50
|
de_dataset["spin"].values,
|
|
49
51
|
de_dataset["energy"].values,
|
|
50
52
|
)
|
|
@@ -58,12 +60,6 @@ def calculate_extendedspin(
|
|
|
58
60
|
hk_qf = flag_hk(de_dataset["spin"].values)
|
|
59
61
|
inst_qf = flag_imap_instruments(de_dataset["spin"].values)
|
|
60
62
|
|
|
61
|
-
# Get the first epoch for each spin.
|
|
62
|
-
mask = xr.DataArray(np.isin(de_dataset["spin"], spin), dims="epoch")
|
|
63
|
-
filtered_dataset = de_dataset.where(mask, drop=True)
|
|
64
|
-
_, first_indices = np.unique(filtered_dataset["spin"].values, return_index=True)
|
|
65
|
-
first_epochs = filtered_dataset["epoch"].values[first_indices]
|
|
66
|
-
|
|
67
63
|
# Get the number of pulses per spin.
|
|
68
64
|
pulses = get_pulses_per_spin(rates_dataset)
|
|
69
65
|
|
|
@@ -75,18 +71,34 @@ def calculate_extendedspin(
|
|
|
75
71
|
de_dataset["quality_outliers"].values,
|
|
76
72
|
)
|
|
77
73
|
# These will be the coordinates.
|
|
78
|
-
extendedspin_dict["epoch"] = first_epochs
|
|
79
74
|
extendedspin_dict["spin_number"] = spin
|
|
80
|
-
extendedspin_dict["energy_bin_geometric_mean"] =
|
|
75
|
+
extendedspin_dict["energy_bin_geometric_mean"] = energy_bin_geometric_mean
|
|
81
76
|
|
|
82
77
|
extendedspin_dict["ena_rates"] = count_rates
|
|
83
78
|
extendedspin_dict["ena_rates_threshold"] = n_sigma_per_energy
|
|
84
79
|
extendedspin_dict["spin_start_time"] = spin_starttime
|
|
85
80
|
extendedspin_dict["spin_period"] = spin_period
|
|
86
81
|
extendedspin_dict["spin_rate"] = spin_rates
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
82
|
+
|
|
83
|
+
# Get index of pulses.unique_spins corresponding to each spin.
|
|
84
|
+
idx: NDArray[np.intp] = np.searchsorted(pulses.unique_spins, spin)
|
|
85
|
+
|
|
86
|
+
# Validate that the spin values match
|
|
87
|
+
valid = (idx < pulses.unique_spins.size) & (pulses.unique_spins[idx] == spin)
|
|
88
|
+
|
|
89
|
+
start_per_spin = np.full(len(spin), FILLVAL_FLOAT32, dtype=np.float32)
|
|
90
|
+
stop_per_spin = np.full(len(spin), FILLVAL_FLOAT32, dtype=np.float32)
|
|
91
|
+
coin_per_spin = np.full(len(spin), FILLVAL_FLOAT32, dtype=np.float32)
|
|
92
|
+
|
|
93
|
+
# Fill only the valid ones
|
|
94
|
+
start_per_spin[valid] = pulses.start_per_spin[idx[valid]]
|
|
95
|
+
stop_per_spin[valid] = pulses.stop_per_spin[idx[valid]]
|
|
96
|
+
coin_per_spin[valid] = pulses.coin_per_spin[idx[valid]]
|
|
97
|
+
|
|
98
|
+
# account for rates spins which are not in the direct event spins
|
|
99
|
+
extendedspin_dict["start_pulses_per_spin"] = start_per_spin
|
|
100
|
+
extendedspin_dict["stop_pulses_per_spin"] = stop_per_spin
|
|
101
|
+
extendedspin_dict["coin_pulses_per_spin"] = coin_per_spin
|
|
90
102
|
extendedspin_dict["rejected_events_per_spin"] = rejected_counts
|
|
91
103
|
extendedspin_dict["quality_attitude"] = attitude_qf
|
|
92
104
|
extendedspin_dict["quality_ena_rates"] = rates_qf
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""Calculate Goodtimes."""
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import xarray as xr
|
|
5
|
+
|
|
6
|
+
from imap_processing.ultra.l1b.quality_flag_filters import SPIN_QUALITY_FLAG_FILTERS
|
|
7
|
+
from imap_processing.ultra.utils.ultra_l1_utils import create_dataset, extract_data_dict
|
|
8
|
+
|
|
9
|
+
FILLVAL_UINT16 = 65535
|
|
10
|
+
FILLVAL_FLOAT32 = -1.0e31
|
|
11
|
+
FILLVAL_FLOAT64 = -1.0e31
|
|
12
|
+
FILLVAL_UINT32 = 4294967295
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def calculate_goodtimes(extendedspin_dataset: xr.Dataset, name: str) -> xr.Dataset:
|
|
16
|
+
"""
|
|
17
|
+
Create dataset with defined datatype for Goodtimes Data.
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
extendedspin_dataset : xarray.Dataset
|
|
22
|
+
Dataset containing the data.
|
|
23
|
+
name : str
|
|
24
|
+
Name of the dataset.
|
|
25
|
+
|
|
26
|
+
Returns
|
|
27
|
+
-------
|
|
28
|
+
goodtimes_dataset : xarray.Dataset
|
|
29
|
+
Dataset containing the extendedspin data that remains after culling.
|
|
30
|
+
"""
|
|
31
|
+
n_bins = extendedspin_dataset.dims["energy_bin_geometric_mean"]
|
|
32
|
+
# If the spin rate was too high or low then the spin should be thrown out.
|
|
33
|
+
# If the rates at any energy level are too high then throw out the entire spin.
|
|
34
|
+
good_mask = (
|
|
35
|
+
(
|
|
36
|
+
extendedspin_dataset["quality_attitude"]
|
|
37
|
+
& sum(flag.value for flag in SPIN_QUALITY_FLAG_FILTERS["quality_attitude"])
|
|
38
|
+
)
|
|
39
|
+
== 0
|
|
40
|
+
) & (
|
|
41
|
+
(
|
|
42
|
+
(
|
|
43
|
+
extendedspin_dataset["quality_ena_rates"]
|
|
44
|
+
& sum(
|
|
45
|
+
flag.value
|
|
46
|
+
for flag in SPIN_QUALITY_FLAG_FILTERS["quality_ena_rates"]
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
== 0
|
|
50
|
+
).all(dim="energy_bin_geometric_mean")
|
|
51
|
+
)
|
|
52
|
+
filtered_dataset = extendedspin_dataset.sel(
|
|
53
|
+
spin_number=extendedspin_dataset["spin_number"][good_mask]
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
data_dict = extract_data_dict(filtered_dataset)
|
|
57
|
+
|
|
58
|
+
goodtimes_dataset = create_dataset(data_dict, name, "l1b")
|
|
59
|
+
|
|
60
|
+
if goodtimes_dataset["spin_number"].size == 0:
|
|
61
|
+
goodtimes_dataset = goodtimes_dataset.drop_dims("spin_number")
|
|
62
|
+
goodtimes_dataset = goodtimes_dataset.expand_dims(spin_number=[FILLVAL_UINT32])
|
|
63
|
+
goodtimes_dataset["spin_start_time"] = xr.DataArray(
|
|
64
|
+
np.array([FILLVAL_FLOAT64], dtype="float64"), dims=["spin_number"]
|
|
65
|
+
)
|
|
66
|
+
goodtimes_dataset["spin_period"] = xr.DataArray(
|
|
67
|
+
np.array([FILLVAL_FLOAT64], dtype="float64"), dims=["spin_number"]
|
|
68
|
+
)
|
|
69
|
+
goodtimes_dataset["spin_rate"] = xr.DataArray(
|
|
70
|
+
np.array([FILLVAL_FLOAT64], dtype="float64"), dims=["spin_number"]
|
|
71
|
+
)
|
|
72
|
+
goodtimes_dataset["start_pulses_per_spin"] = xr.DataArray(
|
|
73
|
+
np.array([FILLVAL_FLOAT32], dtype="float32"),
|
|
74
|
+
dims=["spin_number"],
|
|
75
|
+
)
|
|
76
|
+
goodtimes_dataset["stop_pulses_per_spin"] = xr.DataArray(
|
|
77
|
+
np.array([FILLVAL_FLOAT32], dtype="float32"),
|
|
78
|
+
dims=["spin_number"],
|
|
79
|
+
)
|
|
80
|
+
goodtimes_dataset["coin_pulses_per_spin"] = xr.DataArray(
|
|
81
|
+
np.array([FILLVAL_FLOAT32], dtype="float32"),
|
|
82
|
+
dims=["spin_number"],
|
|
83
|
+
)
|
|
84
|
+
goodtimes_dataset["rejected_events_per_spin"] = xr.DataArray(
|
|
85
|
+
np.array([FILLVAL_UINT32], dtype="uint32"),
|
|
86
|
+
dims=["spin_number"],
|
|
87
|
+
)
|
|
88
|
+
goodtimes_dataset["quality_attitude"] = xr.DataArray(
|
|
89
|
+
np.array([FILLVAL_UINT16], dtype="uint16"), dims=["spin_number"]
|
|
90
|
+
)
|
|
91
|
+
goodtimes_dataset["quality_hk"] = xr.DataArray(
|
|
92
|
+
np.array([FILLVAL_UINT16], dtype="uint16"),
|
|
93
|
+
dims=["spin_number"],
|
|
94
|
+
)
|
|
95
|
+
goodtimes_dataset["quality_instruments"] = xr.DataArray(
|
|
96
|
+
np.array([FILLVAL_UINT16], dtype="uint16"),
|
|
97
|
+
dims=["spin_number"],
|
|
98
|
+
)
|
|
99
|
+
goodtimes_dataset["quality_ena_rates"] = (
|
|
100
|
+
("energy_bin_geometric_mean", "spin_number"),
|
|
101
|
+
np.full((n_bins, 1), FILLVAL_UINT16, dtype="uint16"),
|
|
102
|
+
)
|
|
103
|
+
goodtimes_dataset["ena_rates"] = (
|
|
104
|
+
("energy_bin_geometric_mean", "spin_number"),
|
|
105
|
+
np.full((n_bins, 1), FILLVAL_FLOAT64, dtype="float64"),
|
|
106
|
+
)
|
|
107
|
+
goodtimes_dataset["ena_rates_threshold"] = (
|
|
108
|
+
("energy_bin_geometric_mean", "spin_number"),
|
|
109
|
+
np.full((n_bins, 1), FILLVAL_FLOAT32, dtype="float32"),
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
return goodtimes_dataset
|
|
@@ -345,7 +345,7 @@ def load_scattering_lookup_tables(ancillary_files: dict, instrument_id: int) ->
|
|
|
345
345
|
# TODO remove the line below when the 45 sensor scattering coefficients are
|
|
346
346
|
# delivered.
|
|
347
347
|
instrument_id = 90
|
|
348
|
-
descriptor = f"l1b-{instrument_id}sensor-scattering-calibration"
|
|
348
|
+
descriptor = f"l1b-{instrument_id}sensor-scattering-calibration-data"
|
|
349
349
|
theta_grid = pd.read_csv(
|
|
350
350
|
ancillary_files[descriptor], header=None, skiprows=7, nrows=241
|
|
351
351
|
).to_numpy(dtype=float)
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
import xarray as xr
|
|
4
4
|
|
|
5
5
|
from imap_processing.ultra.l1b.badtimes import calculate_badtimes
|
|
6
|
-
from imap_processing.ultra.l1b.cullingmask import calculate_cullingmask
|
|
7
6
|
from imap_processing.ultra.l1b.de import calculate_de
|
|
8
7
|
from imap_processing.ultra.l1b.extendedspin import calculate_extendedspin
|
|
8
|
+
from imap_processing.ultra.l1b.goodtimes import calculate_goodtimes
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def ultra_l1b(data_dict: dict, ancillary_files: dict) -> list[xr.Dataset]:
|
|
@@ -29,7 +29,7 @@ def ultra_l1b(data_dict: dict, ancillary_files: dict) -> list[xr.Dataset]:
|
|
|
29
29
|
General flow:
|
|
30
30
|
1. l1a data products are created (upstream to this code)
|
|
31
31
|
2. l1b de is created here and dropped in s3 kicking off processing again
|
|
32
|
-
3. l1b extended,
|
|
32
|
+
3. l1b extended, goodtimes, badtimes created here
|
|
33
33
|
"""
|
|
34
34
|
output_datasets = []
|
|
35
35
|
|
|
@@ -72,22 +72,22 @@ def ultra_l1b(data_dict: dict, ancillary_files: dict) -> list[xr.Dataset]:
|
|
|
72
72
|
output_datasets.append(extendedspin_dataset)
|
|
73
73
|
elif (
|
|
74
74
|
f"imap_ultra_l1b_{instrument_id}sensor-extendedspin" in data_dict
|
|
75
|
-
and f"imap_ultra_l1b_{instrument_id}sensor-
|
|
75
|
+
and f"imap_ultra_l1b_{instrument_id}sensor-goodtimes" in data_dict
|
|
76
76
|
):
|
|
77
77
|
badtimes_dataset = calculate_badtimes(
|
|
78
78
|
data_dict[f"imap_ultra_l1b_{instrument_id}sensor-extendedspin"],
|
|
79
|
-
data_dict[f"imap_ultra_l1b_{instrument_id}sensor-
|
|
79
|
+
data_dict[f"imap_ultra_l1b_{instrument_id}sensor-goodtimes"][
|
|
80
80
|
"spin_number"
|
|
81
81
|
].values,
|
|
82
82
|
f"imap_ultra_l1b_{instrument_id}sensor-badtimes",
|
|
83
83
|
)
|
|
84
84
|
output_datasets.append(badtimes_dataset)
|
|
85
85
|
elif f"imap_ultra_l1b_{instrument_id}sensor-extendedspin" in data_dict:
|
|
86
|
-
|
|
86
|
+
goodtimes_dataset = calculate_goodtimes(
|
|
87
87
|
data_dict[f"imap_ultra_l1b_{instrument_id}sensor-extendedspin"],
|
|
88
|
-
f"imap_ultra_l1b_{instrument_id}sensor-
|
|
88
|
+
f"imap_ultra_l1b_{instrument_id}sensor-goodtimes",
|
|
89
89
|
)
|
|
90
|
-
output_datasets.append(
|
|
90
|
+
output_datasets.append(goodtimes_dataset)
|
|
91
91
|
if not output_datasets:
|
|
92
92
|
raise ValueError("Data dictionary does not contain the expected keys.")
|
|
93
93
|
|
|
@@ -31,6 +31,7 @@ SPIN_DURATION = 15 # Default spin duration in seconds.
|
|
|
31
31
|
RateResult = namedtuple(
|
|
32
32
|
"RateResult",
|
|
33
33
|
[
|
|
34
|
+
"unique_spins",
|
|
34
35
|
"start_per_spin",
|
|
35
36
|
"stop_per_spin",
|
|
36
37
|
"coin_per_spin",
|
|
@@ -249,8 +250,8 @@ def flag_rates(
|
|
|
249
250
|
Quality flags.
|
|
250
251
|
spin : NDArray
|
|
251
252
|
Spin data.
|
|
252
|
-
|
|
253
|
-
Energy
|
|
253
|
+
energy_bin_geometric_mean : NDArray
|
|
254
|
+
Energy bin geometric mean.
|
|
254
255
|
n_sigma_per_energy_reshape : NDArray
|
|
255
256
|
N sigma per energy.
|
|
256
257
|
"""
|
|
@@ -264,7 +265,7 @@ def flag_rates(
|
|
|
264
265
|
threshold = get_n_sigma(count_rates, duration, sigma=sigma)
|
|
265
266
|
|
|
266
267
|
bin_edges = np.array(UltraConstants.CULLING_ENERGY_BIN_EDGES)
|
|
267
|
-
|
|
268
|
+
energy_bin_geometric_mean = np.sqrt(bin_edges[:-1] * bin_edges[1:])
|
|
268
269
|
spin = np.unique(spin_number)
|
|
269
270
|
|
|
270
271
|
# Indices where the counts exceed the threshold
|
|
@@ -275,7 +276,7 @@ def flag_rates(
|
|
|
275
276
|
quality_flags[:, 0] |= ImapRatesUltraFlags.FIRSTSPIN.value
|
|
276
277
|
quality_flags[:, -1] |= ImapRatesUltraFlags.LASTSPIN.value
|
|
277
278
|
|
|
278
|
-
return quality_flags, spin,
|
|
279
|
+
return quality_flags, spin, energy_bin_geometric_mean, threshold
|
|
279
280
|
|
|
280
281
|
|
|
281
282
|
def compare_aux_univ_spin_table(
|
|
@@ -424,6 +425,8 @@ def get_pulses_per_spin(rates: xr.Dataset) -> RateResult:
|
|
|
424
425
|
|
|
425
426
|
Returns
|
|
426
427
|
-------
|
|
428
|
+
unique_spins : NDArray
|
|
429
|
+
Unique spin numbers.
|
|
427
430
|
start_per_spin : NDArray
|
|
428
431
|
Total start pulses per spin.
|
|
429
432
|
stop_per_spin : NDArray
|
|
@@ -474,6 +477,7 @@ def get_pulses_per_spin(rates: xr.Dataset) -> RateResult:
|
|
|
474
477
|
coin_per_spin = np.bincount(spin_idx, weights=coin_pulses)
|
|
475
478
|
|
|
476
479
|
return RateResult(
|
|
480
|
+
unique_spins=unique_spins,
|
|
477
481
|
start_per_spin=start_per_spin,
|
|
478
482
|
stop_per_spin=stop_per_spin,
|
|
479
483
|
coin_per_spin=coin_per_spin,
|
|
@@ -12,7 +12,9 @@ from numpy import ndarray
|
|
|
12
12
|
from numpy.typing import NDArray
|
|
13
13
|
from scipy.interpolate import LinearNDInterpolator, RegularGridInterpolator
|
|
14
14
|
|
|
15
|
+
from imap_processing.quality_flags import ImapDEOutliersUltraFlags
|
|
15
16
|
from imap_processing.spice.spin import get_spin_data
|
|
17
|
+
from imap_processing.spice.time import sct_to_et
|
|
16
18
|
from imap_processing.ultra.constants import UltraConstants
|
|
17
19
|
from imap_processing.ultra.l1b.lookup_utils import (
|
|
18
20
|
get_angular_profiles,
|
|
@@ -552,7 +554,7 @@ def get_de_velocity(
|
|
|
552
554
|
v_hat = velocities / np.linalg.norm(velocities, axis=1)[:, None]
|
|
553
555
|
r_hat = -v_hat
|
|
554
556
|
|
|
555
|
-
return velocities, v_hat, r_hat
|
|
557
|
+
return velocities, -v_hat, -r_hat
|
|
556
558
|
|
|
557
559
|
|
|
558
560
|
def get_ssd_tof(
|
|
@@ -837,25 +839,16 @@ def get_ctof(
|
|
|
837
839
|
return ctof, magnitude_v
|
|
838
840
|
|
|
839
841
|
|
|
840
|
-
def determine_species(
|
|
842
|
+
def determine_species(e_bin: np.ndarray, type: str) -> NDArray:
|
|
841
843
|
"""
|
|
842
844
|
Determine the species for pulse-height events.
|
|
843
845
|
|
|
844
|
-
Species is determined
|
|
845
|
-
For velocity, the particle TOF is normalized with respect
|
|
846
|
-
to a fixed distance dmin between the front and back detectors.
|
|
847
|
-
The normalized TOF is termed the corrected TOF (ctof).
|
|
848
|
-
Particle species are determined from ctof using thresholds.
|
|
849
|
-
|
|
850
|
-
Further description is available on pages 42-44 of
|
|
851
|
-
IMAP-Ultra Flight Software Specification document.
|
|
846
|
+
Species is determined using the computed e_bin.
|
|
852
847
|
|
|
853
848
|
Parameters
|
|
854
849
|
----------
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
path_length : np.ndarray
|
|
858
|
-
Path length (r) (hundredths of a millimeter).
|
|
850
|
+
e_bin : np.ndarray
|
|
851
|
+
Computed e_bin.
|
|
859
852
|
type : str
|
|
860
853
|
Type of data (PH or SSD).
|
|
861
854
|
|
|
@@ -864,11 +857,17 @@ def determine_species(tof: np.ndarray, path_length: np.ndarray, type: str) -> ND
|
|
|
864
857
|
species_bin : np.array
|
|
865
858
|
Species bin.
|
|
866
859
|
"""
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
860
|
+
if type == "PH":
|
|
861
|
+
species_groups = UltraConstants.TOFXPH_SPECIES_GROUPS
|
|
862
|
+
if type == "SSD":
|
|
863
|
+
species_groups = UltraConstants.TOFXE_SPECIES_GROUPS
|
|
864
|
+
|
|
865
|
+
non_proton_bins = species_groups["non_proton"]
|
|
866
|
+
proton_bins = species_groups["proton"]
|
|
867
|
+
|
|
868
|
+
species_bin = np.full(e_bin.shape, fill_value=2, dtype=int)
|
|
869
|
+
species_bin[np.isin(e_bin, non_proton_bins)] = 0
|
|
870
|
+
species_bin[np.isin(e_bin, proton_bins)] = 1
|
|
872
871
|
|
|
873
872
|
return species_bin
|
|
874
873
|
|
|
@@ -984,9 +983,9 @@ def get_eventtimes(
|
|
|
984
983
|
Returns
|
|
985
984
|
-------
|
|
986
985
|
event_times : np.ndarray
|
|
987
|
-
Event times.
|
|
986
|
+
Event times in et.
|
|
988
987
|
spin_starts : np.ndarray
|
|
989
|
-
Spin start times.
|
|
988
|
+
Spin start times in et.
|
|
990
989
|
spin_period_sec : np.ndarray
|
|
991
990
|
Spin period in seconds.
|
|
992
991
|
|
|
@@ -1007,7 +1006,7 @@ def get_eventtimes(
|
|
|
1007
1006
|
|
|
1008
1007
|
event_times = spin_starts + spin_period_sec * (phase_angle / 720)
|
|
1009
1008
|
|
|
1010
|
-
return event_times, spin_starts, spin_period_sec
|
|
1009
|
+
return sct_to_et(event_times), sct_to_et(spin_starts), spin_period_sec
|
|
1011
1010
|
|
|
1012
1011
|
|
|
1013
1012
|
def interpolate_fwhm(
|
|
@@ -1375,54 +1374,91 @@ def is_coin_ph_valid(
|
|
|
1375
1374
|
etof: NDArray,
|
|
1376
1375
|
xc: NDArray,
|
|
1377
1376
|
xb: NDArray,
|
|
1377
|
+
stop_north_tdc: NDArray,
|
|
1378
|
+
stop_south_tdc: NDArray,
|
|
1379
|
+
stop_east_tdc: NDArray,
|
|
1380
|
+
stop_west_tdc: NDArray,
|
|
1378
1381
|
sensor: str,
|
|
1379
1382
|
ancillary_files: dict,
|
|
1383
|
+
quality_flags: NDArray,
|
|
1380
1384
|
) -> NDArray:
|
|
1381
1385
|
"""
|
|
1382
|
-
Determine
|
|
1383
|
-
|
|
1384
|
-
This is based on thresholds defined in the IMAP-Ultra Flight Software Specification
|
|
1385
|
-
(see page 36).
|
|
1386
|
+
Determine event validity.
|
|
1386
1387
|
|
|
1387
1388
|
Parameters
|
|
1388
1389
|
----------
|
|
1389
1390
|
etof : NDArray
|
|
1390
|
-
|
|
1391
|
+
Time for the electrons to travel back to the coincidence
|
|
1392
|
+
anode (tenths of a nanosecond).
|
|
1391
1393
|
xc : NDArray
|
|
1392
|
-
|
|
1394
|
+
X coincidence position (hundredths of a millimeter).
|
|
1393
1395
|
xb : NDArray
|
|
1394
|
-
Back
|
|
1396
|
+
Back positions in x direction (hundredths of a millimeter).
|
|
1397
|
+
stop_north_tdc : NDArray
|
|
1398
|
+
Stop North Time to Digital Converter.
|
|
1399
|
+
stop_south_tdc : NDArray
|
|
1400
|
+
Stop South Time to Digital Converter.
|
|
1401
|
+
stop_east_tdc : NDArray
|
|
1402
|
+
Stop East Time to Digital Converter.
|
|
1403
|
+
stop_west_tdc : NDArray
|
|
1404
|
+
Stop West Time to Digital Converter.
|
|
1395
1405
|
sensor : str
|
|
1396
1406
|
Sensor name: "ultra45" or "ultra90".
|
|
1397
1407
|
ancillary_files : dict
|
|
1398
1408
|
Ancillary files for lookup.
|
|
1409
|
+
quality_flags : NDArray
|
|
1410
|
+
Quality flag to set when there is an outlier.
|
|
1399
1411
|
|
|
1400
1412
|
Returns
|
|
1401
1413
|
-------
|
|
1402
|
-
|
|
1403
|
-
Boolean array indicating
|
|
1414
|
+
combined_mask : NDArray
|
|
1415
|
+
Boolean array indicating whether back TOF is valid.
|
|
1404
1416
|
|
|
1405
1417
|
Notes
|
|
1406
1418
|
-----
|
|
1407
|
-
|
|
1419
|
+
From page 36 of the IMAP-Ultra Flight Software Specification document.
|
|
1408
1420
|
"""
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1421
|
+
# Make certain etof is within range for tenths of a nanosecond.
|
|
1422
|
+
etof_valid = (etof >= UltraConstants.ETOFMIN_EVENTFILTER) & (
|
|
1423
|
+
etof <= UltraConstants.ETOFMAX_EVENTFILTER
|
|
1424
|
+
)
|
|
1413
1425
|
|
|
1426
|
+
# Hundredths of a mm.
|
|
1414
1427
|
diff_x = xc - xb
|
|
1415
|
-
etof_offset1 = get_image_params("eTOFOff1", sensor, ancillary_files)
|
|
1416
|
-
etof_offset2 = get_image_params("eTOFOff2", sensor, ancillary_files)
|
|
1417
|
-
etof_slope1 = get_image_params("eTOFSlope1", sensor, ancillary_files)
|
|
1418
|
-
etof_slope2 = get_image_params("eTOFSlope2", sensor, ancillary_files)
|
|
1419
1428
|
|
|
1420
|
-
t1 = (
|
|
1421
|
-
|
|
1429
|
+
t1 = (
|
|
1430
|
+
(etof - UltraConstants.ETOFOFF1_EVENTFILTER)
|
|
1431
|
+
* UltraConstants.ETOFSLOPE1_EVENTFILTER
|
|
1432
|
+
/ 1024
|
|
1433
|
+
)
|
|
1434
|
+
t2 = (
|
|
1435
|
+
(etof - UltraConstants.ETOFOFF2_EVENTFILTER)
|
|
1436
|
+
* UltraConstants.ETOFSLOPE2_EVENTFILTER
|
|
1437
|
+
/ 1024
|
|
1438
|
+
)
|
|
1422
1439
|
|
|
1423
1440
|
condition_1 = (diff_x >= t1) & (diff_x <= t2)
|
|
1424
1441
|
condition_2 = (diff_x >= -t2) & (diff_x <= -t1)
|
|
1425
1442
|
|
|
1426
1443
|
spatial_valid = condition_1 | condition_2
|
|
1427
1444
|
|
|
1428
|
-
|
|
1445
|
+
sp_n_norm = get_norm(stop_north_tdc, "SpN", sensor, ancillary_files)
|
|
1446
|
+
sp_s_norm = get_norm(stop_south_tdc, "SpS", sensor, ancillary_files)
|
|
1447
|
+
sp_e_norm = get_norm(stop_east_tdc, "SpE", sensor, ancillary_files)
|
|
1448
|
+
sp_w_norm = get_norm(stop_west_tdc, "SpW", sensor, ancillary_files)
|
|
1449
|
+
|
|
1450
|
+
tofx = sp_n_norm + sp_s_norm
|
|
1451
|
+
tofy = sp_e_norm + sp_w_norm
|
|
1452
|
+
|
|
1453
|
+
# Units in tenths of a nanosecond
|
|
1454
|
+
delta_tof = tofy - tofx
|
|
1455
|
+
|
|
1456
|
+
delta_tof_mask = (delta_tof >= UltraConstants.TOFDIFFTPMIN_EVENTFILTER) & (
|
|
1457
|
+
delta_tof <= UltraConstants.TOFDIFFTPMAX_EVENTFILTER
|
|
1458
|
+
)
|
|
1459
|
+
|
|
1460
|
+
combined_mask = etof_valid & spatial_valid & delta_tof_mask
|
|
1461
|
+
|
|
1462
|
+
quality_flags[~combined_mask] |= ImapDEOutliersUltraFlags.COINPH.value
|
|
1463
|
+
|
|
1464
|
+
return combined_mask
|