Skip to content

Commit ba6f8bc

Browse files
authored
ULTRA l1b use energy bin's geometric mean for scattering culling (#2373)
* use the energy bins mean instead of the event energy for calculating scattering flags
1 parent 1a32b5c commit ba6f8bc

5 files changed

Lines changed: 42 additions & 11 deletions

File tree

imap_processing/quality_flags.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ class ImapDEOutliersUltraFlags(FlagNameMixin):
4343
NONE = CommonFlags.NONE
4444
FOV = 2**0 # bit 0
4545
PHCORR = 2**1 # bit 1
46-
COINPH = 2**2 # bit 4 # Event validity
46+
COINPH = 2**2 # bit 2 # Event validity
47+
INVALID_ENERGY = 2**3 # bit 3
4748

4849

4950
class ImapHkUltraFlags(FlagNameMixin):

imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -354,12 +354,13 @@ def test_get_de_energy_kev(test_fixture):
354354
test_d,
355355
test_tof,
356356
)
357-
358-
energy = get_de_energy_kev(v, species_bin_ph)
357+
quality_flags = np.zeros_like(species_bin_ph)
358+
energy = get_de_energy_kev(v, species_bin_ph, quality_flags)
359359
index_hydrogen = np.where(species_bin_ph == 1)
360360
actual_energy = energy[index_hydrogen[0]]
361361
expected_energy = df_ph["energy_revised"].astype("float")
362-
362+
# All flags should be zero
363+
assert np.sum(quality_flags) == 0
363364
np.testing.assert_allclose(actual_energy, expected_energy, atol=1e-01, rtol=0)
364365

365366

imap_processing/ultra/l1b/de.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,9 @@ def calculate_de(
304304
de_dict["direct_event_unit_position"] = r_hat.astype(np.float32)
305305

306306
tof_energy[valid_indices] = get_de_energy_kev(
307-
velocities[valid_indices], species_bin[valid_indices]
307+
velocities[valid_indices],
308+
species_bin[valid_indices],
309+
quality_flags[valid_indices],
308310
)
309311
de_dict["tof_energy"] = tof_energy
310312
de_dict["energy"] = energy
@@ -348,8 +350,12 @@ def calculate_de(
348350
de_dict["velocity_dps_sc"] = sc_dps_velocity
349351
de_dict["velocity_dps_helio"] = helio_velocity
350352

351-
de_dict["energy_spacecraft"] = get_de_energy_kev(sc_dps_velocity, species_bin)
352-
de_dict["energy_heliosphere"] = get_de_energy_kev(helio_velocity, species_bin)
353+
de_dict["energy_spacecraft"] = get_de_energy_kev(
354+
sc_dps_velocity, species_bin, quality_flags
355+
)
356+
de_dict["energy_heliosphere"] = get_de_energy_kev(
357+
helio_velocity, species_bin, quality_flags
358+
)
353359

354360
de_dict["phi_fwhm"], de_dict["theta_fwhm"] = get_fwhm(
355361
start_type,
@@ -376,6 +382,7 @@ def calculate_de(
376382
ancillary_files,
377383
"l1b-sensor-gf-noblades",
378384
)
385+
379386
de_dict["quality_outliers"] = quality_flags
380387
flag_scattering(
381388
de_dict["tof_energy"],

imap_processing/ultra/l1b/ultra_l1b_culling.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
get_scattering_thresholds,
2323
)
2424
from imap_processing.ultra.l1b.quality_flag_filters import DE_QUALITY_FLAG_FILTERS
25+
from imap_processing.ultra.l1c.l1c_lookup_utils import build_energy_bins
2526

2627
logging.basicConfig(level=logging.INFO)
2728
logger = logging.getLogger(__name__)
@@ -514,7 +515,13 @@ def flag_scattering(
514515
Quality flags.
515516
"""
516517
scattering_thresholds = get_scattering_thresholds(ancillary_files)
517-
518+
_, _, energy_bin_geometric_means = build_energy_bins()
519+
energy_bin_inds = np.digitize(tof_energy, UltraConstants.PSET_ENERGY_BIN_EDGES)
520+
# Clip indices to valid range (events outside the energy bins get assigned
521+
# to the nearest bin. These events have already been flagged and
522+
# will be ignored in l1c)
523+
energy_bin_inds = np.clip(energy_bin_inds, 1, len(energy_bin_geometric_means))
524+
energy_geom_means = energy_bin_geometric_means[energy_bin_inds - 1]
518525
for (e_min, e_max), threshold in scattering_thresholds.items():
519526
event_mask = (tof_energy >= e_min) & (tof_energy < e_max)
520527
# Input the theta and phi values for the current energy range.
@@ -528,8 +535,11 @@ def flag_scattering(
528535
)
529536
# FWHM_PHI = A_PHI * E^G_PHI
530537
# FWHM_THETA = A_THETA * E^G_THETA
531-
fwhm_theta = theta_coeffs[:, 0] * tof_energy[event_mask] ** theta_coeffs[:, 1]
532-
fwhm_phi = phi_coeffs[:, 0] * tof_energy[event_mask] ** phi_coeffs[:, 1]
538+
# Use the geometric mean of the energy bin for the scattering check
539+
fwhm_theta = (
540+
theta_coeffs[:, 0] * energy_geom_means[event_mask] ** theta_coeffs[:, 1]
541+
)
542+
fwhm_phi = phi_coeffs[:, 0] * energy_geom_means[event_mask] ** phi_coeffs[:, 1]
533543
is_nan = np.isnan(fwhm_theta) | np.isnan(fwhm_phi)
534544
quality_flags[np.where(event_mask)[0][is_nan]] |= (
535545
ImapDEScatteringUltraFlags.NAN_PHI_OR_THETA.value

imap_processing/ultra/l1b/ultra_l1b_extended.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,9 @@ def get_ssd_tof(
616616
return np.asarray(tof, dtype=np.float64)
617617

618618

619-
def get_de_energy_kev(v: np.ndarray, species: np.ndarray) -> NDArray:
619+
def get_de_energy_kev(
620+
v: np.ndarray, species: np.ndarray, quality_flags: np.ndarray | None = None
621+
) -> NDArray:
620622
"""
621623
Calculate the direct event energy.
622624
@@ -626,6 +628,8 @@ def get_de_energy_kev(v: np.ndarray, species: np.ndarray) -> NDArray:
626628
N x 3 array of velocity components (vx, vy, vz) in km/s.
627629
species : np.ndarray
628630
Species of the particle.
631+
quality_flags : np.ndarray, optional
632+
Quality flags to set when there is an outlier.
629633
630634
Returns
631635
-------
@@ -648,6 +652,14 @@ def get_de_energy_kev(v: np.ndarray, species: np.ndarray) -> NDArray:
648652
energy[valid_mask] = (
649653
0.5 * UltraConstants.MASS_H * v2[valid_mask] * UltraConstants.J_KEV
650654
)
655+
# Flag out of range energies
656+
if quality_flags is not None:
657+
energy_out_of_range = (energy < UltraConstants.PSET_ENERGY_BIN_EDGES[0]) | (
658+
energy > UltraConstants.PSET_ENERGY_BIN_EDGES[-1]
659+
)
660+
quality_flags[energy_out_of_range] |= (
661+
ImapDEOutliersUltraFlags.INVALID_ENERGY.value
662+
)
651663

652664
return energy
653665

0 commit comments

Comments
 (0)