imap-processing 0.18.0__py3-none-any.whl → 0.19.2__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/ancillary/ancillary_dataset_combiner.py +161 -1
- imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +6 -0
- imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +221 -1057
- imap_processing/cdf/config/imap_codice_l1b_variable_attrs.yaml +307 -283
- imap_processing/cdf/config/imap_codice_l2_variable_attrs.yaml +1044 -203
- imap_processing/cdf/config/imap_constant_attrs.yaml +4 -2
- imap_processing/cdf/config/imap_enamaps_l2-common_variable_attrs.yaml +11 -0
- imap_processing/cdf/config/imap_glows_l1b_variable_attrs.yaml +15 -1
- imap_processing/cdf/config/imap_hi_global_cdf_attrs.yaml +5 -0
- imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +10 -4
- imap_processing/cdf/config/imap_idex_l2a_variable_attrs.yaml +33 -4
- imap_processing/cdf/config/imap_idex_l2b_variable_attrs.yaml +8 -91
- imap_processing/cdf/config/imap_idex_l2c_variable_attrs.yaml +106 -16
- imap_processing/cdf/config/imap_lo_global_cdf_attrs.yaml +5 -4
- imap_processing/cdf/config/imap_lo_l1a_variable_attrs.yaml +4 -15
- imap_processing/cdf/config/imap_lo_l1c_variable_attrs.yaml +189 -98
- imap_processing/cdf/config/imap_mag_global_cdf_attrs.yaml +85 -2
- imap_processing/cdf/config/imap_mag_l1c_variable_attrs.yaml +24 -1
- imap_processing/cdf/config/imap_ultra_global_cdf_attrs.yaml +20 -8
- imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +45 -35
- imap_processing/cdf/config/imap_ultra_l1c_variable_attrs.yaml +110 -7
- imap_processing/cli.py +138 -93
- imap_processing/codice/codice_l0.py +2 -1
- imap_processing/codice/codice_l1a.py +167 -69
- imap_processing/codice/codice_l1b.py +42 -32
- imap_processing/codice/codice_l2.py +215 -9
- imap_processing/codice/constants.py +790 -603
- imap_processing/codice/data/lo_stepping_values.csv +1 -1
- imap_processing/decom.py +1 -4
- imap_processing/ena_maps/ena_maps.py +71 -43
- imap_processing/ena_maps/utils/corrections.py +291 -0
- imap_processing/ena_maps/utils/map_utils.py +20 -4
- imap_processing/ena_maps/utils/naming.py +8 -2
- imap_processing/glows/ancillary/imap_glows_exclusions-by-instr-team_20250923_v002.dat +10 -0
- imap_processing/glows/ancillary/imap_glows_map-of-excluded-regions_20250923_v002.dat +393 -0
- imap_processing/glows/ancillary/imap_glows_map-of-uv-sources_20250923_v002.dat +593 -0
- imap_processing/glows/ancillary/imap_glows_pipeline-settings_20250923_v002.json +54 -0
- imap_processing/glows/ancillary/imap_glows_suspected-transients_20250923_v002.dat +10 -0
- imap_processing/glows/l1b/glows_l1b.py +123 -18
- imap_processing/glows/l1b/glows_l1b_data.py +358 -47
- imap_processing/glows/l2/glows_l2.py +11 -0
- imap_processing/hi/hi_l1a.py +124 -3
- imap_processing/hi/hi_l1b.py +154 -71
- imap_processing/hi/hi_l1c.py +4 -109
- imap_processing/hi/hi_l2.py +104 -60
- imap_processing/hi/utils.py +262 -8
- imap_processing/hit/l0/constants.py +3 -0
- imap_processing/hit/l0/decom_hit.py +3 -6
- imap_processing/hit/l1a/hit_l1a.py +311 -21
- imap_processing/hit/l1b/hit_l1b.py +54 -126
- imap_processing/hit/l2/hit_l2.py +6 -6
- imap_processing/ialirt/calculate_ingest.py +219 -0
- imap_processing/ialirt/constants.py +12 -2
- imap_processing/ialirt/generate_coverage.py +15 -2
- imap_processing/ialirt/l0/ialirt_spice.py +6 -2
- imap_processing/ialirt/l0/parse_mag.py +293 -42
- imap_processing/ialirt/l0/process_hit.py +5 -3
- imap_processing/ialirt/l0/process_swapi.py +41 -25
- imap_processing/ialirt/process_ephemeris.py +70 -14
- imap_processing/ialirt/utils/create_xarray.py +1 -1
- imap_processing/idex/idex_l0.py +2 -2
- imap_processing/idex/idex_l1a.py +2 -3
- imap_processing/idex/idex_l1b.py +2 -3
- imap_processing/idex/idex_l2a.py +130 -4
- imap_processing/idex/idex_l2b.py +158 -143
- imap_processing/idex/idex_utils.py +1 -3
- 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/l0/lo_science.py +25 -24
- imap_processing/lo/l1b/lo_l1b.py +93 -19
- imap_processing/lo/l1c/lo_l1c.py +273 -93
- imap_processing/lo/l2/lo_l2.py +949 -135
- imap_processing/lo/lo_ancillary.py +55 -0
- imap_processing/mag/l1a/mag_l1a.py +1 -0
- imap_processing/mag/l1a/mag_l1a_data.py +26 -0
- imap_processing/mag/l1b/mag_l1b.py +3 -2
- imap_processing/mag/l1c/interpolation_methods.py +14 -15
- imap_processing/mag/l1c/mag_l1c.py +23 -6
- imap_processing/mag/l1d/mag_l1d.py +57 -14
- imap_processing/mag/l1d/mag_l1d_data.py +202 -32
- imap_processing/mag/l2/mag_l2.py +2 -0
- imap_processing/mag/l2/mag_l2_data.py +14 -5
- imap_processing/quality_flags.py +23 -1
- imap_processing/spice/geometry.py +89 -39
- imap_processing/spice/pointing_frame.py +4 -8
- imap_processing/spice/repoint.py +78 -2
- imap_processing/spice/spin.py +28 -8
- imap_processing/spice/time.py +12 -22
- imap_processing/swapi/l1/swapi_l1.py +10 -4
- imap_processing/swapi/l2/swapi_l2.py +15 -17
- imap_processing/swe/l1b/swe_l1b.py +1 -2
- imap_processing/ultra/constants.py +30 -24
- imap_processing/ultra/l0/ultra_utils.py +9 -11
- imap_processing/ultra/l1a/ultra_l1a.py +1 -2
- imap_processing/ultra/l1b/badtimes.py +35 -11
- imap_processing/ultra/l1b/de.py +95 -31
- imap_processing/ultra/l1b/extendedspin.py +31 -16
- imap_processing/ultra/l1b/goodtimes.py +112 -0
- imap_processing/ultra/l1b/lookup_utils.py +281 -28
- imap_processing/ultra/l1b/quality_flag_filters.py +10 -1
- imap_processing/ultra/l1b/ultra_l1b.py +7 -7
- imap_processing/ultra/l1b/ultra_l1b_culling.py +169 -7
- imap_processing/ultra/l1b/ultra_l1b_extended.py +311 -69
- imap_processing/ultra/l1c/helio_pset.py +139 -37
- imap_processing/ultra/l1c/l1c_lookup_utils.py +289 -0
- imap_processing/ultra/l1c/spacecraft_pset.py +140 -29
- imap_processing/ultra/l1c/ultra_l1c.py +33 -24
- imap_processing/ultra/l1c/ultra_l1c_culling.py +92 -0
- imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +400 -292
- imap_processing/ultra/l2/ultra_l2.py +54 -11
- imap_processing/ultra/utils/ultra_l1_utils.py +37 -7
- imap_processing/utils.py +3 -4
- {imap_processing-0.18.0.dist-info → imap_processing-0.19.2.dist-info}/METADATA +2 -2
- {imap_processing-0.18.0.dist-info → imap_processing-0.19.2.dist-info}/RECORD +118 -109
- imap_processing/idex/idex_l2c.py +0 -84
- imap_processing/spice/kernels.py +0 -187
- imap_processing/ultra/l1b/cullingmask.py +0 -87
- imap_processing/ultra/l1c/histogram.py +0 -36
- {imap_processing-0.18.0.dist-info → imap_processing-0.19.2.dist-info}/LICENSE +0 -0
- {imap_processing-0.18.0.dist-info → imap_processing-0.19.2.dist-info}/WHEEL +0 -0
- {imap_processing-0.18.0.dist-info → imap_processing-0.19.2.dist-info}/entry_points.txt +0 -0
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# TODO: Come back and add in FSW logic.
|
|
4
4
|
import logging
|
|
5
|
+
from collections import namedtuple
|
|
5
6
|
from enum import Enum
|
|
6
7
|
|
|
7
8
|
import numpy as np
|
|
@@ -11,11 +12,14 @@ from numpy import ndarray
|
|
|
11
12
|
from numpy.typing import NDArray
|
|
12
13
|
from scipy.interpolate import LinearNDInterpolator, RegularGridInterpolator
|
|
13
14
|
|
|
15
|
+
from imap_processing.quality_flags import ImapDEOutliersUltraFlags
|
|
14
16
|
from imap_processing.spice.spin import get_spin_data
|
|
17
|
+
from imap_processing.spice.time import sct_to_et
|
|
15
18
|
from imap_processing.ultra.constants import UltraConstants
|
|
16
19
|
from imap_processing.ultra.l1b.lookup_utils import (
|
|
17
20
|
get_angular_profiles,
|
|
18
21
|
get_back_position,
|
|
22
|
+
get_ebins,
|
|
19
23
|
get_energy_efficiencies,
|
|
20
24
|
get_energy_norm,
|
|
21
25
|
get_image_params,
|
|
@@ -26,6 +30,10 @@ from imap_processing.ultra.l1b.lookup_utils import (
|
|
|
26
30
|
|
|
27
31
|
logger = logging.getLogger(__name__)
|
|
28
32
|
|
|
33
|
+
FILLVAL_UINT8 = 255
|
|
34
|
+
FILLVAL_FLOAT32 = -1.0e31
|
|
35
|
+
FILLVAL_FLOAT64 = -1.0e31
|
|
36
|
+
|
|
29
37
|
|
|
30
38
|
class StartType(Enum):
|
|
31
39
|
"""Start Type: 1=Left, 2=Right."""
|
|
@@ -50,6 +58,9 @@ class CoinType(Enum):
|
|
|
50
58
|
Bottom = 2
|
|
51
59
|
|
|
52
60
|
|
|
61
|
+
PHTOFResult = namedtuple("PHTOFResult", ["tof", "t2", "xb", "yb", "tofx", "tofy"])
|
|
62
|
+
|
|
63
|
+
|
|
53
64
|
def get_front_x_position(
|
|
54
65
|
start_type: ndarray, start_position_tdc: ndarray, sensor: str, ancillary_files: dict
|
|
55
66
|
) -> ndarray:
|
|
@@ -161,7 +172,7 @@ def get_front_y_position(
|
|
|
161
172
|
|
|
162
173
|
def get_ph_tof_and_back_positions(
|
|
163
174
|
de_dataset: xarray.Dataset, xf: np.ndarray, sensor: str, ancillary_files: dict
|
|
164
|
-
) ->
|
|
175
|
+
) -> PHTOFResult:
|
|
165
176
|
"""
|
|
166
177
|
Calculate back xb, yb position and tof.
|
|
167
178
|
|
|
@@ -196,6 +207,10 @@ def get_ph_tof_and_back_positions(
|
|
|
196
207
|
Back positions in x direction (hundredths of a millimeter).
|
|
197
208
|
yb : np.array
|
|
198
209
|
Back positions in y direction (hundredths of a millimeter).
|
|
210
|
+
tofx : np.array
|
|
211
|
+
X front position tof offset (tenths of a nanosecond).
|
|
212
|
+
tofy : np.array
|
|
213
|
+
Y front position tof offset (tenths of a nanosecond).
|
|
199
214
|
"""
|
|
200
215
|
indices = np.nonzero(
|
|
201
216
|
np.isin(de_dataset["stop_type"], [StopType.Top.value, StopType.Bottom.value])
|
|
@@ -278,7 +293,7 @@ def get_ph_tof_and_back_positions(
|
|
|
278
293
|
stop_type_bottom
|
|
279
294
|
] / 10 * get_image_params("XFTTOF", sensor, ancillary_files)
|
|
280
295
|
|
|
281
|
-
return tof, t2, xb, yb
|
|
296
|
+
return PHTOFResult(tof=tof, t2=t2, xb=xb, yb=yb, tofx=tofx, tofy=tofy)
|
|
282
297
|
|
|
283
298
|
|
|
284
299
|
def get_path_length(
|
|
@@ -530,16 +545,16 @@ def get_de_velocity(
|
|
|
530
545
|
v_y = delta_v[:, 1] / tof * 1e3
|
|
531
546
|
v_z = delta_v[:, 2] / tof * 1e3
|
|
532
547
|
|
|
533
|
-
v_x[tof < 0] =
|
|
534
|
-
v_y[tof < 0] =
|
|
535
|
-
v_z[tof < 0] =
|
|
548
|
+
v_x[tof < 0] = FILLVAL_FLOAT32 # used as fillvals
|
|
549
|
+
v_y[tof < 0] = FILLVAL_FLOAT32
|
|
550
|
+
v_z[tof < 0] = FILLVAL_FLOAT32
|
|
536
551
|
|
|
537
552
|
velocities = np.vstack((v_x, v_y, v_z)).T
|
|
538
553
|
|
|
539
554
|
v_hat = velocities / np.linalg.norm(velocities, axis=1)[:, None]
|
|
540
555
|
r_hat = -v_hat
|
|
541
556
|
|
|
542
|
-
return velocities, v_hat, r_hat
|
|
557
|
+
return velocities, -v_hat, -r_hat
|
|
543
558
|
|
|
544
559
|
|
|
545
560
|
def get_ssd_tof(
|
|
@@ -621,13 +636,17 @@ def get_de_energy_kev(v: np.ndarray, species: np.ndarray) -> NDArray:
|
|
|
621
636
|
# Compute the sum of squares.
|
|
622
637
|
v2 = np.sum(vv**2, axis=1)
|
|
623
638
|
|
|
624
|
-
|
|
625
|
-
|
|
639
|
+
# Only compute where species == 1 and v is valid
|
|
640
|
+
index_hydrogen = species == 1
|
|
641
|
+
valid_velocity = np.isfinite(v2)
|
|
642
|
+
valid_mask = index_hydrogen & valid_velocity
|
|
643
|
+
|
|
644
|
+
energy = np.full_like(v2, FILLVAL_FLOAT32)
|
|
626
645
|
|
|
627
646
|
# TODO: we will calculate the energies of the different species here.
|
|
628
647
|
# 1/2 mv^2 in Joules, convert to keV
|
|
629
|
-
energy[
|
|
630
|
-
0.5 * UltraConstants.MASS_H * v2[
|
|
648
|
+
energy[valid_mask] = (
|
|
649
|
+
0.5 * UltraConstants.MASS_H * v2[valid_mask] * UltraConstants.J_KEV
|
|
631
650
|
)
|
|
632
651
|
|
|
633
652
|
return energy
|
|
@@ -820,25 +839,16 @@ def get_ctof(
|
|
|
820
839
|
return ctof, magnitude_v
|
|
821
840
|
|
|
822
841
|
|
|
823
|
-
def determine_species(
|
|
842
|
+
def determine_species(e_bin: np.ndarray, type: str) -> NDArray:
|
|
824
843
|
"""
|
|
825
844
|
Determine the species for pulse-height events.
|
|
826
845
|
|
|
827
|
-
Species is determined
|
|
828
|
-
For velocity, the particle TOF is normalized with respect
|
|
829
|
-
to a fixed distance dmin between the front and back detectors.
|
|
830
|
-
The normalized TOF is termed the corrected TOF (ctof).
|
|
831
|
-
Particle species are determined from ctof using thresholds.
|
|
832
|
-
|
|
833
|
-
Further description is available on pages 42-44 of
|
|
834
|
-
IMAP-Ultra Flight Software Specification document.
|
|
846
|
+
Species is determined using the computed e_bin.
|
|
835
847
|
|
|
836
848
|
Parameters
|
|
837
849
|
----------
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
path_length : np.ndarray
|
|
841
|
-
Path length (r) (hundredths of a millimeter).
|
|
850
|
+
e_bin : np.ndarray
|
|
851
|
+
Computed e_bin.
|
|
842
852
|
type : str
|
|
843
853
|
Type of data (PH or SSD).
|
|
844
854
|
|
|
@@ -847,11 +857,17 @@ def determine_species(tof: np.ndarray, path_length: np.ndarray, type: str) -> ND
|
|
|
847
857
|
species_bin : np.array
|
|
848
858
|
Species bin.
|
|
849
859
|
"""
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
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
|
|
855
871
|
|
|
856
872
|
return species_bin
|
|
857
873
|
|
|
@@ -933,7 +949,7 @@ def get_spin_number(de_met: NDArray, de_spin: NDArray) -> NDArray:
|
|
|
933
949
|
possible_spins = spin_numbers & 0xFF
|
|
934
950
|
|
|
935
951
|
# Assign each group based on time.
|
|
936
|
-
for start, end in zip(spin_start_indices, spin_end_indices):
|
|
952
|
+
for start, end in zip(spin_start_indices, spin_end_indices, strict=False):
|
|
937
953
|
# Now that we have the possible spins from the Universal Spin Table,
|
|
938
954
|
# we match the times of those spins to the nearest times in the DE data.
|
|
939
955
|
possible_times = spin_start_mets[possible_spins == de_spin_sorted[start]]
|
|
@@ -967,9 +983,9 @@ def get_eventtimes(
|
|
|
967
983
|
Returns
|
|
968
984
|
-------
|
|
969
985
|
event_times : np.ndarray
|
|
970
|
-
Event times.
|
|
986
|
+
Event times in et.
|
|
971
987
|
spin_starts : np.ndarray
|
|
972
|
-
Spin start times.
|
|
988
|
+
Spin start times in et.
|
|
973
989
|
spin_period_sec : np.ndarray
|
|
974
990
|
Spin period in seconds.
|
|
975
991
|
|
|
@@ -990,7 +1006,7 @@ def get_eventtimes(
|
|
|
990
1006
|
|
|
991
1007
|
event_times = spin_starts + spin_period_sec * (phase_angle / 720)
|
|
992
1008
|
|
|
993
|
-
return event_times, spin_starts, spin_period_sec
|
|
1009
|
+
return sct_to_et(event_times), sct_to_et(spin_starts), spin_period_sec
|
|
994
1010
|
|
|
995
1011
|
|
|
996
1012
|
def interpolate_fwhm(
|
|
@@ -1030,8 +1046,11 @@ def interpolate_fwhm(
|
|
|
1030
1046
|
)
|
|
1031
1047
|
|
|
1032
1048
|
# Note: will return nan for those out-of-bounds inputs.
|
|
1033
|
-
|
|
1034
|
-
|
|
1049
|
+
phi_vals = interp_phi((energy, phi_inst))
|
|
1050
|
+
theta_vals = interp_theta((energy, theta_inst))
|
|
1051
|
+
|
|
1052
|
+
phi_interp = np.where(np.isnan(phi_vals), FILLVAL_FLOAT32, phi_vals)
|
|
1053
|
+
theta_interp = np.where(np.isnan(theta_vals), FILLVAL_FLOAT32, theta_vals)
|
|
1035
1054
|
|
|
1036
1055
|
return phi_interp, theta_interp
|
|
1037
1056
|
|
|
@@ -1069,8 +1088,8 @@ def get_fwhm(
|
|
|
1069
1088
|
theta_interp : NDArray
|
|
1070
1089
|
Interpolated theta FWHM values.
|
|
1071
1090
|
"""
|
|
1072
|
-
phi_interp = np.full_like(phi_inst,
|
|
1073
|
-
theta_interp = np.full_like(theta_inst,
|
|
1091
|
+
phi_interp = np.full_like(phi_inst, FILLVAL_FLOAT64, dtype=np.float64)
|
|
1092
|
+
theta_interp = np.full_like(theta_inst, FILLVAL_FLOAT64, dtype=np.float64)
|
|
1074
1093
|
lt_table = get_angular_profiles("left", sensor, ancillary_files)
|
|
1075
1094
|
rt_table = get_angular_profiles("right", sensor, ancillary_files)
|
|
1076
1095
|
|
|
@@ -1089,20 +1108,12 @@ def get_fwhm(
|
|
|
1089
1108
|
return phi_interp, theta_interp
|
|
1090
1109
|
|
|
1091
1110
|
|
|
1092
|
-
def
|
|
1093
|
-
energy: NDArray, phi_inst: NDArray, theta_inst: NDArray, ancillary_files: dict
|
|
1094
|
-
) -> NDArray:
|
|
1111
|
+
def get_efficiency_interpolator(ancillary_files: dict) -> RegularGridInterpolator:
|
|
1095
1112
|
"""
|
|
1096
|
-
|
|
1113
|
+
Return a callable function that interpolates efficiency values for each event.
|
|
1097
1114
|
|
|
1098
1115
|
Parameters
|
|
1099
1116
|
----------
|
|
1100
|
-
energy : NDArray
|
|
1101
|
-
Energy values for each event.
|
|
1102
|
-
phi_inst : NDArray
|
|
1103
|
-
Instrument-frame azimuth angle for each event.
|
|
1104
|
-
theta_inst : NDArray
|
|
1105
|
-
Instrument-frame elevation angle for each event.
|
|
1106
1117
|
ancillary_files : dict
|
|
1107
1118
|
Ancillary files.
|
|
1108
1119
|
|
|
@@ -1127,14 +1138,54 @@ def get_efficiency(
|
|
|
1127
1138
|
(theta_vals, phi_vals, energy_vals),
|
|
1128
1139
|
efficiency_grid,
|
|
1129
1140
|
bounds_error=False,
|
|
1130
|
-
fill_value=
|
|
1141
|
+
fill_value=FILLVAL_FLOAT32,
|
|
1131
1142
|
)
|
|
1132
1143
|
|
|
1144
|
+
return interpolator
|
|
1145
|
+
|
|
1146
|
+
|
|
1147
|
+
def get_efficiency(
|
|
1148
|
+
energy: NDArray,
|
|
1149
|
+
phi_inst: NDArray,
|
|
1150
|
+
theta_inst: NDArray,
|
|
1151
|
+
ancillary_files: dict,
|
|
1152
|
+
interpolator: RegularGridInterpolator = None,
|
|
1153
|
+
) -> np.ndarray:
|
|
1154
|
+
"""
|
|
1155
|
+
Return interpolated efficiency values for each event.
|
|
1156
|
+
|
|
1157
|
+
Parameters
|
|
1158
|
+
----------
|
|
1159
|
+
energy : NDArray
|
|
1160
|
+
Energy values for each event.
|
|
1161
|
+
phi_inst : NDArray
|
|
1162
|
+
Instrument-frame azimuth angle for each event.
|
|
1163
|
+
theta_inst : NDArray
|
|
1164
|
+
Instrument-frame elevation angle for each event.
|
|
1165
|
+
ancillary_files : dict
|
|
1166
|
+
Ancillary files.
|
|
1167
|
+
interpolator : RegularGridInterpolator, optional
|
|
1168
|
+
Precomputed interpolator to use for efficiency lookup.
|
|
1169
|
+
If None, a new interpolator will be created from the ancillary files.
|
|
1170
|
+
|
|
1171
|
+
Returns
|
|
1172
|
+
-------
|
|
1173
|
+
efficiency : NDArray
|
|
1174
|
+
Interpolated efficiency values.
|
|
1175
|
+
"""
|
|
1176
|
+
if not interpolator:
|
|
1177
|
+
interpolator = get_efficiency_interpolator(ancillary_files)
|
|
1178
|
+
|
|
1133
1179
|
return interpolator((theta_inst, phi_inst, energy))
|
|
1134
1180
|
|
|
1135
1181
|
|
|
1136
1182
|
def determine_ebin_pulse_height(
|
|
1137
|
-
energy:
|
|
1183
|
+
energy: NDArray,
|
|
1184
|
+
tof: NDArray,
|
|
1185
|
+
path_length: NDArray,
|
|
1186
|
+
backtofvalid: NDArray,
|
|
1187
|
+
coinphvalid: NDArray,
|
|
1188
|
+
ancillary_files: dict,
|
|
1138
1189
|
) -> NDArray:
|
|
1139
1190
|
"""
|
|
1140
1191
|
Determine the species for pulse-height events.
|
|
@@ -1152,12 +1203,18 @@ def determine_ebin_pulse_height(
|
|
|
1152
1203
|
|
|
1153
1204
|
Parameters
|
|
1154
1205
|
----------
|
|
1155
|
-
energy :
|
|
1206
|
+
energy : NDArray
|
|
1156
1207
|
Energy from the PH event (keV).
|
|
1157
|
-
tof :
|
|
1208
|
+
tof : NDArray
|
|
1158
1209
|
Time of flight of the PH event (tenths of a nanosecond).
|
|
1159
|
-
path_length :
|
|
1210
|
+
path_length : NDArray
|
|
1160
1211
|
Path length (r) (hundredths of a millimeter).
|
|
1212
|
+
backtofvalid : NDArray
|
|
1213
|
+
Boolean array indicating if the back TOF is valid.
|
|
1214
|
+
coinphvalid : NDArray
|
|
1215
|
+
Boolean array indicating if the Coincidence PH is valid.
|
|
1216
|
+
ancillary_files : dict
|
|
1217
|
+
Ancillary files containing the lookup tables.
|
|
1161
1218
|
|
|
1162
1219
|
Returns
|
|
1163
1220
|
-------
|
|
@@ -1166,15 +1223,22 @@ def determine_ebin_pulse_height(
|
|
|
1166
1223
|
"""
|
|
1167
1224
|
# PH event TOF normalization to Z axis
|
|
1168
1225
|
ctof, _ = get_ctof(tof, path_length, type="PH")
|
|
1169
|
-
# TODO: need lookup tables
|
|
1170
|
-
# placeholder
|
|
1171
|
-
ebin = np.full(len(ctof), 255, dtype=np.uint8)
|
|
1172
1226
|
|
|
1173
|
-
|
|
1227
|
+
ebins = np.full(path_length.shape, FILLVAL_UINT8, dtype=np.uint8)
|
|
1228
|
+
valid = backtofvalid & coinphvalid
|
|
1229
|
+
ebins[valid] = get_ebins(
|
|
1230
|
+
"l1b-tofxph", energy[valid], ctof[valid], ebins[valid], ancillary_files
|
|
1231
|
+
)
|
|
1232
|
+
|
|
1233
|
+
return ebins
|
|
1174
1234
|
|
|
1175
1235
|
|
|
1176
1236
|
def determine_ebin_ssd(
|
|
1177
|
-
energy:
|
|
1237
|
+
energy: NDArray,
|
|
1238
|
+
tof: NDArray,
|
|
1239
|
+
path_length: NDArray,
|
|
1240
|
+
sensor: str,
|
|
1241
|
+
ancillary_files: dict,
|
|
1178
1242
|
) -> NDArray:
|
|
1179
1243
|
"""
|
|
1180
1244
|
Determine the species for SSD events.
|
|
@@ -1194,29 +1258,207 @@ def determine_ebin_ssd(
|
|
|
1194
1258
|
|
|
1195
1259
|
Parameters
|
|
1196
1260
|
----------
|
|
1197
|
-
energy :
|
|
1261
|
+
energy : NDArray
|
|
1198
1262
|
Energy from the SSD event (keV).
|
|
1199
|
-
tof :
|
|
1263
|
+
tof : NDArray
|
|
1200
1264
|
Time of flight of the SSD event (tenths of a nanosecond).
|
|
1201
|
-
path_length :
|
|
1265
|
+
path_length : NDArray
|
|
1202
1266
|
Path length (r) (hundredths of a millimeter).
|
|
1267
|
+
sensor : str
|
|
1268
|
+
Sensor name: "ultra45" or "ultra90".
|
|
1269
|
+
ancillary_files : dict
|
|
1270
|
+
Ancillary files containing the lookup tables.
|
|
1203
1271
|
|
|
1204
1272
|
Returns
|
|
1205
1273
|
-------
|
|
1206
|
-
bin :
|
|
1274
|
+
bin : NDArray
|
|
1207
1275
|
Species bin.
|
|
1208
1276
|
"""
|
|
1209
1277
|
# SSD event TOF normalization to Z axis
|
|
1210
1278
|
ctof, _ = get_ctof(tof, path_length, type="SSD")
|
|
1211
1279
|
|
|
1212
|
-
|
|
1280
|
+
ebins = np.full(path_length.shape, FILLVAL_UINT8, dtype=np.uint8)
|
|
1281
|
+
steep_path_length = get_image_params("PathSteepThresh", sensor, ancillary_files)
|
|
1282
|
+
medium_path_length = get_image_params("PathMediumThresh", sensor, ancillary_files)
|
|
1283
|
+
|
|
1284
|
+
steep_mask = path_length < steep_path_length
|
|
1285
|
+
medium_mask = (path_length >= steep_path_length) & (
|
|
1286
|
+
path_length < medium_path_length
|
|
1287
|
+
)
|
|
1288
|
+
flat_mask = path_length >= medium_path_length
|
|
1289
|
+
|
|
1290
|
+
ebins[steep_mask] = get_ebins(
|
|
1291
|
+
f"l1b-{sensor[5::]}sensor-tofxesteep",
|
|
1292
|
+
energy[steep_mask],
|
|
1293
|
+
ctof[steep_mask],
|
|
1294
|
+
ebins[steep_mask],
|
|
1295
|
+
ancillary_files,
|
|
1296
|
+
)
|
|
1297
|
+
ebins[medium_mask] = get_ebins(
|
|
1298
|
+
f"l1b-{sensor[5::]}sensor-tofxemedium",
|
|
1299
|
+
energy[medium_mask],
|
|
1300
|
+
ctof[medium_mask],
|
|
1301
|
+
ebins[medium_mask],
|
|
1302
|
+
ancillary_files,
|
|
1303
|
+
)
|
|
1304
|
+
ebins[flat_mask] = get_ebins(
|
|
1305
|
+
f"l1b-{sensor[5::]}sensor-tofxeflat",
|
|
1306
|
+
energy[flat_mask],
|
|
1307
|
+
ctof[flat_mask],
|
|
1308
|
+
ebins[flat_mask],
|
|
1309
|
+
ancillary_files,
|
|
1310
|
+
)
|
|
1311
|
+
|
|
1312
|
+
return ebins
|
|
1313
|
+
|
|
1314
|
+
|
|
1315
|
+
def is_back_tof_valid(
|
|
1316
|
+
de_dataset: xarray.Dataset,
|
|
1317
|
+
xf: NDArray,
|
|
1318
|
+
sensor: str,
|
|
1319
|
+
ancillary_files: dict,
|
|
1320
|
+
) -> NDArray:
|
|
1321
|
+
"""
|
|
1322
|
+
Determine whether back TOF is valid based on stop type.
|
|
1323
|
+
|
|
1324
|
+
Parameters
|
|
1325
|
+
----------
|
|
1326
|
+
de_dataset : xarray.Dataset
|
|
1327
|
+
Data in xarray format.
|
|
1328
|
+
xf : NDArray
|
|
1329
|
+
X front position in (hundredths of a millimeter).
|
|
1330
|
+
Has same length as de_dataset.
|
|
1331
|
+
sensor : str
|
|
1332
|
+
Sensor name: "ultra45" or "ultra90".
|
|
1333
|
+
ancillary_files : dict
|
|
1334
|
+
Ancillary files for lookup.
|
|
1335
|
+
|
|
1336
|
+
Returns
|
|
1337
|
+
-------
|
|
1338
|
+
valid_mask : NDArray
|
|
1339
|
+
Boolean array indicating whether back TOF is valid.
|
|
1340
|
+
|
|
1341
|
+
Notes
|
|
1342
|
+
-----
|
|
1343
|
+
From page 33 of the IMAP-Ultra Flight Software Specification document.
|
|
1344
|
+
"""
|
|
1345
|
+
_, _, _, _, tofx, tofy = get_ph_tof_and_back_positions(
|
|
1346
|
+
de_dataset, xf, "ultra45", ancillary_files
|
|
1347
|
+
)
|
|
1348
|
+
diff = tofy - tofx
|
|
1349
|
+
|
|
1350
|
+
indices = np.nonzero(
|
|
1351
|
+
np.isin(de_dataset["stop_type"], [StopType.Top.value, StopType.Bottom.value])
|
|
1352
|
+
)[0]
|
|
1353
|
+
de_ph = de_dataset.isel(epoch=indices)
|
|
1354
|
+
|
|
1355
|
+
top_mask = de_ph["stop_type"] == StopType.Top.value
|
|
1356
|
+
bottom_mask = de_ph["stop_type"] == StopType.Bottom.value
|
|
1357
|
+
|
|
1358
|
+
valid = np.zeros_like(diff, dtype=bool)
|
|
1359
|
+
|
|
1360
|
+
diff_tp_min = get_image_params("TOFDiffTpMin", sensor, ancillary_files)
|
|
1361
|
+
diff_tp_max = get_image_params("TOFDiffTpMax", sensor, ancillary_files)
|
|
1362
|
+
diff_bt_min = get_image_params("TOFDiffBtMin", sensor, ancillary_files)
|
|
1363
|
+
diff_bt_max = get_image_params("TOFDiffBtMax", sensor, ancillary_files)
|
|
1364
|
+
|
|
1365
|
+
valid[top_mask] = (diff[top_mask] >= diff_tp_min) & (diff[top_mask] <= diff_tp_max)
|
|
1366
|
+
valid[bottom_mask] = (diff[bottom_mask] >= diff_bt_min) & (
|
|
1367
|
+
diff[bottom_mask] <= diff_bt_max
|
|
1368
|
+
)
|
|
1369
|
+
|
|
1370
|
+
return valid
|
|
1371
|
+
|
|
1372
|
+
|
|
1373
|
+
def is_coin_ph_valid(
|
|
1374
|
+
etof: NDArray,
|
|
1375
|
+
xc: NDArray,
|
|
1376
|
+
xb: NDArray,
|
|
1377
|
+
stop_north_tdc: NDArray,
|
|
1378
|
+
stop_south_tdc: NDArray,
|
|
1379
|
+
stop_east_tdc: NDArray,
|
|
1380
|
+
stop_west_tdc: NDArray,
|
|
1381
|
+
sensor: str,
|
|
1382
|
+
ancillary_files: dict,
|
|
1383
|
+
quality_flags: NDArray,
|
|
1384
|
+
) -> NDArray:
|
|
1385
|
+
"""
|
|
1386
|
+
Determine event validity.
|
|
1387
|
+
|
|
1388
|
+
Parameters
|
|
1389
|
+
----------
|
|
1390
|
+
etof : NDArray
|
|
1391
|
+
Time for the electrons to travel back to the coincidence
|
|
1392
|
+
anode (tenths of a nanosecond).
|
|
1393
|
+
xc : NDArray
|
|
1394
|
+
X coincidence position (hundredths of a millimeter).
|
|
1395
|
+
xb : NDArray
|
|
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.
|
|
1405
|
+
sensor : str
|
|
1406
|
+
Sensor name: "ultra45" or "ultra90".
|
|
1407
|
+
ancillary_files : dict
|
|
1408
|
+
Ancillary files for lookup.
|
|
1409
|
+
quality_flags : NDArray
|
|
1410
|
+
Quality flag to set when there is an outlier.
|
|
1411
|
+
|
|
1412
|
+
Returns
|
|
1413
|
+
-------
|
|
1414
|
+
combined_mask : NDArray
|
|
1415
|
+
Boolean array indicating whether back TOF is valid.
|
|
1416
|
+
|
|
1417
|
+
Notes
|
|
1418
|
+
-----
|
|
1419
|
+
From page 36 of the IMAP-Ultra Flight Software Specification document.
|
|
1420
|
+
"""
|
|
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
|
+
)
|
|
1425
|
+
|
|
1426
|
+
# Hundredths of a mm.
|
|
1427
|
+
diff_x = xc - xb
|
|
1428
|
+
|
|
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
|
+
)
|
|
1439
|
+
|
|
1440
|
+
condition_1 = (diff_x >= t1) & (diff_x <= t2)
|
|
1441
|
+
condition_2 = (diff_x >= -t2) & (diff_x <= -t1)
|
|
1442
|
+
|
|
1443
|
+
spatial_valid = condition_1 | condition_2
|
|
1444
|
+
|
|
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
|
|
1213
1461
|
|
|
1214
|
-
|
|
1215
|
-
# if r < get_image_params("PathSteepThresh"):
|
|
1216
|
-
# # bin = ExTOFSpeciesSteep[energy, ctof]
|
|
1217
|
-
# elif r < get_image_params("PathMediumThresh"):
|
|
1218
|
-
# # bin = ExTOFSpeciesMedium[energy, ctof]
|
|
1219
|
-
# else:
|
|
1220
|
-
# # bin = ExTOFSpeciesFlat[energy, ctof]
|
|
1462
|
+
quality_flags[~combined_mask] |= ImapDEOutliersUltraFlags.COINPH.value
|
|
1221
1463
|
|
|
1222
|
-
return
|
|
1464
|
+
return combined_mask
|