imap-processing 0.19.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/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 +11 -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 +20 -8
- 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 -0
- 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 +15 -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/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 +54 -10
- imap_processing/ultra/utils/ultra_l1_utils.py +10 -5
- {imap_processing-0.19.0.dist-info → imap_processing-0.19.2.dist-info}/METADATA +1 -1
- {imap_processing-0.19.0.dist-info → imap_processing-0.19.2.dist-info}/RECORD +62 -60
- 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.2.dist-info}/LICENSE +0 -0
- {imap_processing-0.19.0.dist-info → imap_processing-0.19.2.dist-info}/WHEEL +0 -0
- {imap_processing-0.19.0.dist-info → imap_processing-0.19.2.dist-info}/entry_points.txt +0 -0
|
@@ -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
|
|
@@ -2,25 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
|
|
5
|
+
import astropy_healpix.healpy as hp
|
|
5
6
|
import numpy as np
|
|
6
|
-
import pandas as pd
|
|
7
7
|
import xarray as xr
|
|
8
8
|
|
|
9
|
+
from imap_processing.quality_flags import ImapPSETUltraFlags
|
|
9
10
|
from imap_processing.spice.repoint import get_pointing_times
|
|
10
11
|
from imap_processing.spice.time import (
|
|
11
12
|
et_to_met,
|
|
12
13
|
met_to_ttj2000ns,
|
|
13
|
-
sct_to_et,
|
|
14
14
|
ttj2000ns_to_et,
|
|
15
15
|
)
|
|
16
16
|
from imap_processing.ultra.l1b.ultra_l1b_culling import get_de_rejection_mask
|
|
17
17
|
from imap_processing.ultra.l1c.l1c_lookup_utils import (
|
|
18
|
-
|
|
18
|
+
calculate_fwhm_spun_scattering,
|
|
19
19
|
get_spacecraft_pointing_lookup_tables,
|
|
20
20
|
)
|
|
21
|
+
from imap_processing.ultra.l1c.ultra_l1c_culling import compute_culling_mask
|
|
21
22
|
from imap_processing.ultra.l1c.ultra_l1c_pset_bins import (
|
|
22
23
|
build_energy_bins,
|
|
23
24
|
get_efficiencies_and_geometric_function,
|
|
25
|
+
get_energy_delta_minus_plus,
|
|
24
26
|
get_helio_adjusted_data,
|
|
25
27
|
get_spacecraft_exposure_times,
|
|
26
28
|
get_spacecraft_histogram,
|
|
@@ -32,15 +34,14 @@ logger = logging.getLogger(__name__)
|
|
|
32
34
|
|
|
33
35
|
def calculate_helio_pset(
|
|
34
36
|
de_dataset: xr.Dataset,
|
|
35
|
-
|
|
36
|
-
cullingmask_dataset: xr.Dataset,
|
|
37
|
+
goodtimes_dataset: xr.Dataset,
|
|
37
38
|
rates_dataset: xr.Dataset,
|
|
38
39
|
params_dataset: xr.Dataset,
|
|
39
40
|
name: str,
|
|
40
41
|
ancillary_files: dict,
|
|
41
42
|
instrument_id: int,
|
|
42
|
-
species_id:
|
|
43
|
-
) -> xr.Dataset:
|
|
43
|
+
species_id: list,
|
|
44
|
+
) -> xr.Dataset | None:
|
|
44
45
|
"""
|
|
45
46
|
Create dictionary with defined datatype for Pointing Set Grid Data.
|
|
46
47
|
|
|
@@ -48,10 +49,8 @@ def calculate_helio_pset(
|
|
|
48
49
|
----------
|
|
49
50
|
de_dataset : xarray.Dataset
|
|
50
51
|
Dataset containing de data.
|
|
51
|
-
|
|
52
|
-
Dataset containing
|
|
53
|
-
cullingmask_dataset : xarray.Dataset
|
|
54
|
-
Dataset containing cullingmask data.
|
|
52
|
+
goodtimes_dataset : xarray.Dataset
|
|
53
|
+
Dataset containing goodtimes data.
|
|
55
54
|
rates_dataset : xarray.Dataset
|
|
56
55
|
Dataset containing image rates data.
|
|
57
56
|
params_dataset : xarray.Dataset
|
|
@@ -62,8 +61,8 @@ def calculate_helio_pset(
|
|
|
62
61
|
Ancillary files.
|
|
63
62
|
instrument_id : int
|
|
64
63
|
Instrument ID, either 45 or 90.
|
|
65
|
-
species_id :
|
|
66
|
-
Species ID
|
|
64
|
+
species_id : List
|
|
65
|
+
Species ID.
|
|
67
66
|
|
|
68
67
|
Returns
|
|
69
68
|
-------
|
|
@@ -72,14 +71,14 @@ def calculate_helio_pset(
|
|
|
72
71
|
"""
|
|
73
72
|
pset_dict: dict[str, np.ndarray] = {}
|
|
74
73
|
# Select only the species we are interested in.
|
|
75
|
-
indices = np.where(de_dataset["
|
|
74
|
+
indices = np.where(np.isin(de_dataset["e_bin"].values, species_id))[0]
|
|
76
75
|
species_dataset = de_dataset.isel(epoch=indices)
|
|
77
76
|
|
|
78
77
|
rejected = get_de_rejection_mask(
|
|
79
78
|
species_dataset["quality_scattering"].values,
|
|
80
79
|
species_dataset["quality_outliers"].values,
|
|
81
80
|
)
|
|
82
|
-
|
|
81
|
+
species_dataset = species_dataset.isel(epoch=~rejected)
|
|
83
82
|
|
|
84
83
|
v_mag_helio_spacecraft = np.linalg.norm(
|
|
85
84
|
species_dataset["velocity_dps_helio"].values, axis=1
|
|
@@ -89,15 +88,6 @@ def calculate_helio_pset(
|
|
|
89
88
|
/ v_mag_helio_spacecraft[:, np.newaxis]
|
|
90
89
|
)
|
|
91
90
|
intervals, _, energy_bin_geometric_means = build_energy_bins()
|
|
92
|
-
counts, latitude, longitude, n_pix = get_spacecraft_histogram(
|
|
93
|
-
vhat_dps_helio,
|
|
94
|
-
species_dataset["energy_heliosphere"].values,
|
|
95
|
-
intervals,
|
|
96
|
-
nside=128,
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
healpix = np.arange(n_pix)
|
|
100
|
-
|
|
101
91
|
# Get lookup table for FOR indices by spin phase step
|
|
102
92
|
(
|
|
103
93
|
for_indices_by_spin_phase,
|
|
@@ -106,26 +96,39 @@ def calculate_helio_pset(
|
|
|
106
96
|
ra_and_dec,
|
|
107
97
|
boundary_scale_factors,
|
|
108
98
|
) = get_spacecraft_pointing_lookup_tables(ancillary_files, instrument_id)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
99
|
+
|
|
100
|
+
logger.info("calculating spun FWHM scattering values.")
|
|
101
|
+
pixels_below_scattering, scattering_theta, scattering_phi, scattering_thresholds = (
|
|
102
|
+
calculate_fwhm_spun_scattering(
|
|
103
|
+
for_indices_by_spin_phase,
|
|
104
|
+
theta_vals,
|
|
105
|
+
phi_vals,
|
|
106
|
+
ancillary_files,
|
|
107
|
+
instrument_id,
|
|
114
108
|
)
|
|
109
|
+
)
|
|
115
110
|
|
|
116
|
-
|
|
117
|
-
|
|
111
|
+
nside = hp.npix2nside(for_indices_by_spin_phase.shape[0])
|
|
112
|
+
counts, latitude, longitude, n_pix = get_spacecraft_histogram(
|
|
113
|
+
vhat_dps_helio,
|
|
114
|
+
species_dataset["energy_heliosphere"].values,
|
|
115
|
+
intervals,
|
|
116
|
+
nside=nside,
|
|
117
|
+
)
|
|
118
|
+
helio_pset_quality_flags = np.full(
|
|
119
|
+
n_pix, ImapPSETUltraFlags.NONE.value, dtype=np.uint16
|
|
118
120
|
)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
121
|
+
healpix = np.arange(n_pix)
|
|
122
|
+
|
|
123
|
+
logger.info("Calculating spacecraft exposure times with deadtime correction.")
|
|
122
124
|
exposure_time, deadtime_ratios = get_spacecraft_exposure_times(
|
|
123
|
-
df_exposure,
|
|
124
125
|
rates_dataset,
|
|
125
126
|
params_dataset,
|
|
126
127
|
pixels_below_scattering,
|
|
127
128
|
boundary_scale_factors,
|
|
129
|
+
n_pix=n_pix,
|
|
128
130
|
)
|
|
131
|
+
logger.info("Calculating spun efficiencies and geometric function.")
|
|
129
132
|
# calculate efficiency and geometric function as a function of energy
|
|
130
133
|
efficiencies, geometric_function = get_efficiencies_and_geometric_function(
|
|
131
134
|
pixels_below_scattering,
|
|
@@ -136,11 +139,12 @@ def calculate_helio_pset(
|
|
|
136
139
|
ancillary_files,
|
|
137
140
|
)
|
|
138
141
|
# Get midpoint timestamp for pointing.
|
|
139
|
-
# TODO remove sct_to_et conversion
|
|
140
142
|
pointing_start, pointing_stop = get_pointing_times(
|
|
141
|
-
et_to_met(
|
|
143
|
+
et_to_met(species_dataset["event_times"].data[0])
|
|
142
144
|
)
|
|
143
145
|
mid_time = ttj2000ns_to_et(met_to_ttj2000ns((pointing_start + pointing_stop) / 2))
|
|
146
|
+
|
|
147
|
+
logger.info("Adjusting data for helio frame.")
|
|
144
148
|
exposure_time, efficiency, geometric_function = get_helio_adjusted_data(
|
|
145
149
|
mid_time,
|
|
146
150
|
exposure_time,
|
|
@@ -148,11 +152,26 @@ def calculate_helio_pset(
|
|
|
148
152
|
efficiencies,
|
|
149
153
|
ra_and_dec[:, 0],
|
|
150
154
|
ra_and_dec[:, 1],
|
|
155
|
+
nside=nside,
|
|
151
156
|
)
|
|
152
157
|
sensitivity = efficiencies * geometric_function
|
|
153
158
|
|
|
154
|
-
|
|
155
|
-
|
|
159
|
+
start: float = np.min(species_dataset["event_times"].values)
|
|
160
|
+
end: float = np.max(species_dataset["event_times"].values)
|
|
161
|
+
|
|
162
|
+
# Time bins in 30 minute intervals
|
|
163
|
+
time_bins = np.arange(start, end + 1800, 1800)
|
|
164
|
+
|
|
165
|
+
# Compute mask for culling the Earth
|
|
166
|
+
compute_culling_mask(
|
|
167
|
+
time_bins,
|
|
168
|
+
6378.1, # Earth radius
|
|
169
|
+
helio_pset_quality_flags,
|
|
170
|
+
nside=nside,
|
|
171
|
+
)
|
|
172
|
+
pointing_start = met_to_ttj2000ns(pointing_start)
|
|
173
|
+
# Epoch should be the start of the pointing
|
|
174
|
+
pset_dict["epoch"] = np.atleast_1d(pointing_start).astype(np.int64)
|
|
156
175
|
pset_dict["counts"] = counts[np.newaxis, ...]
|
|
157
176
|
pset_dict["latitude"] = latitude[np.newaxis, ...]
|
|
158
177
|
pset_dict["longitude"] = longitude[np.newaxis, ...]
|
|
@@ -167,6 +186,16 @@ def calculate_helio_pset(
|
|
|
167
186
|
pset_dict["geometric_function"] = geometric_function
|
|
168
187
|
pset_dict["dead_time_ratio"] = deadtime_ratios
|
|
169
188
|
pset_dict["spin_phase_step"] = np.arange(len(deadtime_ratios))
|
|
189
|
+
pset_dict["quality_flags"] = helio_pset_quality_flags[np.newaxis, ...]
|
|
190
|
+
|
|
191
|
+
pset_dict["scatter_theta"] = scattering_theta
|
|
192
|
+
pset_dict["scatter_phi"] = scattering_phi
|
|
193
|
+
pset_dict["scatter_threshold"] = scattering_thresholds
|
|
194
|
+
|
|
195
|
+
# Add the energy delta plus/minus to the dataset
|
|
196
|
+
energy_delta_minus, energy_delta_plus = get_energy_delta_minus_plus()
|
|
197
|
+
pset_dict["energy_delta_minus"] = energy_delta_minus
|
|
198
|
+
pset_dict["energy_delta_plus"] = energy_delta_plus
|
|
170
199
|
|
|
171
200
|
dataset = create_dataset(pset_dict, name, "l1c")
|
|
172
201
|
|