imap-processing 0.17.0__py3-none-any.whl → 0.19.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/ancillary/ancillary_dataset_combiner.py +161 -1
- imap_processing/ccsds/excel_to_xtce.py +12 -0
- imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +6 -6
- imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +312 -274
- imap_processing/cdf/config/imap_codice_l1b_variable_attrs.yaml +39 -28
- imap_processing/cdf/config/imap_codice_l2_variable_attrs.yaml +1048 -183
- imap_processing/cdf/config/imap_constant_attrs.yaml +4 -2
- imap_processing/cdf/config/imap_glows_l1b_variable_attrs.yaml +12 -0
- 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_hit_l1a_variable_attrs.yaml +163 -100
- imap_processing/cdf/config/imap_hit_l2_variable_attrs.yaml +4 -4
- imap_processing/cdf/config/imap_ialirt_l1_variable_attrs.yaml +97 -54
- imap_processing/cdf/config/imap_idex_l2a_variable_attrs.yaml +33 -4
- imap_processing/cdf/config/imap_idex_l2b_variable_attrs.yaml +44 -44
- imap_processing/cdf/config/imap_idex_l2c_variable_attrs.yaml +77 -61
- imap_processing/cdf/config/imap_lo_global_cdf_attrs.yaml +30 -0
- 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 +99 -2
- imap_processing/cdf/config/imap_mag_l1c_variable_attrs.yaml +24 -1
- imap_processing/cdf/config/imap_ultra_global_cdf_attrs.yaml +60 -0
- imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +99 -11
- imap_processing/cdf/config/imap_ultra_l1c_variable_attrs.yaml +50 -7
- imap_processing/cli.py +121 -44
- imap_processing/codice/codice_l1a.py +165 -77
- imap_processing/codice/codice_l1b.py +1 -1
- imap_processing/codice/codice_l2.py +118 -19
- imap_processing/codice/constants.py +1217 -1089
- imap_processing/decom.py +1 -4
- imap_processing/ena_maps/ena_maps.py +32 -25
- 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 +99 -9
- imap_processing/glows/l1b/glows_l1b_data.py +350 -38
- 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_l2.py +84 -51
- imap_processing/hi/utils.py +153 -8
- imap_processing/hit/l0/constants.py +3 -0
- imap_processing/hit/l0/decom_hit.py +5 -8
- imap_processing/hit/l1a/hit_l1a.py +375 -45
- imap_processing/hit/l1b/constants.py +5 -0
- imap_processing/hit/l1b/hit_l1b.py +61 -131
- imap_processing/hit/l2/constants.py +1 -1
- imap_processing/hit/l2/hit_l2.py +10 -11
- imap_processing/ialirt/calculate_ingest.py +219 -0
- imap_processing/ialirt/constants.py +32 -1
- imap_processing/ialirt/generate_coverage.py +201 -0
- imap_processing/ialirt/l0/ialirt_spice.py +5 -2
- imap_processing/ialirt/l0/parse_mag.py +337 -29
- imap_processing/ialirt/l0/process_hit.py +5 -3
- imap_processing/ialirt/l0/process_swapi.py +41 -25
- imap_processing/ialirt/l0/process_swe.py +23 -7
- imap_processing/ialirt/process_ephemeris.py +70 -14
- imap_processing/ialirt/utils/constants.py +22 -16
- imap_processing/ialirt/utils/create_xarray.py +42 -19
- imap_processing/idex/idex_constants.py +1 -5
- 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 +313 -119
- imap_processing/idex/idex_utils.py +1 -3
- imap_processing/lo/l0/lo_apid.py +1 -0
- imap_processing/lo/l0/lo_science.py +25 -24
- imap_processing/lo/l1a/lo_l1a.py +44 -0
- imap_processing/lo/l1b/lo_l1b.py +3 -3
- imap_processing/lo/l1c/lo_l1c.py +116 -50
- imap_processing/lo/l2/lo_l2.py +29 -29
- imap_processing/lo/lo_ancillary.py +55 -0
- imap_processing/lo/packet_definitions/lo_xtce.xml +5359 -106
- imap_processing/mag/constants.py +1 -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/__init__.py +0 -0
- imap_processing/mag/l1d/mag_l1d.py +176 -0
- imap_processing/mag/l1d/mag_l1d_data.py +725 -0
- imap_processing/mag/l2/__init__.py +0 -0
- imap_processing/mag/l2/mag_l2.py +25 -20
- imap_processing/mag/l2/mag_l2_data.py +199 -130
- imap_processing/quality_flags.py +28 -2
- imap_processing/spice/geometry.py +101 -36
- imap_processing/spice/pointing_frame.py +1 -7
- imap_processing/spice/repoint.py +29 -2
- imap_processing/spice/spin.py +32 -8
- imap_processing/spice/time.py +60 -19
- imap_processing/swapi/l1/swapi_l1.py +10 -4
- imap_processing/swapi/l2/swapi_l2.py +66 -24
- imap_processing/swapi/swapi_utils.py +1 -1
- imap_processing/swe/l1b/swe_l1b.py +3 -6
- imap_processing/ultra/constants.py +28 -3
- imap_processing/ultra/l0/decom_tools.py +15 -8
- imap_processing/ultra/l0/decom_ultra.py +35 -11
- imap_processing/ultra/l0/ultra_utils.py +102 -12
- imap_processing/ultra/l1a/ultra_l1a.py +26 -6
- imap_processing/ultra/l1b/cullingmask.py +6 -3
- imap_processing/ultra/l1b/de.py +122 -26
- imap_processing/ultra/l1b/extendedspin.py +29 -2
- imap_processing/ultra/l1b/lookup_utils.py +424 -50
- imap_processing/ultra/l1b/quality_flag_filters.py +23 -0
- imap_processing/ultra/l1b/ultra_l1b_culling.py +356 -5
- imap_processing/ultra/l1b/ultra_l1b_extended.py +534 -90
- imap_processing/ultra/l1c/helio_pset.py +127 -7
- imap_processing/ultra/l1c/l1c_lookup_utils.py +256 -0
- imap_processing/ultra/l1c/spacecraft_pset.py +90 -15
- imap_processing/ultra/l1c/ultra_l1c.py +6 -0
- imap_processing/ultra/l1c/ultra_l1c_culling.py +85 -0
- imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +446 -341
- imap_processing/ultra/l2/ultra_l2.py +0 -1
- imap_processing/ultra/utils/ultra_l1_utils.py +40 -3
- imap_processing/utils.py +3 -4
- {imap_processing-0.17.0.dist-info → imap_processing-0.19.0.dist-info}/METADATA +3 -3
- {imap_processing-0.17.0.dist-info → imap_processing-0.19.0.dist-info}/RECORD +126 -126
- imap_processing/idex/idex_l2c.py +0 -250
- imap_processing/spice/kernels.py +0 -187
- imap_processing/ultra/lookup_tables/Angular_Profiles_FM45_LeftSlit.csv +0 -526
- imap_processing/ultra/lookup_tables/Angular_Profiles_FM45_RightSlit.csv +0 -526
- imap_processing/ultra/lookup_tables/Angular_Profiles_FM90_LeftSlit.csv +0 -526
- imap_processing/ultra/lookup_tables/Angular_Profiles_FM90_RightSlit.csv +0 -524
- imap_processing/ultra/lookup_tables/EgyNorm.mem.csv +0 -32769
- imap_processing/ultra/lookup_tables/FM45_Startup1_ULTRA_IMGPARAMS_20240719.csv +0 -2
- imap_processing/ultra/lookup_tables/FM90_Startup1_ULTRA_IMGPARAMS_20240719.csv +0 -2
- imap_processing/ultra/lookup_tables/dps_grid45_compressed.cdf +0 -0
- imap_processing/ultra/lookup_tables/ultra45_back-pos-luts.csv +0 -4097
- imap_processing/ultra/lookup_tables/ultra45_tdc_norm.csv +0 -2050
- imap_processing/ultra/lookup_tables/ultra90_back-pos-luts.csv +0 -4097
- imap_processing/ultra/lookup_tables/ultra90_tdc_norm.csv +0 -2050
- imap_processing/ultra/lookup_tables/yadjust.csv +0 -257
- {imap_processing-0.17.0.dist-info → imap_processing-0.19.0.dist-info}/LICENSE +0 -0
- {imap_processing-0.17.0.dist-info → imap_processing-0.19.0.dist-info}/WHEEL +0 -0
- {imap_processing-0.17.0.dist-info → imap_processing-0.19.0.dist-info}/entry_points.txt +0 -0
imap_processing/idex/idex_l2b.py
CHANGED
|
@@ -17,8 +17,9 @@ Examples
|
|
|
17
17
|
l1a_data, l1a_evt_data, l1b_evt_data = PacketParser(l0_file)
|
|
18
18
|
l1b_data = idex_l1b(l1a_data)
|
|
19
19
|
l1a_data = idex_l2a(l1b_data)
|
|
20
|
-
|
|
21
|
-
write_cdf(
|
|
20
|
+
l2b_and_l2c_datasets = idex_l2b(l2a_data, [evt_data])
|
|
21
|
+
write_cdf(l2b_and_l2c_datasets[0])
|
|
22
|
+
write_cdf(l2b_and_l2c_datasets[1])
|
|
22
23
|
"""
|
|
23
24
|
|
|
24
25
|
import collections
|
|
@@ -29,8 +30,12 @@ from datetime import datetime, timedelta
|
|
|
29
30
|
import numpy as np
|
|
30
31
|
import xarray as xr
|
|
31
32
|
|
|
33
|
+
from imap_processing.ena_maps.ena_maps import SkyTilingType
|
|
34
|
+
from imap_processing.ena_maps.utils.spatial_utils import AzElSkyGrid
|
|
32
35
|
from imap_processing.idex.idex_constants import (
|
|
33
36
|
FG_TO_KG,
|
|
37
|
+
IDEX_EVENT_REFERENCE_FRAME,
|
|
38
|
+
IDEX_SPACING_DEG,
|
|
34
39
|
SECONDS_IN_DAY,
|
|
35
40
|
IDEXEvtAcquireCodes,
|
|
36
41
|
)
|
|
@@ -71,12 +76,20 @@ CHARGE_BIN_EDGES = np.array(
|
|
|
71
76
|
)
|
|
72
77
|
SPIN_PHASE_BIN_EDGES = np.array([0, 90, 180, 270, 360])
|
|
73
78
|
|
|
79
|
+
# Get the rectangular map grid with the specified spacing
|
|
80
|
+
SKY_GRID = AzElSkyGrid(IDEX_SPACING_DEG)
|
|
81
|
+
LON_BINS_EDGES = SKY_GRID.az_bin_edges
|
|
82
|
+
LAT_BINS_EDGES = SKY_GRID.el_bin_edges
|
|
83
|
+
|
|
74
84
|
|
|
75
85
|
def idex_l2b(
|
|
76
86
|
l2a_datasets: list[xr.Dataset], evt_datasets: list[xr.Dataset]
|
|
77
|
-
) -> xr.Dataset:
|
|
87
|
+
) -> list[xr.Dataset]:
|
|
78
88
|
"""
|
|
79
|
-
Will process IDEX l2a data to create l2b data products.
|
|
89
|
+
Will process IDEX l2a data to create l2b and l2c data products.
|
|
90
|
+
|
|
91
|
+
IDEX L2B processing creates L2b and L2c at the same time because L2c needs no
|
|
92
|
+
additional dependencies and is a natural extension of L2b processing.
|
|
80
93
|
|
|
81
94
|
Parameters
|
|
82
95
|
----------
|
|
@@ -87,140 +100,254 @@ def idex_l2b(
|
|
|
87
100
|
|
|
88
101
|
Returns
|
|
89
102
|
-------
|
|
90
|
-
|
|
91
|
-
The``xarray``
|
|
103
|
+
list[xarray.Dataset]
|
|
104
|
+
The``xarray`` datasets containing the l2b and l2c science data and supporting
|
|
105
|
+
metadata.
|
|
92
106
|
"""
|
|
93
107
|
logger.info(
|
|
94
|
-
|
|
95
|
-
|
|
108
|
+
"Running IDEX L2B and L2C processing on L2a datasets. NOTE: L2C datasets are "
|
|
109
|
+
"processed at the same time as L2B datasets because L2C needs no additional "
|
|
110
|
+
"dependencies."
|
|
96
111
|
)
|
|
97
|
-
|
|
98
112
|
# create the attribute manager for this data level
|
|
99
|
-
|
|
113
|
+
idex_l2b_attrs = get_idex_attrs("l2b")
|
|
114
|
+
idex_l2c_attrs = get_idex_attrs("l2c")
|
|
100
115
|
evt_dataset = xr.concat(evt_datasets, dim="epoch")
|
|
101
116
|
|
|
102
117
|
# Concat all the l2a datasets together
|
|
103
118
|
l2a_dataset = xr.concat(l2a_datasets, dim="epoch")
|
|
104
119
|
epoch_doy_unique = np.unique(epoch_to_doy(l2a_dataset["epoch"].data))
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
120
|
+
(
|
|
121
|
+
counts_by_charge,
|
|
122
|
+
counts_by_mass,
|
|
123
|
+
counts_by_charge_map,
|
|
124
|
+
counts_by_mass_map,
|
|
125
|
+
daily_epoch,
|
|
126
|
+
) = compute_counts_by_charge_and_mass(l2a_dataset, epoch_doy_unique)
|
|
108
127
|
# Get science acquisition percentage for each day
|
|
109
128
|
daily_on_percentage = get_science_acquisition_on_percentage(evt_dataset)
|
|
110
|
-
|
|
111
|
-
|
|
129
|
+
(
|
|
130
|
+
rate_by_charge,
|
|
131
|
+
rate_by_mass,
|
|
132
|
+
rate_by_charge_map,
|
|
133
|
+
rate_by_mass_map,
|
|
134
|
+
rate_quality_flags,
|
|
135
|
+
) = compute_rates_by_charge_and_mass(
|
|
136
|
+
counts_by_charge,
|
|
137
|
+
counts_by_mass,
|
|
138
|
+
counts_by_charge_map,
|
|
139
|
+
counts_by_mass_map,
|
|
140
|
+
epoch_doy_unique,
|
|
141
|
+
daily_on_percentage,
|
|
112
142
|
)
|
|
113
143
|
# Create l2b Dataset
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
144
|
+
charge_bin_means = np.sqrt(CHARGE_BIN_EDGES[:-1] * CHARGE_BIN_EDGES[1:])
|
|
145
|
+
mass_bin_means = np.sqrt(MASS_BIN_EDGES[:-1] * MASS_BIN_EDGES[1:])
|
|
146
|
+
spin_phase_means = (SPIN_PHASE_BIN_EDGES[:-1] + SPIN_PHASE_BIN_EDGES[1:]) / 2
|
|
147
|
+
|
|
148
|
+
# Define xarrays that are shared between l2b and l2c
|
|
117
149
|
epoch = xr.DataArray(
|
|
118
150
|
name="epoch",
|
|
119
151
|
data=daily_epoch,
|
|
120
152
|
dims="epoch",
|
|
121
|
-
attrs=
|
|
153
|
+
attrs=idex_l2b_attrs.get_variable_attributes("epoch", check_schema=False),
|
|
122
154
|
)
|
|
123
|
-
|
|
155
|
+
|
|
156
|
+
common_vars = {
|
|
124
157
|
"impact_day_of_year": xr.DataArray(
|
|
125
158
|
name="impact_day_of_year",
|
|
126
159
|
data=epoch_doy_unique,
|
|
127
160
|
dims="epoch",
|
|
128
|
-
attrs=
|
|
129
|
-
),
|
|
130
|
-
"rate_calculation_quality_flags": xr.DataArray(
|
|
131
|
-
name="rate_calculation_quality_flags",
|
|
132
|
-
data=rate_quality_flags,
|
|
133
|
-
dims="epoch",
|
|
134
|
-
attrs=idex_attrs.get_variable_attributes("rate_calculation_quality_flags"),
|
|
161
|
+
attrs=idex_l2b_attrs.get_variable_attributes("impact_day_of_year"),
|
|
135
162
|
),
|
|
136
163
|
"charge_labels": xr.DataArray(
|
|
137
164
|
name="impact_charge_labels",
|
|
138
|
-
data=
|
|
139
|
-
dims="
|
|
140
|
-
attrs=
|
|
165
|
+
data=charge_bin_means.astype(str),
|
|
166
|
+
dims="impact_charge",
|
|
167
|
+
attrs=idex_l2b_attrs.get_variable_attributes(
|
|
141
168
|
"charge_labels", check_schema=False
|
|
142
169
|
),
|
|
143
170
|
),
|
|
144
|
-
"spin_phase_labels": xr.DataArray(
|
|
145
|
-
name="spin_phase_labels",
|
|
146
|
-
data=spin_phase_bins.astype(str),
|
|
147
|
-
dims="spin_phase_bins",
|
|
148
|
-
attrs=idex_attrs.get_variable_attributes(
|
|
149
|
-
"spin_phase_labels", check_schema=False
|
|
150
|
-
),
|
|
151
|
-
),
|
|
152
171
|
"mass_labels": xr.DataArray(
|
|
153
172
|
name="mass_labels",
|
|
154
|
-
data=
|
|
155
|
-
dims="
|
|
156
|
-
attrs=
|
|
173
|
+
data=mass_bin_means.astype(str),
|
|
174
|
+
dims="mass",
|
|
175
|
+
attrs=idex_l2b_attrs.get_variable_attributes(
|
|
176
|
+
"mass_labels", check_schema=False
|
|
177
|
+
),
|
|
157
178
|
),
|
|
158
|
-
"
|
|
159
|
-
name="
|
|
160
|
-
data=
|
|
161
|
-
dims="
|
|
162
|
-
attrs=
|
|
163
|
-
"
|
|
179
|
+
"impact_charge": xr.DataArray(
|
|
180
|
+
name="impact_charge",
|
|
181
|
+
data=charge_bin_means,
|
|
182
|
+
dims="impact_charge",
|
|
183
|
+
attrs=idex_l2b_attrs.get_variable_attributes(
|
|
184
|
+
"impact_charge", check_schema=False
|
|
164
185
|
),
|
|
165
186
|
),
|
|
166
|
-
"
|
|
167
|
-
name="
|
|
168
|
-
data=
|
|
169
|
-
dims="
|
|
170
|
-
attrs=
|
|
187
|
+
"mass": xr.DataArray(
|
|
188
|
+
name="mass",
|
|
189
|
+
data=mass_bin_means,
|
|
190
|
+
dims="mass",
|
|
191
|
+
attrs=idex_l2b_attrs.get_variable_attributes("mass", check_schema=False),
|
|
171
192
|
),
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
193
|
+
}
|
|
194
|
+
l2b_vars = common_vars | {
|
|
195
|
+
"spin_phase": xr.DataArray(
|
|
196
|
+
name="spin_phase",
|
|
197
|
+
data=spin_phase_means,
|
|
198
|
+
dims="spin_phase",
|
|
199
|
+
attrs=idex_l2b_attrs.get_variable_attributes(
|
|
200
|
+
"spin_phase", check_schema=False
|
|
201
|
+
),
|
|
202
|
+
),
|
|
203
|
+
"spin_phase_labels": xr.DataArray(
|
|
204
|
+
name="spin_phase_labels",
|
|
205
|
+
data=spin_phase_means.astype(str),
|
|
206
|
+
dims="spin_phase",
|
|
207
|
+
attrs=idex_l2b_attrs.get_variable_attributes(
|
|
208
|
+
"spin_phase_labels", check_schema=False
|
|
209
|
+
),
|
|
210
|
+
),
|
|
211
|
+
"rate_calculation_quality_flags": xr.DataArray(
|
|
212
|
+
name="rate_calculation_quality_flags",
|
|
213
|
+
data=rate_quality_flags,
|
|
214
|
+
dims="epoch",
|
|
215
|
+
attrs=idex_l2b_attrs.get_variable_attributes(
|
|
216
|
+
"rate_calculation_quality_flags"
|
|
178
217
|
),
|
|
179
218
|
),
|
|
180
219
|
"counts_by_charge": xr.DataArray(
|
|
181
220
|
name="counts_by_charge",
|
|
182
221
|
data=counts_by_charge.astype(np.int64),
|
|
183
|
-
dims=("epoch", "
|
|
184
|
-
attrs=
|
|
222
|
+
dims=("epoch", "impact_charge", "spin_phase"),
|
|
223
|
+
attrs=idex_l2b_attrs.get_variable_attributes("counts_by_charge"),
|
|
185
224
|
),
|
|
186
225
|
"counts_by_mass": xr.DataArray(
|
|
187
226
|
name="counts_by_mass",
|
|
188
227
|
data=counts_by_mass.astype(np.int64),
|
|
189
|
-
dims=("epoch", "
|
|
190
|
-
attrs=
|
|
228
|
+
dims=("epoch", "mass", "spin_phase"),
|
|
229
|
+
attrs=idex_l2b_attrs.get_variable_attributes("counts_by_mass"),
|
|
191
230
|
),
|
|
192
231
|
"rate_by_charge": xr.DataArray(
|
|
193
232
|
name="rate_by_charge",
|
|
194
233
|
data=rate_by_charge,
|
|
195
|
-
dims=("epoch", "
|
|
196
|
-
attrs=
|
|
234
|
+
dims=("epoch", "impact_charge", "spin_phase"),
|
|
235
|
+
attrs=idex_l2b_attrs.get_variable_attributes("rate_by_charge"),
|
|
197
236
|
),
|
|
198
237
|
"rate_by_mass": xr.DataArray(
|
|
199
238
|
name="rate_by_mass",
|
|
200
239
|
data=rate_by_mass,
|
|
201
|
-
dims=("epoch", "
|
|
202
|
-
attrs=
|
|
240
|
+
dims=("epoch", "mass", "spin_phase"),
|
|
241
|
+
attrs=idex_l2b_attrs.get_variable_attributes("rate_by_mass"),
|
|
203
242
|
),
|
|
204
243
|
}
|
|
244
|
+
l2c_vars = common_vars | {
|
|
245
|
+
"rectangular_lon_pixel_label": xr.DataArray(
|
|
246
|
+
name="rectangular_lon_pixel_label",
|
|
247
|
+
data=SKY_GRID.az_bin_midpoints.astype(str),
|
|
248
|
+
dims="rectangular_lon_pixel",
|
|
249
|
+
attrs=idex_l2c_attrs.get_variable_attributes(
|
|
250
|
+
"rectangular_lon_pixel_label", check_schema=False
|
|
251
|
+
),
|
|
252
|
+
),
|
|
253
|
+
"rectangular_lat_pixel_label": xr.DataArray(
|
|
254
|
+
name="rectangular_lat_pixel_label",
|
|
255
|
+
data=SKY_GRID.el_bin_midpoints.astype(str),
|
|
256
|
+
dims="rectangular_lat_pixel",
|
|
257
|
+
attrs=idex_l2c_attrs.get_variable_attributes(
|
|
258
|
+
"rectangular_lat_pixel_label", check_schema=False
|
|
259
|
+
),
|
|
260
|
+
),
|
|
261
|
+
"rectangular_lon_pixel": xr.DataArray(
|
|
262
|
+
name="rectangular_lon_pixel",
|
|
263
|
+
data=SKY_GRID.az_bin_midpoints,
|
|
264
|
+
dims="rectangular_lon_pixel",
|
|
265
|
+
attrs=idex_l2c_attrs.get_variable_attributes(
|
|
266
|
+
"rectangular_lon_pixel", check_schema=False
|
|
267
|
+
),
|
|
268
|
+
),
|
|
269
|
+
"rectangular_lat_pixel": xr.DataArray(
|
|
270
|
+
name="rectangular_lat_pixel",
|
|
271
|
+
data=SKY_GRID.el_bin_midpoints,
|
|
272
|
+
dims="rectangular_lat_pixel",
|
|
273
|
+
attrs=idex_l2c_attrs.get_variable_attributes(
|
|
274
|
+
"rectangular_lat_pixel", check_schema=False
|
|
275
|
+
),
|
|
276
|
+
),
|
|
277
|
+
"counts_by_charge_map": xr.DataArray(
|
|
278
|
+
name="counts_by_charge_map",
|
|
279
|
+
data=counts_by_charge_map.astype(np.int64),
|
|
280
|
+
dims=(
|
|
281
|
+
"epoch",
|
|
282
|
+
"impact_charge",
|
|
283
|
+
"rectangular_lon_pixel",
|
|
284
|
+
"rectangular_lat_pixel",
|
|
285
|
+
),
|
|
286
|
+
attrs=idex_l2c_attrs.get_variable_attributes("counts_by_charge_map"),
|
|
287
|
+
),
|
|
288
|
+
"counts_by_mass_map": xr.DataArray(
|
|
289
|
+
name="counts_by_mass_map",
|
|
290
|
+
data=counts_by_mass_map.astype(np.int64),
|
|
291
|
+
dims=(
|
|
292
|
+
"epoch",
|
|
293
|
+
"mass",
|
|
294
|
+
"rectangular_lon_pixel",
|
|
295
|
+
"rectangular_lat_pixel",
|
|
296
|
+
),
|
|
297
|
+
attrs=idex_l2c_attrs.get_variable_attributes("counts_by_mass_map"),
|
|
298
|
+
),
|
|
299
|
+
"rate_by_charge_map": xr.DataArray(
|
|
300
|
+
name="rate_by_charge_map",
|
|
301
|
+
data=rate_by_charge_map,
|
|
302
|
+
dims=(
|
|
303
|
+
"epoch",
|
|
304
|
+
"impact_charge",
|
|
305
|
+
"rectangular_lon_pixel",
|
|
306
|
+
"rectangular_lat_pixel",
|
|
307
|
+
),
|
|
308
|
+
attrs=idex_l2c_attrs.get_variable_attributes("rate_by_charge_map"),
|
|
309
|
+
),
|
|
310
|
+
"rate_by_mass_map": xr.DataArray(
|
|
311
|
+
name="rate_by_mass_map",
|
|
312
|
+
data=rate_by_mass_map,
|
|
313
|
+
dims=(
|
|
314
|
+
"epoch",
|
|
315
|
+
"mass",
|
|
316
|
+
"rectangular_lon_pixel",
|
|
317
|
+
"rectangular_lat_pixel",
|
|
318
|
+
),
|
|
319
|
+
attrs=idex_l2c_attrs.get_variable_attributes("rate_by_mass_map"),
|
|
320
|
+
),
|
|
321
|
+
}
|
|
322
|
+
|
|
205
323
|
l2b_dataset = xr.Dataset(
|
|
206
324
|
coords={"epoch": epoch},
|
|
207
|
-
data_vars=
|
|
208
|
-
attrs=
|
|
325
|
+
data_vars=l2b_vars,
|
|
326
|
+
attrs=idex_l2b_attrs.get_global_attributes("imap_idex_l2b_sci"),
|
|
327
|
+
)
|
|
328
|
+
l2c_dataset = xr.Dataset(
|
|
329
|
+
coords={"epoch": epoch},
|
|
330
|
+
data_vars=l2c_vars,
|
|
209
331
|
)
|
|
210
|
-
#
|
|
211
|
-
|
|
212
|
-
|
|
332
|
+
# Add map attributes
|
|
333
|
+
map_attrs = {
|
|
334
|
+
"sky_tiling_type": SkyTilingType.RECTANGULAR.value,
|
|
335
|
+
"Spacing_degrees": str(IDEX_SPACING_DEG),
|
|
336
|
+
"Spice_reference_frame": IDEX_EVENT_REFERENCE_FRAME.name,
|
|
337
|
+
} | idex_l2c_attrs.get_global_attributes("imap_idex_l2c_sci-rectangular")
|
|
213
338
|
|
|
214
|
-
|
|
339
|
+
l2c_dataset.attrs.update(map_attrs)
|
|
215
340
|
|
|
216
|
-
|
|
341
|
+
logger.info("IDEX L2B and L2C science data processing completed.")
|
|
342
|
+
|
|
343
|
+
return [l2b_dataset, l2c_dataset]
|
|
217
344
|
|
|
218
345
|
|
|
219
346
|
def compute_counts_by_charge_and_mass(
|
|
220
347
|
l2a_dataset: xr.Dataset, epoch_doy_unique: np.ndarray
|
|
221
|
-
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
348
|
+
) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
|
|
222
349
|
"""
|
|
223
|
-
Compute the dust
|
|
350
|
+
Compute the dust counts by charge and mass by spin phase or lon and lat per day.
|
|
224
351
|
|
|
225
352
|
Parameters
|
|
226
353
|
----------
|
|
@@ -231,20 +358,17 @@ def compute_counts_by_charge_and_mass(
|
|
|
231
358
|
|
|
232
359
|
Returns
|
|
233
360
|
-------
|
|
234
|
-
tuple[np.ndarray, np.ndarray, np.ndarray]
|
|
361
|
+
tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]
|
|
235
362
|
Two 3D arrays containing counts by charge or mass, and by spin phase for each
|
|
236
|
-
dataset,
|
|
363
|
+
dataset, Two 4D arrays containing counts by charge or mass, and by lon and lat
|
|
364
|
+
for each dataset, and a 1D array of daily epoch values.
|
|
237
365
|
"""
|
|
238
|
-
# Initialize
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
)
|
|
244
|
-
counts_by_mass = np.zeros(
|
|
245
|
-
(len(epoch_doy_unique), len(MASS_BIN_EDGES), len(SPIN_PHASE_BIN_EDGES) - 1),
|
|
246
|
-
)
|
|
247
|
-
daily_epoch = np.zeros(len(epoch_doy_unique))
|
|
366
|
+
# Initialize lists to hold counts.
|
|
367
|
+
counts_by_charge = []
|
|
368
|
+
counts_by_mass = []
|
|
369
|
+
counts_by_charge_map = []
|
|
370
|
+
counts_by_mass_map = []
|
|
371
|
+
daily_epoch = np.zeros(len(epoch_doy_unique), dtype=np.float64)
|
|
248
372
|
for i in range(len(epoch_doy_unique)):
|
|
249
373
|
doy = epoch_doy_unique[i]
|
|
250
374
|
# Get the indices for the current day
|
|
@@ -258,39 +382,89 @@ def compute_counts_by_charge_and_mass(
|
|
|
258
382
|
]
|
|
259
383
|
charge_vals = l2a_dataset["target_low_impact_charge"].data[current_day_indices]
|
|
260
384
|
spin_phase_angles = l2a_dataset["spin_phase"].data[current_day_indices]
|
|
385
|
+
# Make sure longitude values are in the range [0, 360)
|
|
386
|
+
longitude = np.mod(l2a_dataset["longitude"].data[current_day_indices], 360)
|
|
387
|
+
latitude = l2a_dataset["latitude"].data[current_day_indices]
|
|
261
388
|
# Convert units
|
|
262
|
-
mass_vals = FG_TO_KG * np.
|
|
263
|
-
# Bin masses
|
|
264
|
-
binned_mass = np.array(np.digitize(mass_vals, bins=MASS_BIN_EDGES))
|
|
265
|
-
# Bin charges
|
|
266
|
-
binned_charge = np.array(np.digitize(charge_vals, bins=CHARGE_BIN_EDGES))
|
|
389
|
+
mass_vals = FG_TO_KG * np.atleast_1d(mass_vals)
|
|
267
390
|
# Bin spin phases
|
|
268
391
|
binned_spin_phase = bin_spin_phases(spin_phase_angles)
|
|
269
|
-
#
|
|
270
|
-
#
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
392
|
+
# Clip arrays to ensure that the values are within the valid range of bins.
|
|
393
|
+
# Latitude should be binned with the right edge included. 90 is a valid latitude
|
|
394
|
+
latitude = np.clip(latitude, -90, 90)
|
|
395
|
+
mass_vals = np.clip(mass_vals, MASS_BIN_EDGES[0], MASS_BIN_EDGES[-1])
|
|
396
|
+
charge_vals = np.clip(charge_vals, CHARGE_BIN_EDGES[0], CHARGE_BIN_EDGES[-1])
|
|
397
|
+
|
|
398
|
+
counts_by_mass.append(
|
|
399
|
+
np.histogramdd(
|
|
400
|
+
np.column_stack([mass_vals, binned_spin_phase]),
|
|
401
|
+
bins=[MASS_BIN_EDGES, np.arange(5)],
|
|
402
|
+
)[0]
|
|
274
403
|
)
|
|
275
|
-
|
|
404
|
+
counts_by_charge.append(
|
|
405
|
+
np.histogramdd(
|
|
406
|
+
np.column_stack([charge_vals, binned_spin_phase]),
|
|
407
|
+
bins=[CHARGE_BIN_EDGES, np.arange(5)],
|
|
408
|
+
)[0]
|
|
409
|
+
)
|
|
410
|
+
counts_by_mass_map.append(
|
|
411
|
+
np.histogramdd(
|
|
412
|
+
np.column_stack([mass_vals, longitude, latitude]),
|
|
413
|
+
bins=[MASS_BIN_EDGES, LON_BINS_EDGES, LAT_BINS_EDGES],
|
|
414
|
+
)[0]
|
|
415
|
+
)
|
|
416
|
+
counts_by_charge_map.append(
|
|
417
|
+
np.histogramdd(
|
|
418
|
+
np.column_stack([charge_vals, longitude, latitude]),
|
|
419
|
+
bins=[CHARGE_BIN_EDGES, LON_BINS_EDGES, LAT_BINS_EDGES],
|
|
420
|
+
)[0]
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
return (
|
|
424
|
+
np.stack(counts_by_charge),
|
|
425
|
+
np.stack(counts_by_mass),
|
|
426
|
+
np.stack(counts_by_charge_map),
|
|
427
|
+
np.stack(counts_by_mass_map),
|
|
428
|
+
daily_epoch,
|
|
429
|
+
)
|
|
276
430
|
|
|
277
|
-
# TODO use np.histogramdd to compute the counts by charge and mass.
|
|
278
|
-
# Count dust events for each spin phase and mass bin or charge bin.
|
|
279
|
-
for mass_bin, charge_bin, spin_phase_bin in zip(
|
|
280
|
-
binned_mass, binned_charge, binned_spin_phase
|
|
281
|
-
):
|
|
282
|
-
counts_by_mass[i, mass_bin, spin_phase_bin] += 1
|
|
283
|
-
counts_by_charge[i, charge_bin, spin_phase_bin] += 1
|
|
284
431
|
|
|
285
|
-
|
|
432
|
+
def compute_rates(
|
|
433
|
+
counts: np.ndarray, epoch_doy_percent_on: np.ndarray, non_zero_inds: np.ndarray
|
|
434
|
+
) -> np.ndarray:
|
|
435
|
+
"""
|
|
436
|
+
Compute the count rates given the percent uptime of IDEX.
|
|
437
|
+
|
|
438
|
+
Parameters
|
|
439
|
+
----------
|
|
440
|
+
counts : np.ndarray
|
|
441
|
+
Count values for the dust events.
|
|
442
|
+
epoch_doy_percent_on : np.ndarray
|
|
443
|
+
Percentage of time science acquisition was on for each day of the year.
|
|
444
|
+
non_zero_inds : np.ndarray
|
|
445
|
+
Indices of the days with non-zero science acquisition percentage.
|
|
446
|
+
|
|
447
|
+
Returns
|
|
448
|
+
-------
|
|
449
|
+
np.ndarray
|
|
450
|
+
Count rates.
|
|
451
|
+
"""
|
|
452
|
+
while len(epoch_doy_percent_on.shape) < len(counts.shape):
|
|
453
|
+
epoch_doy_percent_on = np.expand_dims(epoch_doy_percent_on, axis=-1)
|
|
454
|
+
|
|
455
|
+
return counts[non_zero_inds] / (
|
|
456
|
+
0.01 * epoch_doy_percent_on[non_zero_inds] * SECONDS_IN_DAY
|
|
457
|
+
)
|
|
286
458
|
|
|
287
459
|
|
|
288
460
|
def compute_rates_by_charge_and_mass(
|
|
289
461
|
counts_by_charge: np.ndarray,
|
|
290
462
|
counts_by_mass: np.ndarray,
|
|
463
|
+
counts_by_charge_map: np.ndarray,
|
|
464
|
+
counts_by_mass_map: np.ndarray,
|
|
291
465
|
epoch_doy: np.ndarray,
|
|
292
466
|
daily_on_percentage: dict,
|
|
293
|
-
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
467
|
+
) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
|
|
294
468
|
"""
|
|
295
469
|
Compute the dust event counts rates by charge and mass by spin phase for each day.
|
|
296
470
|
|
|
@@ -299,7 +473,11 @@ def compute_rates_by_charge_and_mass(
|
|
|
299
473
|
counts_by_charge : np.ndarray
|
|
300
474
|
3D array containing counts by charge and spin phase for each dataset.
|
|
301
475
|
counts_by_mass : np.ndarray
|
|
302
|
-
3D array containing counts by mass and
|
|
476
|
+
3D array containing counts by mass and lon and lat for each dataset.
|
|
477
|
+
counts_by_charge_map : np.ndarray
|
|
478
|
+
4D array containing counts by charge and lon and lat for each dataset.
|
|
479
|
+
counts_by_mass_map : np.ndarray
|
|
480
|
+
4D array containing counts by mass and spin phase for each dataset.
|
|
303
481
|
epoch_doy : np.ndarray
|
|
304
482
|
Unique days of year corresponding to the epochs in the dataset.
|
|
305
483
|
daily_on_percentage : dict
|
|
@@ -314,6 +492,8 @@ def compute_rates_by_charge_and_mass(
|
|
|
314
492
|
# Initialize arrays to hold rates.
|
|
315
493
|
rate_by_charge = np.full(counts_by_charge.shape, -1.0)
|
|
316
494
|
rate_by_mass = np.full(counts_by_mass.shape, -1.0)
|
|
495
|
+
rate_by_charge_map = np.full(counts_by_charge_map.shape, -1.0)
|
|
496
|
+
rate_by_mass_map = np.full(counts_by_mass_map.shape, -1.0)
|
|
317
497
|
# Initialize an array to hold quality flags for each epoch. A quality flag of 0
|
|
318
498
|
# indicates that there was no science acquisition data for that epoch, and the rate
|
|
319
499
|
# is not valid. A quality flag of 1 indicates that the rate is valid.
|
|
@@ -336,18 +516,26 @@ def compute_rates_by_charge_and_mass(
|
|
|
336
516
|
# acquisition time.
|
|
337
517
|
non_zero_inds = np.where(epoch_doy_percent_on > 0)[0]
|
|
338
518
|
# Compute rates only for days with non-zero science acquisition percentage
|
|
339
|
-
rate_by_charge[non_zero_inds] =
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
519
|
+
rate_by_charge[non_zero_inds] = compute_rates(
|
|
520
|
+
counts_by_charge, epoch_doy_percent_on, non_zero_inds
|
|
521
|
+
)
|
|
522
|
+
rate_by_mass[non_zero_inds] = compute_rates(
|
|
523
|
+
counts_by_mass, epoch_doy_percent_on, non_zero_inds
|
|
524
|
+
)
|
|
525
|
+
rate_by_charge_map[non_zero_inds] = compute_rates(
|
|
526
|
+
counts_by_charge_map, epoch_doy_percent_on, non_zero_inds
|
|
343
527
|
)
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
* epoch_doy_percent_on[non_zero_inds, np.newaxis, np.newaxis]
|
|
347
|
-
* SECONDS_IN_DAY
|
|
528
|
+
rate_by_mass_map[non_zero_inds] = compute_rates(
|
|
529
|
+
counts_by_mass_map, epoch_doy_percent_on, non_zero_inds
|
|
348
530
|
)
|
|
349
531
|
|
|
350
|
-
return
|
|
532
|
+
return (
|
|
533
|
+
rate_by_charge,
|
|
534
|
+
rate_by_mass,
|
|
535
|
+
rate_by_charge_map,
|
|
536
|
+
rate_by_mass_map,
|
|
537
|
+
rate_quality_flags,
|
|
538
|
+
)
|
|
351
539
|
|
|
352
540
|
|
|
353
541
|
def bin_spin_phases(spin_phases: xr.DataArray) -> np.ndarray:
|
|
@@ -370,7 +558,7 @@ def bin_spin_phases(spin_phases: xr.DataArray) -> np.ndarray:
|
|
|
370
558
|
f"phase angle range, [0, 360)."
|
|
371
559
|
)
|
|
372
560
|
# Shift spin phases by +45° so that the first bin starts at 0°.
|
|
373
|
-
# Use mod to wrap values
|
|
561
|
+
# Use mod to wrap values >= 360 to 0.
|
|
374
562
|
shifted_spin_phases = (spin_phases + 45) % 360
|
|
375
563
|
# Use np.digitize to find the bin index for each spin phase.
|
|
376
564
|
bin_indices = np.digitize(shifted_spin_phases, SPIN_PHASE_BIN_EDGES, right=False)
|
|
@@ -419,7 +607,7 @@ def get_science_acquisition_timestamps(
|
|
|
419
607
|
epochs = evt_dataset["epoch"][sc_indices].data
|
|
420
608
|
# Now the state change values and check if it is either a science
|
|
421
609
|
# acquisition start or science acquisition stop event.
|
|
422
|
-
for v1, v2, epoch in zip(val1, val2, epochs):
|
|
610
|
+
for v1, v2, epoch in zip(val1, val2, epochs, strict=False):
|
|
423
611
|
# An "acquire" start will have val1=ACQSETUP and val2=ACQ
|
|
424
612
|
# An "acquire" stop will have val1=ACQ and val2=CHILL
|
|
425
613
|
if (v1, v2) == (IDEXEvtAcquireCodes.ACQSETUP, IDEXEvtAcquireCodes.ACQ):
|
|
@@ -458,6 +646,12 @@ def get_science_acquisition_on_percentage(evt_dataset: xr.Dataset) -> dict:
|
|
|
458
646
|
"""
|
|
459
647
|
# Get science acquisition start and stop times
|
|
460
648
|
evt_logs, evt_time, evt_values = get_science_acquisition_timestamps(evt_dataset)
|
|
649
|
+
if len(evt_time) == 0:
|
|
650
|
+
logger.warning(
|
|
651
|
+
"No science acquisition events found in event dataset. Returning empty "
|
|
652
|
+
"uptime percentages. All rate variables will be set to -1."
|
|
653
|
+
)
|
|
654
|
+
return {}
|
|
461
655
|
# Track total and 'on' durations per day
|
|
462
656
|
daily_totals: collections.defaultdict = defaultdict(timedelta)
|
|
463
657
|
daily_on: collections.defaultdict = defaultdict(timedelta)
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
"""Contains helper functions to support IDEX processing."""
|
|
2
2
|
|
|
3
|
-
from typing import Optional
|
|
4
|
-
|
|
5
3
|
import xarray as xr
|
|
6
4
|
|
|
7
5
|
from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
|
|
@@ -31,7 +29,7 @@ def setup_dataset(
|
|
|
31
29
|
dataset: xr.Dataset,
|
|
32
30
|
match_strings: list,
|
|
33
31
|
idex_attrs: ImapCdfAttributes,
|
|
34
|
-
data_vars:
|
|
32
|
+
data_vars: dict | None = None,
|
|
35
33
|
) -> xr.Dataset:
|
|
36
34
|
"""
|
|
37
35
|
Initialize a dataset and copy over any dataArrays.
|
imap_processing/lo/l0/lo_apid.py
CHANGED