imap-processing 1.0.0__py3-none-any.whl → 1.0.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.
- imap_processing/_version.py +2 -2
- imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +13 -1
- imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +97 -254
- imap_processing/cdf/config/imap_codice_l2-hi-omni_variable_attrs.yaml +635 -0
- imap_processing/cdf/config/imap_codice_l2-hi-sectored_variable_attrs.yaml +422 -0
- imap_processing/cdf/config/imap_enamaps_l2-common_variable_attrs.yaml +29 -22
- imap_processing/cdf/config/imap_enamaps_l2-healpix_variable_attrs.yaml +2 -0
- imap_processing/cdf/config/imap_enamaps_l2-rectangular_variable_attrs.yaml +12 -2
- imap_processing/cdf/config/imap_swapi_variable_attrs.yaml +2 -13
- imap_processing/cdf/utils.py +2 -2
- imap_processing/cli.py +10 -27
- imap_processing/codice/codice_l1a_lo_angular.py +362 -0
- imap_processing/codice/codice_l1a_lo_species.py +282 -0
- imap_processing/codice/codice_l1b.py +62 -97
- imap_processing/codice/codice_l2.py +801 -174
- imap_processing/codice/codice_new_l1a.py +64 -0
- imap_processing/codice/constants.py +96 -0
- imap_processing/codice/utils.py +270 -0
- imap_processing/ena_maps/ena_maps.py +157 -95
- imap_processing/ena_maps/utils/coordinates.py +5 -0
- imap_processing/ena_maps/utils/corrections.py +450 -0
- imap_processing/ena_maps/utils/map_utils.py +143 -42
- imap_processing/ena_maps/utils/naming.py +3 -1
- imap_processing/hi/hi_l1c.py +34 -12
- imap_processing/hi/hi_l2.py +82 -44
- imap_processing/ialirt/constants.py +7 -1
- imap_processing/ialirt/generate_coverage.py +3 -1
- imap_processing/ialirt/l0/parse_mag.py +1 -0
- imap_processing/ialirt/l0/process_codice.py +66 -0
- imap_processing/ialirt/l0/process_hit.py +1 -0
- imap_processing/ialirt/l0/process_swapi.py +1 -0
- imap_processing/ialirt/l0/process_swe.py +2 -0
- imap_processing/ialirt/process_ephemeris.py +6 -2
- imap_processing/ialirt/utils/create_xarray.py +4 -2
- imap_processing/idex/idex_l2a.py +2 -2
- imap_processing/idex/idex_l2b.py +1 -1
- imap_processing/lo/l1c/lo_l1c.py +62 -4
- imap_processing/lo/l2/lo_l2.py +85 -15
- imap_processing/mag/l1a/mag_l1a.py +2 -2
- imap_processing/mag/l1a/mag_l1a_data.py +71 -13
- imap_processing/mag/l1c/interpolation_methods.py +34 -13
- imap_processing/mag/l1c/mag_l1c.py +117 -67
- imap_processing/mag/l1d/mag_l1d_data.py +3 -1
- imap_processing/quality_flags.py +1 -0
- imap_processing/spice/geometry.py +11 -9
- imap_processing/spice/pointing_frame.py +77 -50
- imap_processing/swapi/constants.py +4 -0
- imap_processing/swapi/l1/swapi_l1.py +59 -24
- imap_processing/swapi/l2/swapi_l2.py +17 -3
- imap_processing/swe/utils/swe_constants.py +7 -7
- imap_processing/ultra/l1a/ultra_l1a.py +121 -72
- imap_processing/ultra/l1b/de.py +57 -1
- imap_processing/ultra/l1b/extendedspin.py +1 -1
- imap_processing/ultra/l1b/ultra_l1b_annotated.py +0 -1
- imap_processing/ultra/l1b/ultra_l1b_culling.py +2 -2
- imap_processing/ultra/l1b/ultra_l1b_extended.py +25 -12
- imap_processing/ultra/l1c/helio_pset.py +29 -6
- imap_processing/ultra/l1c/l1c_lookup_utils.py +4 -2
- imap_processing/ultra/l1c/spacecraft_pset.py +10 -6
- imap_processing/ultra/l1c/ultra_l1c.py +6 -6
- imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +82 -20
- imap_processing/ultra/l2/ultra_l2.py +2 -2
- imap_processing-1.0.2.dist-info/METADATA +121 -0
- {imap_processing-1.0.0.dist-info → imap_processing-1.0.2.dist-info}/RECORD +67 -61
- imap_processing-1.0.0.dist-info/METADATA +0 -120
- {imap_processing-1.0.0.dist-info → imap_processing-1.0.2.dist-info}/LICENSE +0 -0
- {imap_processing-1.0.0.dist-info → imap_processing-1.0.2.dist-info}/WHEEL +0 -0
- {imap_processing-1.0.0.dist-info → imap_processing-1.0.2.dist-info}/entry_points.txt +0 -0
|
@@ -14,7 +14,7 @@ from scipy.interpolate import LinearNDInterpolator, RegularGridInterpolator
|
|
|
14
14
|
|
|
15
15
|
from imap_processing.quality_flags import ImapDEOutliersUltraFlags
|
|
16
16
|
from imap_processing.spice.spin import get_spin_data
|
|
17
|
-
from imap_processing.spice.time import
|
|
17
|
+
from imap_processing.spice.time import met_to_ttj2000ns, ttj2000ns_to_et
|
|
18
18
|
from imap_processing.ultra.constants import UltraConstants
|
|
19
19
|
from imap_processing.ultra.l1b.lookup_utils import (
|
|
20
20
|
get_angular_profiles,
|
|
@@ -592,7 +592,7 @@ def get_ssd_tof(
|
|
|
592
592
|
tof : np.ndarray
|
|
593
593
|
Time of flight (tenths of a nanosecond).
|
|
594
594
|
"""
|
|
595
|
-
_, tof_offset,
|
|
595
|
+
_, tof_offset, _ssd_number = get_ssd_back_position_and_tof_offset(
|
|
596
596
|
de_dataset, sensor, ancillary_files
|
|
597
597
|
)
|
|
598
598
|
indices = np.nonzero(np.isin(de_dataset["stop_type"], [StopType.SSD.value]))[0]
|
|
@@ -711,7 +711,7 @@ def get_energy_pulse_height(
|
|
|
711
711
|
ylut[indices_bottom] = (yb[indices_bottom] / 100 + 82 / 2) * 32 / 82 # mm
|
|
712
712
|
|
|
713
713
|
ph_correction_top, updated_flags_top = get_ph_corrected(
|
|
714
|
-
|
|
714
|
+
sensor,
|
|
715
715
|
"tp",
|
|
716
716
|
ancillary_files,
|
|
717
717
|
np.round(xlut[indices_top]),
|
|
@@ -720,7 +720,7 @@ def get_energy_pulse_height(
|
|
|
720
720
|
)
|
|
721
721
|
quality_flags[indices_top] = updated_flags_top
|
|
722
722
|
ph_correction_bottom, updated_flags_bottom = get_ph_corrected(
|
|
723
|
-
|
|
723
|
+
sensor,
|
|
724
724
|
"bt",
|
|
725
725
|
ancillary_files,
|
|
726
726
|
np.round(xlut[indices_bottom]),
|
|
@@ -996,6 +996,7 @@ def get_eventtimes(
|
|
|
996
996
|
t_spin_period_sec * phase_angle/720
|
|
997
997
|
"""
|
|
998
998
|
spin_df = get_spin_data()
|
|
999
|
+
|
|
999
1000
|
index = np.searchsorted(spin_df["spin_number"].values, spin)
|
|
1000
1001
|
spin_starts = (
|
|
1001
1002
|
spin_df["spin_start_sec_sclk"].values[index]
|
|
@@ -1003,10 +1004,13 @@ def get_eventtimes(
|
|
|
1003
1004
|
)
|
|
1004
1005
|
|
|
1005
1006
|
spin_period_sec = spin_df["spin_period_sec"].values[index]
|
|
1006
|
-
|
|
1007
1007
|
event_times = spin_starts + spin_period_sec * (phase_angle / 720)
|
|
1008
1008
|
|
|
1009
|
-
return
|
|
1009
|
+
return (
|
|
1010
|
+
ttj2000ns_to_et(met_to_ttj2000ns(event_times)),
|
|
1011
|
+
ttj2000ns_to_et(met_to_ttj2000ns(spin_starts)),
|
|
1012
|
+
spin_period_sec,
|
|
1013
|
+
)
|
|
1010
1014
|
|
|
1011
1015
|
|
|
1012
1016
|
def interpolate_fwhm(
|
|
@@ -1108,7 +1112,9 @@ def get_fwhm(
|
|
|
1108
1112
|
return phi_interp, theta_interp
|
|
1109
1113
|
|
|
1110
1114
|
|
|
1111
|
-
def get_efficiency_interpolator(
|
|
1115
|
+
def get_efficiency_interpolator(
|
|
1116
|
+
ancillary_files: dict,
|
|
1117
|
+
) -> tuple[RegularGridInterpolator, tuple, tuple]:
|
|
1112
1118
|
"""
|
|
1113
1119
|
Return a callable function that interpolates efficiency values for each event.
|
|
1114
1120
|
|
|
@@ -1119,8 +1125,12 @@ def get_efficiency_interpolator(ancillary_files: dict) -> RegularGridInterpolato
|
|
|
1119
1125
|
|
|
1120
1126
|
Returns
|
|
1121
1127
|
-------
|
|
1122
|
-
|
|
1123
|
-
|
|
1128
|
+
interpolator : RegularGridInterpolator
|
|
1129
|
+
Callable function to interpolate efficiency values.
|
|
1130
|
+
theta_min_max : tuple
|
|
1131
|
+
Minimum and maximum theta values in the lookup table.
|
|
1132
|
+
phi_min_max : tuple
|
|
1133
|
+
Minimum and maximum phi values in the lookup table.
|
|
1124
1134
|
"""
|
|
1125
1135
|
lookup_table = get_energy_efficiencies(ancillary_files)
|
|
1126
1136
|
|
|
@@ -1133,6 +1143,9 @@ def get_efficiency_interpolator(ancillary_files: dict) -> RegularGridInterpolato
|
|
|
1133
1143
|
efficiency_grid = efficiency_2d.reshape(
|
|
1134
1144
|
(len(theta_vals), len(phi_vals), len(energy_vals))
|
|
1135
1145
|
)
|
|
1146
|
+
# Find the min and max values for theta and phi
|
|
1147
|
+
theta_min_max = (theta_vals.min(), theta_vals.max())
|
|
1148
|
+
phi_min_max = (phi_vals.min(), phi_vals.max())
|
|
1136
1149
|
|
|
1137
1150
|
interpolator = RegularGridInterpolator(
|
|
1138
1151
|
(theta_vals, phi_vals, energy_vals),
|
|
@@ -1141,7 +1154,7 @@ def get_efficiency_interpolator(ancillary_files: dict) -> RegularGridInterpolato
|
|
|
1141
1154
|
fill_value=FILLVAL_FLOAT32,
|
|
1142
1155
|
)
|
|
1143
1156
|
|
|
1144
|
-
return interpolator
|
|
1157
|
+
return interpolator, theta_min_max, phi_min_max
|
|
1145
1158
|
|
|
1146
1159
|
|
|
1147
1160
|
def get_efficiency(
|
|
@@ -1174,7 +1187,7 @@ def get_efficiency(
|
|
|
1174
1187
|
Interpolated efficiency values.
|
|
1175
1188
|
"""
|
|
1176
1189
|
if not interpolator:
|
|
1177
|
-
interpolator = get_efficiency_interpolator(ancillary_files)
|
|
1190
|
+
interpolator, _, _ = get_efficiency_interpolator(ancillary_files)
|
|
1178
1191
|
|
|
1179
1192
|
return interpolator((theta_inst, phi_inst, energy))
|
|
1180
1193
|
|
|
@@ -1343,7 +1356,7 @@ def is_back_tof_valid(
|
|
|
1343
1356
|
From page 33 of the IMAP-Ultra Flight Software Specification document.
|
|
1344
1357
|
"""
|
|
1345
1358
|
_, _, _, _, tofx, tofy = get_ph_tof_and_back_positions(
|
|
1346
|
-
de_dataset, xf,
|
|
1359
|
+
de_dataset, xf, sensor, ancillary_files
|
|
1347
1360
|
)
|
|
1348
1361
|
diff = tofy - tofx
|
|
1349
1362
|
|
|
@@ -6,6 +6,7 @@ import astropy_healpix.healpy as hp
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
import xarray as xr
|
|
8
8
|
|
|
9
|
+
from imap_processing.cdf.utils import parse_filename_like
|
|
9
10
|
from imap_processing.quality_flags import ImapPSETUltraFlags
|
|
10
11
|
from imap_processing.spice.repoint import get_pointing_times
|
|
11
12
|
from imap_processing.spice.time import (
|
|
@@ -24,6 +25,7 @@ from imap_processing.ultra.l1c.ultra_l1c_pset_bins import (
|
|
|
24
25
|
get_efficiencies_and_geometric_function,
|
|
25
26
|
get_energy_delta_minus_plus,
|
|
26
27
|
get_helio_adjusted_data,
|
|
28
|
+
get_spacecraft_background_rates,
|
|
27
29
|
get_spacecraft_exposure_times,
|
|
28
30
|
get_spacecraft_histogram,
|
|
29
31
|
)
|
|
@@ -69,9 +71,14 @@ def calculate_helio_pset(
|
|
|
69
71
|
dataset : xarray.Dataset
|
|
70
72
|
Dataset containing the data.
|
|
71
73
|
"""
|
|
74
|
+
sensor = parse_filename_like(name)["sensor"][0:2]
|
|
72
75
|
pset_dict: dict[str, np.ndarray] = {}
|
|
73
76
|
# Select only the species we are interested in.
|
|
74
77
|
indices = np.where(np.isin(de_dataset["ebin"].values, species_id))[0]
|
|
78
|
+
if indices.size == 0:
|
|
79
|
+
logger.info(f"No data available for {name}")
|
|
80
|
+
return None
|
|
81
|
+
|
|
75
82
|
species_dataset = de_dataset.isel(epoch=indices)
|
|
76
83
|
|
|
77
84
|
rejected = get_de_rejection_mask(
|
|
@@ -120,17 +127,23 @@ def calculate_helio_pset(
|
|
|
120
127
|
)
|
|
121
128
|
healpix = np.arange(n_pix)
|
|
122
129
|
|
|
130
|
+
# Get midpoint timestamp for pointing.
|
|
131
|
+
pointing_start, pointing_stop = get_pointing_times(
|
|
132
|
+
et_to_met(species_dataset["event_times"].data[0])
|
|
133
|
+
)
|
|
123
134
|
logger.info("Calculating spacecraft exposure times with deadtime correction.")
|
|
124
135
|
exposure_time, deadtime_ratios = get_spacecraft_exposure_times(
|
|
125
136
|
rates_dataset,
|
|
126
137
|
params_dataset,
|
|
127
138
|
pixels_below_scattering,
|
|
128
139
|
boundary_scale_factors,
|
|
140
|
+
pointing_start,
|
|
141
|
+
pointing_stop,
|
|
129
142
|
n_pix=n_pix,
|
|
130
143
|
)
|
|
131
144
|
logger.info("Calculating spun efficiencies and geometric function.")
|
|
132
145
|
# calculate efficiency and geometric function as a function of energy
|
|
133
|
-
|
|
146
|
+
geometric_function, efficiencies = get_efficiencies_and_geometric_function(
|
|
134
147
|
pixels_below_scattering,
|
|
135
148
|
boundary_scale_factors,
|
|
136
149
|
theta_vals,
|
|
@@ -138,14 +151,23 @@ def calculate_helio_pset(
|
|
|
138
151
|
n_pix,
|
|
139
152
|
ancillary_files,
|
|
140
153
|
)
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
154
|
+
|
|
155
|
+
logger.info("Calculating background rates.")
|
|
156
|
+
# TODO calculate helio background rates
|
|
157
|
+
# Calculate background rates
|
|
158
|
+
background_rates = get_spacecraft_background_rates(
|
|
159
|
+
rates_dataset,
|
|
160
|
+
sensor,
|
|
161
|
+
ancillary_files,
|
|
162
|
+
intervals,
|
|
163
|
+
goodtimes_dataset["spin_number"].values,
|
|
164
|
+
nside=nside,
|
|
144
165
|
)
|
|
166
|
+
|
|
145
167
|
mid_time = ttj2000ns_to_et(met_to_ttj2000ns((pointing_start + pointing_stop) / 2))
|
|
146
168
|
|
|
147
169
|
logger.info("Adjusting data for helio frame.")
|
|
148
|
-
exposure_time,
|
|
170
|
+
exposure_time, _efficiency, geometric_function = get_helio_adjusted_data(
|
|
149
171
|
mid_time,
|
|
150
172
|
exposure_time,
|
|
151
173
|
geometric_function,
|
|
@@ -176,7 +198,8 @@ def calculate_helio_pset(
|
|
|
176
198
|
pset_dict["latitude"] = latitude[np.newaxis, ...]
|
|
177
199
|
pset_dict["longitude"] = longitude[np.newaxis, ...]
|
|
178
200
|
pset_dict["energy_bin_geometric_mean"] = energy_bin_geometric_means
|
|
179
|
-
pset_dict["
|
|
201
|
+
pset_dict["background_rates"] = background_rates[np.newaxis, ...]
|
|
202
|
+
pset_dict["exposure_factor"] = exposure_time[np.newaxis, ...]
|
|
180
203
|
pset_dict["pixel_index"] = healpix
|
|
181
204
|
pset_dict["energy_bin_delta"] = np.diff(intervals, axis=1).squeeze()[
|
|
182
205
|
np.newaxis, ...
|
|
@@ -174,8 +174,10 @@ def calculate_fwhm_spun_scattering(
|
|
|
174
174
|
fwhm_phi_sum[:, for_inds] += fwhm_phi.T
|
|
175
175
|
sample_count[:, for_inds] += 1
|
|
176
176
|
|
|
177
|
-
fwhm_phi_avg = np.
|
|
178
|
-
fwhm_theta_avg = np.
|
|
177
|
+
fwhm_phi_avg = np.zeros_like(fwhm_phi_sum)
|
|
178
|
+
fwhm_theta_avg = np.zeros_like(fwhm_theta_sum)
|
|
179
|
+
np.divide(fwhm_phi_sum, sample_count, out=fwhm_phi_avg, where=sample_count != 0)
|
|
180
|
+
np.divide(fwhm_theta_sum, sample_count, out=fwhm_theta_avg, where=sample_count != 0)
|
|
179
181
|
return (
|
|
180
182
|
pixels_below_scattering,
|
|
181
183
|
fwhm_theta_avg,
|
|
@@ -77,6 +77,7 @@ def calculate_spacecraft_pset(
|
|
|
77
77
|
|
|
78
78
|
# If there are no species return None.
|
|
79
79
|
if indices.size == 0:
|
|
80
|
+
logger.info(f"No data available for {name}")
|
|
80
81
|
return None
|
|
81
82
|
|
|
82
83
|
# Before we use the de_dataset to calculate the pointing set grid we need to filter.
|
|
@@ -100,7 +101,7 @@ def calculate_spacecraft_pset(
|
|
|
100
101
|
for_indices_by_spin_phase,
|
|
101
102
|
theta_vals,
|
|
102
103
|
phi_vals,
|
|
103
|
-
|
|
104
|
+
_ra_and_dec,
|
|
104
105
|
boundary_scale_factors,
|
|
105
106
|
) = get_spacecraft_pointing_lookup_tables(ancillary_files, instrument_id)
|
|
106
107
|
|
|
@@ -126,7 +127,7 @@ def calculate_spacecraft_pset(
|
|
|
126
127
|
|
|
127
128
|
logger.info("Calculating spun efficiencies and geometric function.")
|
|
128
129
|
# calculate efficiency and geometric function as a function of energy
|
|
129
|
-
|
|
130
|
+
geometric_function, efficiencies = get_efficiencies_and_geometric_function(
|
|
130
131
|
pixels_below_scattering,
|
|
131
132
|
boundary_scale_factors,
|
|
132
133
|
theta_vals,
|
|
@@ -136,6 +137,10 @@ def calculate_spacecraft_pset(
|
|
|
136
137
|
)
|
|
137
138
|
sensitivity = efficiencies * geometric_function
|
|
138
139
|
|
|
140
|
+
# Get the start and stop times of the pointing period
|
|
141
|
+
pointing_start, pointing_stop = get_pointing_times(
|
|
142
|
+
float(et_to_met(species_dataset["event_times"].data[0]))
|
|
143
|
+
)
|
|
139
144
|
# Calculate exposure times
|
|
140
145
|
logger.info("Calculating spacecraft exposure times with deadtime correction.")
|
|
141
146
|
exposure_pointing, deadtime_ratios = get_spacecraft_exposure_times(
|
|
@@ -143,6 +148,8 @@ def calculate_spacecraft_pset(
|
|
|
143
148
|
params_dataset,
|
|
144
149
|
pixels_below_scattering,
|
|
145
150
|
boundary_scale_factors,
|
|
151
|
+
pointing_start,
|
|
152
|
+
pointing_stop,
|
|
146
153
|
n_pix=n_pix,
|
|
147
154
|
)
|
|
148
155
|
logger.info("Calculating background rates.")
|
|
@@ -172,10 +179,7 @@ def calculate_spacecraft_pset(
|
|
|
172
179
|
spacecraft_pset_quality_flags,
|
|
173
180
|
nside=nside,
|
|
174
181
|
)
|
|
175
|
-
#
|
|
176
|
-
pointing_start, pointing_stop = get_pointing_times(
|
|
177
|
-
float(et_to_met(species_dataset["event_times"].data[0]))
|
|
178
|
-
)
|
|
182
|
+
# Convert pointing start time to ttj2000ns
|
|
179
183
|
pointing_start = met_to_ttj2000ns(pointing_start)
|
|
180
184
|
# Epoch should be the start of the pointing
|
|
181
185
|
pset_dict["epoch"] = np.atleast_1d(pointing_start).astype(np.int64)
|
|
@@ -8,7 +8,7 @@ from imap_processing.ultra.l1c.spacecraft_pset import calculate_spacecraft_pset
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def ultra_l1c(
|
|
11
|
-
data_dict: dict, ancillary_files: dict,
|
|
11
|
+
data_dict: dict, ancillary_files: dict, descriptor: str
|
|
12
12
|
) -> list[xr.Dataset]:
|
|
13
13
|
"""
|
|
14
14
|
Will process ULTRA L1A and L1B data into L1C CDF files at output_filepath.
|
|
@@ -19,8 +19,8 @@ def ultra_l1c(
|
|
|
19
19
|
The data itself and its dependent data.
|
|
20
20
|
ancillary_files : dict
|
|
21
21
|
Ancillary files.
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
descriptor : str
|
|
23
|
+
Job descriptor.
|
|
24
24
|
|
|
25
25
|
Returns
|
|
26
26
|
-------
|
|
@@ -28,15 +28,15 @@ def ultra_l1c(
|
|
|
28
28
|
List of xarray.Dataset.
|
|
29
29
|
"""
|
|
30
30
|
output_datasets = []
|
|
31
|
-
|
|
32
|
-
# Account for possibility of having 45 and 90 in dictionary.
|
|
31
|
+
create_helio_pset = True if "helio" in descriptor else False
|
|
32
|
+
# Account for the possibility of having 45 and 90 in the dictionary.
|
|
33
33
|
for instrument_id in [45, 90]:
|
|
34
34
|
if (
|
|
35
35
|
f"imap_ultra_l1b_{instrument_id}sensor-goodtimes" in data_dict
|
|
36
36
|
and f"imap_ultra_l1b_{instrument_id}sensor-de" in data_dict
|
|
37
37
|
and f"imap_ultra_l1a_{instrument_id}sensor-rates" in data_dict
|
|
38
38
|
and f"imap_ultra_l1a_{instrument_id}sensor-params" in data_dict
|
|
39
|
-
and
|
|
39
|
+
and create_helio_pset
|
|
40
40
|
):
|
|
41
41
|
helio_pset = calculate_helio_pset(
|
|
42
42
|
data_dict[f"imap_ultra_l1b_{instrument_id}sensor-de"],
|
|
@@ -13,7 +13,11 @@ from imap_processing.spice.geometry import (
|
|
|
13
13
|
cartesian_to_spherical,
|
|
14
14
|
imap_state,
|
|
15
15
|
)
|
|
16
|
-
from imap_processing.spice.spin import
|
|
16
|
+
from imap_processing.spice.spin import (
|
|
17
|
+
get_spacecraft_spin_phase,
|
|
18
|
+
get_spin_angle,
|
|
19
|
+
get_spin_data,
|
|
20
|
+
)
|
|
17
21
|
from imap_processing.spice.time import ttj2000ns_to_met
|
|
18
22
|
from imap_processing.ultra.constants import UltraConstants
|
|
19
23
|
from imap_processing.ultra.l1b.lookup_utils import (
|
|
@@ -381,7 +385,7 @@ def calculate_exposure_time(
|
|
|
381
385
|
# Get energy bin geometric means
|
|
382
386
|
energy_bin_geometric_means = build_energy_bins()[2]
|
|
383
387
|
# Exposure time should now be of shape (energy, npix)
|
|
384
|
-
|
|
388
|
+
counts = np.zeros((len(energy_bin_geometric_means), n_pix))
|
|
385
389
|
# nominal spin phase step.
|
|
386
390
|
nominal_ms_step = 15 / len(pixels_below_scattering) # time step
|
|
387
391
|
# Query the dead-time ratio and apply the nominal exposure time to pixels in the FOR
|
|
@@ -396,12 +400,13 @@ def calculate_exposure_time(
|
|
|
396
400
|
continue
|
|
397
401
|
# Apply the nominal exposure time (1 ms) scaled by the deadtime ratio to
|
|
398
402
|
# every pixel in the FOR, that is below the FWHM scattering threshold,
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
* deadtime_ratios[i]
|
|
403
|
+
counts[energy_bin_idx, pixels_at_energy_and_spin] += (
|
|
404
|
+
deadtime_ratios[i]
|
|
402
405
|
* boundary_scale_factors[pixels_at_energy_and_spin, i]
|
|
403
406
|
)
|
|
404
407
|
|
|
408
|
+
# Multiply by the nominal spin step to get the exposure time in ms
|
|
409
|
+
exposure_pointing = counts * nominal_ms_step
|
|
405
410
|
return exposure_pointing
|
|
406
411
|
|
|
407
412
|
|
|
@@ -410,6 +415,8 @@ def get_spacecraft_exposure_times(
|
|
|
410
415
|
params_dataset: xr.Dataset,
|
|
411
416
|
pixels_below_scattering: list[list],
|
|
412
417
|
boundary_scale_factors: NDArray,
|
|
418
|
+
pointing_start_met: float,
|
|
419
|
+
pointing_stop_met: float,
|
|
413
420
|
n_pix: int,
|
|
414
421
|
) -> tuple[NDArray, NDArray]:
|
|
415
422
|
"""
|
|
@@ -428,6 +435,10 @@ def get_spacecraft_exposure_times(
|
|
|
428
435
|
below the FWHM scattering threshold.
|
|
429
436
|
boundary_scale_factors : np.ndarray
|
|
430
437
|
Boundary scale factors for each pixel at each spin phase.
|
|
438
|
+
pointing_start_met : float
|
|
439
|
+
Start time of the pointing period in mission elapsed time.
|
|
440
|
+
pointing_stop_met : float
|
|
441
|
+
Stop time of the pointing period in mission elapsed time.
|
|
431
442
|
n_pix : int
|
|
432
443
|
Number of HEALPix pixels.
|
|
433
444
|
|
|
@@ -440,13 +451,36 @@ def get_spacecraft_exposure_times(
|
|
|
440
451
|
nominal_deadtime_ratios : np.ndarray
|
|
441
452
|
Deadtime ratios at each spin phase step (1ms res).
|
|
442
453
|
"""
|
|
443
|
-
# TODO: use the universal spin table and
|
|
444
|
-
# universal pointing table here to determine actual number of spins
|
|
445
454
|
sectored_rates = get_sectored_rates(rates_dataset, params_dataset)
|
|
446
455
|
nominal_deadtime_ratios = get_deadtime_ratios_by_spin_phase(sectored_rates)
|
|
447
|
-
|
|
456
|
+
# The exposure time will be approximately the same per spin, so to save
|
|
457
|
+
# computation time, calculate the exposure time for a single spin and then scale it
|
|
458
|
+
# by the number of spins in the pointing. For more information, see section 3.4.3
|
|
459
|
+
# of the Ultra Algorithm Document.
|
|
460
|
+
exposure_time = calculate_exposure_time(
|
|
448
461
|
nominal_deadtime_ratios, pixels_below_scattering, boundary_scale_factors, n_pix
|
|
449
462
|
)
|
|
463
|
+
# Use the universal spin table to determine the actual number of spins
|
|
464
|
+
nominal_spin_seconds = 15.0
|
|
465
|
+
spin_data = get_spin_data()
|
|
466
|
+
# Filter for spins only in pointing
|
|
467
|
+
spin_data = spin_data[
|
|
468
|
+
(spin_data["spin_start_met"] >= pointing_start_met)
|
|
469
|
+
& (spin_data["spin_start_met"] <= pointing_stop_met)
|
|
470
|
+
]
|
|
471
|
+
# Get only valid spin data
|
|
472
|
+
valid_mask = (spin_data["spin_phase_valid"].values == 1) & (
|
|
473
|
+
spin_data["spin_period_valid"].values == 1
|
|
474
|
+
)
|
|
475
|
+
n_spins_in_pointing: float = np.sum(
|
|
476
|
+
spin_data[valid_mask].spin_period_sec / nominal_spin_seconds
|
|
477
|
+
)
|
|
478
|
+
logger.info(
|
|
479
|
+
f"Calculated total spins universal spin table. Found {n_spins_in_pointing} "
|
|
480
|
+
f"valid spins."
|
|
481
|
+
)
|
|
482
|
+
# Adjust exposure time by the actual number of valid spins in the pointing
|
|
483
|
+
exposure_pointing_adjusted = n_spins_in_pointing * exposure_time
|
|
450
484
|
return exposure_pointing_adjusted, nominal_deadtime_ratios
|
|
451
485
|
|
|
452
486
|
|
|
@@ -483,13 +517,17 @@ def get_efficiencies_and_geometric_function(
|
|
|
483
517
|
|
|
484
518
|
Returns
|
|
485
519
|
-------
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
520
|
+
gf_averaged : np.ndarray
|
|
521
|
+
Averaged geometric factors across all spin phases.
|
|
522
|
+
Shape = (n_energy_bins, npix).
|
|
523
|
+
eff_averaged : np.ndarray
|
|
524
|
+
Averaged efficiencies across all spin phases.
|
|
525
|
+
Shape = (n_energy_bins, npix).
|
|
490
526
|
"""
|
|
491
527
|
# Load callable efficiency interpolator function
|
|
492
|
-
eff_interpolator = get_efficiency_interpolator(
|
|
528
|
+
eff_interpolator, theta_min_max, phi_min_max = get_efficiency_interpolator(
|
|
529
|
+
ancillary_files
|
|
530
|
+
)
|
|
493
531
|
# load geometric factor lookup table
|
|
494
532
|
geometric_lookup_table = load_geometric_factor_tables(
|
|
495
533
|
ancillary_files, "l1b-sensor-gf-blades"
|
|
@@ -497,6 +535,24 @@ def get_efficiencies_and_geometric_function(
|
|
|
497
535
|
# Get energy bin geometric means
|
|
498
536
|
energy_bin_geometric_means = build_energy_bins()[2]
|
|
499
537
|
energy_bins = len(energy_bin_geometric_means)
|
|
538
|
+
# clip arrays to avoid out of bounds errors
|
|
539
|
+
logger.info(
|
|
540
|
+
"Clipping Theta and Phi values to valid ranges for the efficiency "
|
|
541
|
+
"interpolation. \n"
|
|
542
|
+
f"Theta valid range: {theta_min_max}, Phi valid range: {phi_min_max}. \n "
|
|
543
|
+
f"Found "
|
|
544
|
+
f"{np.sum((theta_vals < theta_min_max[0]) | (theta_vals > theta_min_max[1]))}"
|
|
545
|
+
f" Theta values out of range. \n"
|
|
546
|
+
f"Found "
|
|
547
|
+
f"{np.sum((phi_vals < phi_min_max[0]) | (phi_vals > phi_min_max[1]))}"
|
|
548
|
+
f" Phi values out of range. \n"
|
|
549
|
+
f"Theta min and max values before clipping: "
|
|
550
|
+
f"{theta_vals.min()}, {theta_vals.max()} \n"
|
|
551
|
+
f"Phi min and max values before clipping:"
|
|
552
|
+
f" {phi_vals.min()}, {phi_vals.max()} \n"
|
|
553
|
+
)
|
|
554
|
+
theta_vals_clipped = np.clip(theta_vals, theta_min_max[0], theta_min_max[1])
|
|
555
|
+
phi_vals_clipped = np.clip(phi_vals, phi_min_max[0], phi_min_max[1])
|
|
500
556
|
# Initialize summation arrays for geometric factors and efficiencies
|
|
501
557
|
gf_summation = np.zeros((energy_bins, npix))
|
|
502
558
|
eff_summation = np.zeros((energy_bins, npix))
|
|
@@ -507,6 +563,8 @@ def get_efficiencies_and_geometric_function(
|
|
|
507
563
|
# Compute gf and eff for these theta/phi pairs
|
|
508
564
|
theta_at_spin = theta_vals[:, i]
|
|
509
565
|
phi_at_spin = phi_vals[:, i]
|
|
566
|
+
theta_at_spin_clipped = theta_vals_clipped[:, i]
|
|
567
|
+
phi_at_spin_clipped = phi_vals_clipped[:, i]
|
|
510
568
|
gf_values = get_geometric_factor(
|
|
511
569
|
phi=phi_at_spin,
|
|
512
570
|
theta=theta_at_spin,
|
|
@@ -518,10 +576,12 @@ def get_efficiencies_and_geometric_function(
|
|
|
518
576
|
if pixel_inds.size == 0:
|
|
519
577
|
continue
|
|
520
578
|
energy = energy_bin_geometric_means[energy_bin_idx]
|
|
579
|
+
# Clip energy to calibrated range
|
|
580
|
+
energy_clipped = np.clip(energy, 3.0, 80.0)
|
|
521
581
|
eff_values = get_efficiency(
|
|
522
|
-
np.full(phi_at_spin[pixel_inds].shape,
|
|
523
|
-
|
|
524
|
-
|
|
582
|
+
np.full(phi_at_spin[pixel_inds].shape, energy_clipped),
|
|
583
|
+
phi_at_spin_clipped[pixel_inds],
|
|
584
|
+
theta_at_spin_clipped[pixel_inds],
|
|
525
585
|
ancillary_files,
|
|
526
586
|
interpolator=eff_interpolator,
|
|
527
587
|
)
|
|
@@ -536,8 +596,10 @@ def get_efficiencies_and_geometric_function(
|
|
|
536
596
|
|
|
537
597
|
# return averaged geometric factors and efficiencies across all spin phases
|
|
538
598
|
# These are now energy dependent.
|
|
539
|
-
gf_averaged = np.
|
|
540
|
-
eff_averaged = np.
|
|
599
|
+
gf_averaged = np.zeros_like(gf_summation)
|
|
600
|
+
eff_averaged = np.zeros_like(eff_summation)
|
|
601
|
+
np.divide(gf_summation, sample_count, out=gf_averaged, where=sample_count != 0)
|
|
602
|
+
np.divide(eff_summation, sample_count, out=eff_averaged, where=sample_count != 0)
|
|
541
603
|
return gf_averaged, eff_averaged
|
|
542
604
|
|
|
543
605
|
|
|
@@ -698,8 +760,8 @@ def get_spacecraft_background_rates(
|
|
|
698
760
|
"""
|
|
699
761
|
pulses = get_pulses_per_spin(rates_dataset)
|
|
700
762
|
# Pulses for the pointing.
|
|
701
|
-
etof_min = get_image_params("eTOFMin", sensor, ancillary_files)
|
|
702
|
-
etof_max = get_image_params("eTOFMax", sensor, ancillary_files)
|
|
763
|
+
etof_min = get_image_params("eTOFMin", f"ultra{sensor}", ancillary_files)
|
|
764
|
+
etof_max = get_image_params("eTOFMax", f"ultra{sensor}", ancillary_files)
|
|
703
765
|
spin_number, _ = get_spin_and_duration(
|
|
704
766
|
rates_dataset["shcoarse"], rates_dataset["spin"]
|
|
705
767
|
)
|
|
@@ -364,8 +364,8 @@ def generate_ultra_healpix_skymap( # noqa: PLR0912
|
|
|
364
364
|
for var in pointing_indep_vars:
|
|
365
365
|
skymap.data_1d[var] = skymap.data_1d[var].squeeze("epoch", drop=True)
|
|
366
366
|
|
|
367
|
-
# Background rates must be scaled by
|
|
368
|
-
# map pixel / pointing set pixel
|
|
367
|
+
# Background rates must be scaled by
|
|
368
|
+
# the ratio of the solid angles of the map pixel / pointing set pixel
|
|
369
369
|
skymap.data_1d["background_rates"] *= skymap.solid_angle / pointing_set.solid_angle
|
|
370
370
|
|
|
371
371
|
# Get the energy bin widths from a PointingSet (they will all be the same)
|