imap-processing 0.18.0__py3-none-any.whl → 0.19.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of imap-processing might be problematic. Click here for more details.
- imap_processing/_version.py +2 -2
- imap_processing/ancillary/ancillary_dataset_combiner.py +161 -1
- imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +6 -0
- imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +221 -1057
- imap_processing/cdf/config/imap_codice_l1b_variable_attrs.yaml +307 -283
- imap_processing/cdf/config/imap_codice_l2_variable_attrs.yaml +1044 -203
- imap_processing/cdf/config/imap_constant_attrs.yaml +4 -2
- imap_processing/cdf/config/imap_enamaps_l2-common_variable_attrs.yaml +11 -0
- imap_processing/cdf/config/imap_glows_l1b_variable_attrs.yaml +15 -1
- imap_processing/cdf/config/imap_hi_global_cdf_attrs.yaml +5 -0
- imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +10 -4
- imap_processing/cdf/config/imap_idex_l2a_variable_attrs.yaml +33 -4
- imap_processing/cdf/config/imap_idex_l2b_variable_attrs.yaml +8 -91
- imap_processing/cdf/config/imap_idex_l2c_variable_attrs.yaml +106 -16
- imap_processing/cdf/config/imap_lo_global_cdf_attrs.yaml +5 -4
- imap_processing/cdf/config/imap_lo_l1a_variable_attrs.yaml +4 -15
- imap_processing/cdf/config/imap_lo_l1c_variable_attrs.yaml +189 -98
- imap_processing/cdf/config/imap_mag_global_cdf_attrs.yaml +85 -2
- imap_processing/cdf/config/imap_mag_l1c_variable_attrs.yaml +24 -1
- imap_processing/cdf/config/imap_ultra_global_cdf_attrs.yaml +20 -8
- imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +45 -35
- imap_processing/cdf/config/imap_ultra_l1c_variable_attrs.yaml +110 -7
- imap_processing/cli.py +138 -93
- imap_processing/codice/codice_l0.py +2 -1
- imap_processing/codice/codice_l1a.py +167 -69
- imap_processing/codice/codice_l1b.py +42 -32
- imap_processing/codice/codice_l2.py +215 -9
- imap_processing/codice/constants.py +790 -603
- imap_processing/codice/data/lo_stepping_values.csv +1 -1
- imap_processing/decom.py +1 -4
- imap_processing/ena_maps/ena_maps.py +71 -43
- imap_processing/ena_maps/utils/corrections.py +291 -0
- imap_processing/ena_maps/utils/map_utils.py +20 -4
- imap_processing/ena_maps/utils/naming.py +8 -2
- imap_processing/glows/ancillary/imap_glows_exclusions-by-instr-team_20250923_v002.dat +10 -0
- imap_processing/glows/ancillary/imap_glows_map-of-excluded-regions_20250923_v002.dat +393 -0
- imap_processing/glows/ancillary/imap_glows_map-of-uv-sources_20250923_v002.dat +593 -0
- imap_processing/glows/ancillary/imap_glows_pipeline-settings_20250923_v002.json +54 -0
- imap_processing/glows/ancillary/imap_glows_suspected-transients_20250923_v002.dat +10 -0
- imap_processing/glows/l1b/glows_l1b.py +123 -18
- imap_processing/glows/l1b/glows_l1b_data.py +358 -47
- imap_processing/glows/l2/glows_l2.py +11 -0
- imap_processing/hi/hi_l1a.py +124 -3
- imap_processing/hi/hi_l1b.py +154 -71
- imap_processing/hi/hi_l1c.py +4 -109
- imap_processing/hi/hi_l2.py +104 -60
- imap_processing/hi/utils.py +262 -8
- imap_processing/hit/l0/constants.py +3 -0
- imap_processing/hit/l0/decom_hit.py +3 -6
- imap_processing/hit/l1a/hit_l1a.py +311 -21
- imap_processing/hit/l1b/hit_l1b.py +54 -126
- imap_processing/hit/l2/hit_l2.py +6 -6
- imap_processing/ialirt/calculate_ingest.py +219 -0
- imap_processing/ialirt/constants.py +12 -2
- imap_processing/ialirt/generate_coverage.py +15 -2
- imap_processing/ialirt/l0/ialirt_spice.py +6 -2
- imap_processing/ialirt/l0/parse_mag.py +293 -42
- imap_processing/ialirt/l0/process_hit.py +5 -3
- imap_processing/ialirt/l0/process_swapi.py +41 -25
- imap_processing/ialirt/process_ephemeris.py +70 -14
- imap_processing/ialirt/utils/create_xarray.py +1 -1
- imap_processing/idex/idex_l0.py +2 -2
- imap_processing/idex/idex_l1a.py +2 -3
- imap_processing/idex/idex_l1b.py +2 -3
- imap_processing/idex/idex_l2a.py +130 -4
- imap_processing/idex/idex_l2b.py +158 -143
- imap_processing/idex/idex_utils.py +1 -3
- imap_processing/lo/ancillary_data/imap_lo_hydrogen-geometric-factor_v001.csv +75 -0
- imap_processing/lo/ancillary_data/imap_lo_oxygen-geometric-factor_v001.csv +75 -0
- imap_processing/lo/l0/lo_science.py +25 -24
- imap_processing/lo/l1b/lo_l1b.py +93 -19
- imap_processing/lo/l1c/lo_l1c.py +273 -93
- imap_processing/lo/l2/lo_l2.py +949 -135
- imap_processing/lo/lo_ancillary.py +55 -0
- imap_processing/mag/l1a/mag_l1a.py +1 -0
- imap_processing/mag/l1a/mag_l1a_data.py +26 -0
- imap_processing/mag/l1b/mag_l1b.py +3 -2
- imap_processing/mag/l1c/interpolation_methods.py +14 -15
- imap_processing/mag/l1c/mag_l1c.py +23 -6
- imap_processing/mag/l1d/mag_l1d.py +57 -14
- imap_processing/mag/l1d/mag_l1d_data.py +202 -32
- imap_processing/mag/l2/mag_l2.py +2 -0
- imap_processing/mag/l2/mag_l2_data.py +14 -5
- imap_processing/quality_flags.py +23 -1
- imap_processing/spice/geometry.py +89 -39
- imap_processing/spice/pointing_frame.py +4 -8
- imap_processing/spice/repoint.py +78 -2
- imap_processing/spice/spin.py +28 -8
- imap_processing/spice/time.py +12 -22
- imap_processing/swapi/l1/swapi_l1.py +10 -4
- imap_processing/swapi/l2/swapi_l2.py +15 -17
- imap_processing/swe/l1b/swe_l1b.py +1 -2
- imap_processing/ultra/constants.py +30 -24
- imap_processing/ultra/l0/ultra_utils.py +9 -11
- imap_processing/ultra/l1a/ultra_l1a.py +1 -2
- imap_processing/ultra/l1b/badtimes.py +35 -11
- imap_processing/ultra/l1b/de.py +95 -31
- imap_processing/ultra/l1b/extendedspin.py +31 -16
- imap_processing/ultra/l1b/goodtimes.py +112 -0
- imap_processing/ultra/l1b/lookup_utils.py +281 -28
- imap_processing/ultra/l1b/quality_flag_filters.py +10 -1
- imap_processing/ultra/l1b/ultra_l1b.py +7 -7
- imap_processing/ultra/l1b/ultra_l1b_culling.py +169 -7
- imap_processing/ultra/l1b/ultra_l1b_extended.py +311 -69
- imap_processing/ultra/l1c/helio_pset.py +139 -37
- imap_processing/ultra/l1c/l1c_lookup_utils.py +289 -0
- imap_processing/ultra/l1c/spacecraft_pset.py +140 -29
- imap_processing/ultra/l1c/ultra_l1c.py +33 -24
- imap_processing/ultra/l1c/ultra_l1c_culling.py +92 -0
- imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +400 -292
- imap_processing/ultra/l2/ultra_l2.py +54 -11
- imap_processing/ultra/utils/ultra_l1_utils.py +37 -7
- imap_processing/utils.py +3 -4
- {imap_processing-0.18.0.dist-info → imap_processing-0.19.2.dist-info}/METADATA +2 -2
- {imap_processing-0.18.0.dist-info → imap_processing-0.19.2.dist-info}/RECORD +118 -109
- imap_processing/idex/idex_l2c.py +0 -84
- imap_processing/spice/kernels.py +0 -187
- imap_processing/ultra/l1b/cullingmask.py +0 -87
- imap_processing/ultra/l1c/histogram.py +0 -36
- {imap_processing-0.18.0.dist-info → imap_processing-0.19.2.dist-info}/LICENSE +0 -0
- {imap_processing-0.18.0.dist-info → imap_processing-0.19.2.dist-info}/WHEEL +0 -0
- {imap_processing-0.18.0.dist-info → imap_processing-0.19.2.dist-info}/entry_points.txt +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Culls Events for ULTRA L1b."""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
+
from collections import namedtuple
|
|
4
5
|
|
|
5
6
|
import numpy as np
|
|
6
7
|
import pandas as pd
|
|
@@ -9,18 +10,37 @@ from numpy.typing import NDArray
|
|
|
9
10
|
|
|
10
11
|
from imap_processing.quality_flags import (
|
|
11
12
|
ImapAttitudeUltraFlags,
|
|
13
|
+
ImapDEScatteringUltraFlags,
|
|
12
14
|
ImapHkUltraFlags,
|
|
13
15
|
ImapInstrumentUltraFlags,
|
|
14
16
|
ImapRatesUltraFlags,
|
|
15
17
|
)
|
|
16
18
|
from imap_processing.spice.spin import get_spin_data
|
|
17
19
|
from imap_processing.ultra.constants import UltraConstants
|
|
20
|
+
from imap_processing.ultra.l1b.lookup_utils import (
|
|
21
|
+
get_scattering_coefficients,
|
|
22
|
+
get_scattering_thresholds,
|
|
23
|
+
)
|
|
24
|
+
from imap_processing.ultra.l1b.quality_flag_filters import DE_QUALITY_FLAG_FILTERS
|
|
18
25
|
|
|
19
26
|
logging.basicConfig(level=logging.INFO)
|
|
20
27
|
logger = logging.getLogger(__name__)
|
|
21
28
|
|
|
22
29
|
SPIN_DURATION = 15 # Default spin duration in seconds.
|
|
23
30
|
|
|
31
|
+
RateResult = namedtuple(
|
|
32
|
+
"RateResult",
|
|
33
|
+
[
|
|
34
|
+
"unique_spins",
|
|
35
|
+
"start_per_spin",
|
|
36
|
+
"stop_per_spin",
|
|
37
|
+
"coin_per_spin",
|
|
38
|
+
"start_pulses",
|
|
39
|
+
"stop_pulses",
|
|
40
|
+
"coin_pulses",
|
|
41
|
+
],
|
|
42
|
+
)
|
|
43
|
+
|
|
24
44
|
|
|
25
45
|
def get_energy_histogram(
|
|
26
46
|
spin_number: NDArray, energy: NDArray
|
|
@@ -230,8 +250,8 @@ def flag_rates(
|
|
|
230
250
|
Quality flags.
|
|
231
251
|
spin : NDArray
|
|
232
252
|
Spin data.
|
|
233
|
-
|
|
234
|
-
Energy
|
|
253
|
+
energy_bin_geometric_mean : NDArray
|
|
254
|
+
Energy bin geometric mean.
|
|
235
255
|
n_sigma_per_energy_reshape : NDArray
|
|
236
256
|
N sigma per energy.
|
|
237
257
|
"""
|
|
@@ -245,7 +265,7 @@ def flag_rates(
|
|
|
245
265
|
threshold = get_n_sigma(count_rates, duration, sigma=sigma)
|
|
246
266
|
|
|
247
267
|
bin_edges = np.array(UltraConstants.CULLING_ENERGY_BIN_EDGES)
|
|
248
|
-
|
|
268
|
+
energy_bin_geometric_mean = np.sqrt(bin_edges[:-1] * bin_edges[1:])
|
|
249
269
|
spin = np.unique(spin_number)
|
|
250
270
|
|
|
251
271
|
# Indices where the counts exceed the threshold
|
|
@@ -256,7 +276,7 @@ def flag_rates(
|
|
|
256
276
|
quality_flags[:, 0] |= ImapRatesUltraFlags.FIRSTSPIN.value
|
|
257
277
|
quality_flags[:, -1] |= ImapRatesUltraFlags.LASTSPIN.value
|
|
258
278
|
|
|
259
|
-
return quality_flags, spin,
|
|
279
|
+
return quality_flags, spin, energy_bin_geometric_mean, threshold
|
|
260
280
|
|
|
261
281
|
|
|
262
282
|
def compare_aux_univ_spin_table(
|
|
@@ -369,7 +389,7 @@ def get_spin_and_duration(met: NDArray, spin: NDArray) -> tuple[NDArray, NDArray
|
|
|
369
389
|
possible_spins = spin_numbers & 0xFF
|
|
370
390
|
|
|
371
391
|
# Assign each group based on time.
|
|
372
|
-
for start, end in zip(spin_start_indices, spin_end_indices):
|
|
392
|
+
for start, end in zip(spin_start_indices, spin_end_indices, strict=False):
|
|
373
393
|
# Now that we have the possible spins from the Universal Spin Table,
|
|
374
394
|
# we match the times of those spins to the nearest times in the DE data.
|
|
375
395
|
possible_times = spin_start_mets[
|
|
@@ -394,7 +414,7 @@ def get_spin_and_duration(met: NDArray, spin: NDArray) -> tuple[NDArray, NDArray
|
|
|
394
414
|
return assigned_spin_number, assigned_duration
|
|
395
415
|
|
|
396
416
|
|
|
397
|
-
def get_pulses_per_spin(rates: xr.Dataset) ->
|
|
417
|
+
def get_pulses_per_spin(rates: xr.Dataset) -> RateResult:
|
|
398
418
|
"""
|
|
399
419
|
Get the total number of pulses per spin.
|
|
400
420
|
|
|
@@ -405,12 +425,20 @@ def get_pulses_per_spin(rates: xr.Dataset) -> tuple[NDArray, NDArray, NDArray]:
|
|
|
405
425
|
|
|
406
426
|
Returns
|
|
407
427
|
-------
|
|
428
|
+
unique_spins : NDArray
|
|
429
|
+
Unique spin numbers.
|
|
408
430
|
start_per_spin : NDArray
|
|
409
431
|
Total start pulses per spin.
|
|
410
432
|
stop_per_spin : NDArray
|
|
411
433
|
Total stop pulses per spin.
|
|
412
434
|
coin_per_spin : NDArray
|
|
413
435
|
Total coincidence pulses per spin.
|
|
436
|
+
start_pulses : NDArray
|
|
437
|
+
Total start pulses.
|
|
438
|
+
stop_pulses : NDArray
|
|
439
|
+
Total stop pulses.
|
|
440
|
+
coin_pulses : NDArray
|
|
441
|
+
Total coincidence pulses.
|
|
414
442
|
"""
|
|
415
443
|
spin_number, duration = get_spin_and_duration(rates["shcoarse"], rates["spin"])
|
|
416
444
|
|
|
@@ -448,4 +476,138 @@ def get_pulses_per_spin(rates: xr.Dataset) -> tuple[NDArray, NDArray, NDArray]:
|
|
|
448
476
|
stop_per_spin = np.bincount(spin_idx, weights=stop_pulses)
|
|
449
477
|
coin_per_spin = np.bincount(spin_idx, weights=coin_pulses)
|
|
450
478
|
|
|
451
|
-
return
|
|
479
|
+
return RateResult(
|
|
480
|
+
unique_spins=unique_spins,
|
|
481
|
+
start_per_spin=start_per_spin,
|
|
482
|
+
stop_per_spin=stop_per_spin,
|
|
483
|
+
coin_per_spin=coin_per_spin,
|
|
484
|
+
start_pulses=start_pulses,
|
|
485
|
+
stop_pulses=stop_pulses,
|
|
486
|
+
coin_pulses=coin_pulses,
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
def flag_scattering(
|
|
491
|
+
tof_energy: NDArray,
|
|
492
|
+
theta: NDArray,
|
|
493
|
+
phi: NDArray,
|
|
494
|
+
ancillary_files: dict,
|
|
495
|
+
sensor: str,
|
|
496
|
+
quality_flags: NDArray,
|
|
497
|
+
) -> None:
|
|
498
|
+
"""
|
|
499
|
+
Flag events where either theta or phi FWHM exceed the threshold or equal nan.
|
|
500
|
+
|
|
501
|
+
Parameters
|
|
502
|
+
----------
|
|
503
|
+
tof_energy : NDArray
|
|
504
|
+
TOF energy for each event in keV.
|
|
505
|
+
theta : NDArray
|
|
506
|
+
Elevation angles in degrees.
|
|
507
|
+
phi : NDArray
|
|
508
|
+
Azimuth angles in degrees.
|
|
509
|
+
ancillary_files : dict[Path]
|
|
510
|
+
Ancillary files.
|
|
511
|
+
sensor : str
|
|
512
|
+
Sensor name: "ultra45" or "ultra90".
|
|
513
|
+
quality_flags : NDArray
|
|
514
|
+
Quality flags.
|
|
515
|
+
"""
|
|
516
|
+
scattering_thresholds = get_scattering_thresholds(ancillary_files)
|
|
517
|
+
|
|
518
|
+
for (e_min, e_max), threshold in scattering_thresholds.items():
|
|
519
|
+
event_mask = (tof_energy >= e_min) & (tof_energy < e_max)
|
|
520
|
+
# Input the theta and phi values for the current energy range.
|
|
521
|
+
# Returns a_theta_val, g_theta_val, a_phi_val, g_phi_val
|
|
522
|
+
theta_coeffs, phi_coeffs = get_scattering_coefficients(
|
|
523
|
+
theta[event_mask],
|
|
524
|
+
phi[event_mask],
|
|
525
|
+
lookup_tables=None,
|
|
526
|
+
ancillary_files=ancillary_files,
|
|
527
|
+
instrument_id=int(sensor[-2:]),
|
|
528
|
+
)
|
|
529
|
+
# FWHM_PHI = A_PHI * E^G_PHI
|
|
530
|
+
# 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]
|
|
533
|
+
is_nan = np.isnan(fwhm_theta) | np.isnan(fwhm_phi)
|
|
534
|
+
quality_flags[np.where(event_mask)[0][is_nan]] |= (
|
|
535
|
+
ImapDEScatteringUltraFlags.NAN_PHI_OR_THETA.value
|
|
536
|
+
)
|
|
537
|
+
|
|
538
|
+
theta_exceeds = fwhm_theta > threshold
|
|
539
|
+
phi_exceeds = fwhm_phi > threshold
|
|
540
|
+
either_exceeds = theta_exceeds | phi_exceeds
|
|
541
|
+
|
|
542
|
+
# Set flags for events where either theta or phi FWHM exceed the threshold
|
|
543
|
+
quality_flags[np.where(event_mask)[0][either_exceeds]] |= (
|
|
544
|
+
ImapDEScatteringUltraFlags.ABOVE_THRESHOLD.value
|
|
545
|
+
)
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
def get_de_rejection_mask(
|
|
549
|
+
quality_scattering: NDArray, quality_outliers: NDArray
|
|
550
|
+
) -> NDArray:
|
|
551
|
+
"""
|
|
552
|
+
Create boolean mask where event is rejected due to relevant flags.
|
|
553
|
+
|
|
554
|
+
Parameters
|
|
555
|
+
----------
|
|
556
|
+
quality_scattering : NDArray
|
|
557
|
+
Quality scattering flags.
|
|
558
|
+
quality_outliers : NDArray
|
|
559
|
+
Quality outliers flags.
|
|
560
|
+
|
|
561
|
+
Returns
|
|
562
|
+
-------
|
|
563
|
+
rejected : NDArray
|
|
564
|
+
Rejected events where True = rejected.
|
|
565
|
+
"""
|
|
566
|
+
# Bitmasks from the DE_QUALITY_FLAG_FILTERS
|
|
567
|
+
scattering_mask = sum(
|
|
568
|
+
flag.value for flag in DE_QUALITY_FLAG_FILTERS["quality_scattering"]
|
|
569
|
+
)
|
|
570
|
+
outliers_mask = sum(
|
|
571
|
+
flag.value for flag in DE_QUALITY_FLAG_FILTERS["quality_outliers"]
|
|
572
|
+
)
|
|
573
|
+
|
|
574
|
+
# Boolean mask where event is rejected due to relevant flags
|
|
575
|
+
rejected = ((quality_scattering & scattering_mask) != 0) | (
|
|
576
|
+
(quality_outliers & outliers_mask) != 0
|
|
577
|
+
)
|
|
578
|
+
|
|
579
|
+
return rejected
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
def count_rejected_events_per_spin(
|
|
583
|
+
spins: NDArray, quality_scattering: NDArray, quality_outliers: NDArray
|
|
584
|
+
) -> NDArray:
|
|
585
|
+
"""
|
|
586
|
+
Count rejected events per spin based on DE_QUALITY_FLAG_FILTERS.
|
|
587
|
+
|
|
588
|
+
Parameters
|
|
589
|
+
----------
|
|
590
|
+
spins : NDArray
|
|
591
|
+
Spins in which each direct event is within.
|
|
592
|
+
quality_scattering : NDArray
|
|
593
|
+
Quality scattering flags.
|
|
594
|
+
quality_outliers : NDArray
|
|
595
|
+
Quality outliers flags.
|
|
596
|
+
|
|
597
|
+
Returns
|
|
598
|
+
-------
|
|
599
|
+
rejected_counts : NDArray
|
|
600
|
+
Rejected counts per spin.
|
|
601
|
+
"""
|
|
602
|
+
# Boolean mask where event is rejected due to relevant flags
|
|
603
|
+
rejected = get_de_rejection_mask(quality_scattering, quality_outliers)
|
|
604
|
+
|
|
605
|
+
# Unique spin numbers
|
|
606
|
+
unique_spins = np.unique(spins)
|
|
607
|
+
|
|
608
|
+
# Count rejected events per spin
|
|
609
|
+
rejected_counts = np.array(
|
|
610
|
+
[np.count_nonzero(rejected[spins == spin]) for spin in unique_spins], dtype=int
|
|
611
|
+
)
|
|
612
|
+
|
|
613
|
+
return rejected_counts
|