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
|
@@ -37,28 +37,28 @@ HistPacking = namedtuple(
|
|
|
37
37
|
|
|
38
38
|
HIST_DATA_META = {
|
|
39
39
|
# field: bit_length, section_length, shape
|
|
40
|
-
"start_a": HistPacking(12, 504, (
|
|
41
|
-
"start_c": HistPacking(12, 504, (
|
|
42
|
-
"stop_b0": HistPacking(12, 504, (
|
|
43
|
-
"stop_b3": HistPacking(12, 504, (
|
|
44
|
-
"tof0_count": HistPacking(8, 336, (
|
|
45
|
-
"tof1_count": HistPacking(8, 336, (
|
|
46
|
-
"tof2_count": HistPacking(8, 336, (
|
|
47
|
-
"tof3_count": HistPacking(8, 336, (
|
|
48
|
-
"tof0_tof1": HistPacking(8, 3360, (
|
|
49
|
-
"tof0_tof2": HistPacking(8, 3360, (
|
|
50
|
-
"tof1_tof2": HistPacking(8, 3360, (
|
|
51
|
-
"silver": HistPacking(8, 3360, (
|
|
52
|
-
"disc_tof0": HistPacking(8, 336, (
|
|
53
|
-
"disc_tof1": HistPacking(8, 336, (
|
|
54
|
-
"disc_tof2": HistPacking(8, 336, (
|
|
55
|
-
"disc_tof3": HistPacking(8, 336, (
|
|
56
|
-
"pos0": HistPacking(12, 504, (
|
|
57
|
-
"pos1": HistPacking(12, 504, (
|
|
58
|
-
"pos2": HistPacking(12, 504, (
|
|
59
|
-
"pos3": HistPacking(12, 504, (
|
|
60
|
-
"hydrogen": HistPacking(8, 3360, (
|
|
61
|
-
"oxygen": HistPacking(8, 3360, (
|
|
40
|
+
"start_a": HistPacking(12, 504, (7, 6)),
|
|
41
|
+
"start_c": HistPacking(12, 504, (7, 6)),
|
|
42
|
+
"stop_b0": HistPacking(12, 504, (7, 6)),
|
|
43
|
+
"stop_b3": HistPacking(12, 504, (7, 6)),
|
|
44
|
+
"tof0_count": HistPacking(8, 336, (7, 6)),
|
|
45
|
+
"tof1_count": HistPacking(8, 336, (7, 6)),
|
|
46
|
+
"tof2_count": HistPacking(8, 336, (7, 6)),
|
|
47
|
+
"tof3_count": HistPacking(8, 336, (7, 6)),
|
|
48
|
+
"tof0_tof1": HistPacking(8, 3360, (7, 60)),
|
|
49
|
+
"tof0_tof2": HistPacking(8, 3360, (7, 60)),
|
|
50
|
+
"tof1_tof2": HistPacking(8, 3360, (7, 60)),
|
|
51
|
+
"silver": HistPacking(8, 3360, (7, 60)),
|
|
52
|
+
"disc_tof0": HistPacking(8, 336, (7, 6)),
|
|
53
|
+
"disc_tof1": HistPacking(8, 336, (7, 6)),
|
|
54
|
+
"disc_tof2": HistPacking(8, 336, (7, 6)),
|
|
55
|
+
"disc_tof3": HistPacking(8, 336, (7, 6)),
|
|
56
|
+
"pos0": HistPacking(12, 504, (7, 6)),
|
|
57
|
+
"pos1": HistPacking(12, 504, (7, 6)),
|
|
58
|
+
"pos2": HistPacking(12, 504, (7, 6)),
|
|
59
|
+
"pos3": HistPacking(12, 504, (7, 6)),
|
|
60
|
+
"hydrogen": HistPacking(8, 3360, (7, 60)),
|
|
61
|
+
"oxygen": HistPacking(8, 3360, (7, 60)),
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
|
|
@@ -399,7 +399,7 @@ def combine_segmented_packets(dataset: xr.Dataset) -> xr.Dataset:
|
|
|
399
399
|
# Combine the segmented packets into a single binary string
|
|
400
400
|
dataset["events"] = [
|
|
401
401
|
"".join(dataset["data"].values[start : end + 1])
|
|
402
|
-
for start, end in zip(seg_starts, seg_ends)
|
|
402
|
+
for start, end in zip(seg_starts, seg_ends, strict=False)
|
|
403
403
|
]
|
|
404
404
|
|
|
405
405
|
# drop any group of segmented packets that aren't sequential
|
|
@@ -441,7 +441,8 @@ def find_valid_groups(
|
|
|
441
441
|
"""
|
|
442
442
|
# Check if the sequence counters from the CCSDS header are sequential
|
|
443
443
|
grouped_seq_ctrs = [
|
|
444
|
-
np.array(seq_ctrs[start : end + 1])
|
|
444
|
+
np.array(seq_ctrs[start : end + 1])
|
|
445
|
+
for start, end in zip(seg_starts, seg_ends, strict=False)
|
|
445
446
|
]
|
|
446
447
|
valid_groups = [is_sequential(seq_ctrs) for seq_ctrs in grouped_seq_ctrs]
|
|
447
448
|
return valid_groups
|
imap_processing/lo/l1a/lo_l1a.py
CHANGED
|
@@ -45,6 +45,11 @@ def lo_l1a(dependency: Path) -> list[xr.Dataset]:
|
|
|
45
45
|
xtce_packet_definition=xtce_file.resolve(),
|
|
46
46
|
use_derived_value=False,
|
|
47
47
|
)
|
|
48
|
+
datasets_by_apid_derived = packet_file_to_datasets(
|
|
49
|
+
packet_file=dependency.resolve(),
|
|
50
|
+
xtce_packet_definition=xtce_file.resolve(),
|
|
51
|
+
use_derived_value=True,
|
|
52
|
+
)
|
|
48
53
|
|
|
49
54
|
# create the attribute manager for this data level
|
|
50
55
|
attr_mgr = ImapCdfAttributes()
|
|
@@ -101,6 +106,45 @@ def lo_l1a(dependency: Path) -> list[xr.Dataset]:
|
|
|
101
106
|
ds = process_star_sensor(ds)
|
|
102
107
|
ds = add_dataset_attrs(ds, attr_mgr, logical_source)
|
|
103
108
|
datasets_to_return.append(ds)
|
|
109
|
+
if LoAPID.ILO_DIAG_PCC in datasets_by_apid:
|
|
110
|
+
logger.info(
|
|
111
|
+
f"\nProcessing {LoAPID(LoAPID.ILO_DIAG_PCC).name} "
|
|
112
|
+
f"packet (APID: {LoAPID.ILO_DIAG_PCC.value})"
|
|
113
|
+
)
|
|
114
|
+
logical_source = "imap_lo_l1a_pcc"
|
|
115
|
+
ds = datasets_by_apid[LoAPID.ILO_DIAG_PCC]
|
|
116
|
+
ds = add_dataset_attrs(ds, attr_mgr, logical_source)
|
|
117
|
+
datasets_to_return.append(ds)
|
|
118
|
+
if LoAPID.ILO_APP_NHK in datasets_by_apid:
|
|
119
|
+
logger.info(
|
|
120
|
+
f"\nProcessing {LoAPID(LoAPID.ILO_APP_NHK).name} "
|
|
121
|
+
f"packet (APID: {LoAPID.ILO_APP_NHK.value})"
|
|
122
|
+
)
|
|
123
|
+
logical_source = "imap_lo_l1a_nhk"
|
|
124
|
+
ds = datasets_by_apid[LoAPID.ILO_APP_NHK]
|
|
125
|
+
ds = add_dataset_attrs(ds, attr_mgr, logical_source)
|
|
126
|
+
datasets_to_return.append(ds)
|
|
127
|
+
|
|
128
|
+
# Engineering units conversion
|
|
129
|
+
logical_source = "imap_lo_l1b_nhk"
|
|
130
|
+
ds = datasets_by_apid_derived[LoAPID.ILO_APP_NHK]
|
|
131
|
+
ds = add_dataset_attrs(ds, attr_mgr, logical_source)
|
|
132
|
+
datasets_to_return.append(ds)
|
|
133
|
+
if LoAPID.ILO_APP_SHK in datasets_by_apid:
|
|
134
|
+
logger.info(
|
|
135
|
+
f"\nProcessing {LoAPID(LoAPID.ILO_APP_SHK).name} "
|
|
136
|
+
f"packet (APID: {LoAPID.ILO_APP_SHK.value})"
|
|
137
|
+
)
|
|
138
|
+
logical_source = "imap_lo_l1a_shk"
|
|
139
|
+
ds = datasets_by_apid[LoAPID.ILO_APP_SHK]
|
|
140
|
+
ds = add_dataset_attrs(ds, attr_mgr, logical_source)
|
|
141
|
+
datasets_to_return.append(ds)
|
|
142
|
+
|
|
143
|
+
# Engineering units conversion
|
|
144
|
+
logical_source = "imap_lo_l1b_shk"
|
|
145
|
+
ds = datasets_by_apid_derived[LoAPID.ILO_APP_SHK]
|
|
146
|
+
ds = add_dataset_attrs(ds, attr_mgr, logical_source)
|
|
147
|
+
datasets_to_return.append(ds)
|
|
104
148
|
|
|
105
149
|
logger.info(f"Returning [{len(datasets_to_return)}] datasets")
|
|
106
150
|
return datasets_to_return
|
imap_processing/lo/l1b/lo_l1b.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import logging
|
|
4
4
|
from dataclasses import Field
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from typing import Any
|
|
6
|
+
from typing import Any
|
|
7
7
|
|
|
8
8
|
import numpy as np
|
|
9
9
|
import xarray as xr
|
|
@@ -204,7 +204,7 @@ def get_avg_spin_durations_per_cycle(
|
|
|
204
204
|
return avg_spin_durations_per_cycle
|
|
205
205
|
|
|
206
206
|
|
|
207
|
-
def get_spin_angle(l1a_de: xr.Dataset) ->
|
|
207
|
+
def get_spin_angle(l1a_de: xr.Dataset) -> np.ndarray[np.float64] | Any:
|
|
208
208
|
"""
|
|
209
209
|
Get the spin angle (0 - 360 degrees) for each DE.
|
|
210
210
|
|
|
@@ -587,7 +587,7 @@ def convert_tofs_to_eu(
|
|
|
587
587
|
tof_conversions = [TOF0_CONV, TOF1_CONV, TOF2_CONV, TOF3_CONV]
|
|
588
588
|
|
|
589
589
|
# Loop through the TOF fields and convert them to engineering units
|
|
590
|
-
for tof, conv in zip(tof_fields, tof_conversions):
|
|
590
|
+
for tof, conv in zip(tof_fields, tof_conversions, strict=False):
|
|
591
591
|
# Get the fill value for the L1A and L1B TOF
|
|
592
592
|
fillval_1a = attr_mgr_l1a.get_variable_attributes(tof)["FILLVAL"]
|
|
593
593
|
fillval_1b = attr_mgr_l1b.get_variable_attributes(tof)["FILLVAL"]
|
imap_processing/lo/l1c/lo_l1c.py
CHANGED
|
@@ -4,12 +4,26 @@ from dataclasses import Field
|
|
|
4
4
|
from enum import Enum
|
|
5
5
|
|
|
6
6
|
import numpy as np
|
|
7
|
-
import pandas as pd
|
|
8
7
|
import xarray as xr
|
|
9
8
|
from scipy.stats import binned_statistic_dd
|
|
10
9
|
|
|
11
10
|
from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
|
|
12
|
-
from imap_processing.
|
|
11
|
+
from imap_processing.lo import lo_ancillary
|
|
12
|
+
from imap_processing.spice.repoint import get_pointing_times
|
|
13
|
+
from imap_processing.spice.spin import get_spin_number
|
|
14
|
+
from imap_processing.spice.time import met_to_ttj2000ns, ttj2000ns_to_met
|
|
15
|
+
|
|
16
|
+
N_ESA_ENERGY_STEPS = 7
|
|
17
|
+
N_SPIN_ANGLE_BINS = 3600
|
|
18
|
+
N_OFF_ANGLE_BINS = 40
|
|
19
|
+
# 1 time, 7 energy steps, 3600 spin angle bins, and 40 off angle bins
|
|
20
|
+
PSET_SHAPE = (1, N_ESA_ENERGY_STEPS, N_SPIN_ANGLE_BINS, N_OFF_ANGLE_BINS)
|
|
21
|
+
PSET_DIMS = ["epoch", "esa_energy_step", "spin_angle", "off_angle"]
|
|
22
|
+
ESA_ENERGY_STEPS = np.arange(N_ESA_ENERGY_STEPS) + 1 # 1 to 7 inclusive
|
|
23
|
+
SPIN_ANGLE_BIN_EDGES = np.linspace(0, 360, N_SPIN_ANGLE_BINS + 1)
|
|
24
|
+
SPIN_ANGLE_BIN_CENTERS = (SPIN_ANGLE_BIN_EDGES[:-1] + SPIN_ANGLE_BIN_EDGES[1:]) / 2
|
|
25
|
+
OFF_ANGLE_BIN_EDGES = np.linspace(-2, 2, N_OFF_ANGLE_BINS + 1)
|
|
26
|
+
OFF_ANGLE_BIN_CENTERS = (OFF_ANGLE_BIN_EDGES[:-1] + OFF_ANGLE_BIN_EDGES[1:]) / 2
|
|
13
27
|
|
|
14
28
|
|
|
15
29
|
class FilterType(str, Enum):
|
|
@@ -52,10 +66,45 @@ def lo_l1c(sci_dependencies: dict, anc_dependencies: list) -> list[xr.Dataset]:
|
|
|
52
66
|
if "imap_lo_l1b_de" in sci_dependencies:
|
|
53
67
|
logical_source = "imap_lo_l1c_pset"
|
|
54
68
|
l1b_de = sci_dependencies["imap_lo_l1b_de"]
|
|
55
|
-
|
|
56
69
|
l1b_goodtimes_only = filter_goodtimes(l1b_de, anc_dependencies)
|
|
57
70
|
pset = initialize_pset(l1b_goodtimes_only, attr_mgr, logical_source)
|
|
58
71
|
full_counts = create_pset_counts(l1b_goodtimes_only)
|
|
72
|
+
|
|
73
|
+
# Set the pointing start and end times based on the first epoch
|
|
74
|
+
pointing_start_met, pointing_end_met = get_pointing_times(
|
|
75
|
+
ttj2000ns_to_met(l1b_goodtimes_only["epoch"][0].item())
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
pset["pointing_start_met"] = xr.DataArray(
|
|
79
|
+
np.array([pointing_start_met]),
|
|
80
|
+
dims="epoch",
|
|
81
|
+
attrs=attr_mgr.get_variable_attributes("pointing_start_met"),
|
|
82
|
+
)
|
|
83
|
+
pset["pointing_end_met"] = xr.DataArray(
|
|
84
|
+
np.array([pointing_end_met]),
|
|
85
|
+
dims="epoch",
|
|
86
|
+
attrs=attr_mgr.get_variable_attributes("pointing_end_met"),
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Set the epoch to the start of the pointing
|
|
90
|
+
pset["epoch"] = xr.DataArray(
|
|
91
|
+
met_to_ttj2000ns(pset["pointing_start_met"].values),
|
|
92
|
+
attrs=attr_mgr.get_variable_attributes("epoch"),
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# Get the start and end spin numbers based on the pointing start and end MET
|
|
96
|
+
pset["start_spin_number"] = xr.DataArray(
|
|
97
|
+
[get_spin_number(pset["pointing_start_met"].item())],
|
|
98
|
+
dims="epoch",
|
|
99
|
+
attrs=attr_mgr.get_variable_attributes("start_spin_number"),
|
|
100
|
+
)
|
|
101
|
+
pset["end_spin_number"] = xr.DataArray(
|
|
102
|
+
[get_spin_number(pset["pointing_end_met"].item())],
|
|
103
|
+
dims="epoch",
|
|
104
|
+
attrs=attr_mgr.get_variable_attributes("end_spin_number"),
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# Set the counts
|
|
59
108
|
pset["triples_counts"] = create_pset_counts(
|
|
60
109
|
l1b_goodtimes_only, FilterType.TRIPLES
|
|
61
110
|
)
|
|
@@ -64,19 +113,18 @@ def lo_l1c(sci_dependencies: dict, anc_dependencies: list) -> list[xr.Dataset]:
|
|
|
64
113
|
)
|
|
65
114
|
pset["h_counts"] = create_pset_counts(l1b_goodtimes_only, FilterType.HYDROGEN)
|
|
66
115
|
pset["o_counts"] = create_pset_counts(l1b_goodtimes_only, FilterType.OXYGEN)
|
|
116
|
+
|
|
117
|
+
# Set the exposure time
|
|
67
118
|
pset["exposure_time"] = calculate_exposure_times(
|
|
68
119
|
full_counts, l1b_goodtimes_only
|
|
69
120
|
)
|
|
70
121
|
pset.attrs = attr_mgr.get_global_attributes(logical_source)
|
|
71
|
-
# TODO: Temp fix before adding attribute variables.
|
|
72
|
-
# CDF won't open if DEPEND_0 is not deleted currently.
|
|
73
|
-
del pset["epoch"].attrs["DEPEND_0"]
|
|
74
122
|
|
|
75
123
|
pset = pset.assign_coords(
|
|
76
124
|
{
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
125
|
+
"esa_energy_step": ESA_ENERGY_STEPS,
|
|
126
|
+
"spin_angle": SPIN_ANGLE_BIN_CENTERS,
|
|
127
|
+
"off_angle": OFF_ANGLE_BIN_CENTERS,
|
|
80
128
|
}
|
|
81
129
|
)
|
|
82
130
|
|
|
@@ -141,7 +189,7 @@ def filter_goodtimes(l1b_de: xr.Dataset, anc_dependencies: list) -> xr.Dataset:
|
|
|
141
189
|
Filtered L1B Direct Event dataset.
|
|
142
190
|
"""
|
|
143
191
|
# the goodtimes are currently the only ancillary file needed for L1C processing
|
|
144
|
-
goodtimes_table_df =
|
|
192
|
+
goodtimes_table_df = lo_ancillary.read_ancillary_file(anc_dependencies[0])
|
|
145
193
|
|
|
146
194
|
# convert goodtimes from MET to TTJ2000
|
|
147
195
|
goodtimes_start = met_to_ttj2000ns(goodtimes_table_df["GoodTime_strt"])
|
|
@@ -151,7 +199,7 @@ def filter_goodtimes(l1b_de: xr.Dataset, anc_dependencies: list) -> xr.Dataset:
|
|
|
151
199
|
goodtimes_mask = np.zeros_like(l1b_de["epoch"], dtype=bool)
|
|
152
200
|
|
|
153
201
|
# Iterate over the good times and create a mask
|
|
154
|
-
for start, end in zip(goodtimes_start, goodtimes_end):
|
|
202
|
+
for start, end in zip(goodtimes_start, goodtimes_end, strict=False):
|
|
155
203
|
goodtimes_mask |= (l1b_de["epoch"] >= start) & (l1b_de["epoch"] < end)
|
|
156
204
|
|
|
157
205
|
# Filter the dataset using the mask
|
|
@@ -249,7 +297,7 @@ def create_pset_counts(
|
|
|
249
297
|
|
|
250
298
|
counts = xr.DataArray(
|
|
251
299
|
data=hist.astype(np.int16),
|
|
252
|
-
dims=
|
|
300
|
+
dims=PSET_DIMS,
|
|
253
301
|
)
|
|
254
302
|
|
|
255
303
|
return counts
|
|
@@ -275,11 +323,6 @@ def calculate_exposure_times(counts: xr.DataArray, l1b_de: xr.Dataset) -> xr.Dat
|
|
|
275
323
|
exposure_time : xarray.DataArray
|
|
276
324
|
The exposure times for the L1B Direct Event dataset.
|
|
277
325
|
"""
|
|
278
|
-
# Create bin edges
|
|
279
|
-
lon_edges = np.arange(3601)
|
|
280
|
-
lat_edges = np.arange(41)
|
|
281
|
-
energy_edges = np.arange(8)
|
|
282
|
-
|
|
283
326
|
data = np.column_stack(
|
|
284
327
|
(l1b_de["esa_step"], l1b_de["pointing_bin_lon"], l1b_de["pointing_bin_lat"])
|
|
285
328
|
)
|
|
@@ -289,14 +332,19 @@ def calculate_exposure_times(counts: xr.DataArray, l1b_de: xr.Dataset) -> xr.Dat
|
|
|
289
332
|
# exposure time equation from Lo Alg Document 10.1.1.4
|
|
290
333
|
4 * l1b_de["avg_spin_durations"].to_numpy() / 3600,
|
|
291
334
|
statistic="mean",
|
|
292
|
-
|
|
335
|
+
# NOTE: The l1b pointing_bin_lon is bin number, not actual angle
|
|
336
|
+
bins=[
|
|
337
|
+
np.arange(N_ESA_ENERGY_STEPS + 1),
|
|
338
|
+
np.arange(N_SPIN_ANGLE_BINS + 1),
|
|
339
|
+
np.arange(N_OFF_ANGLE_BINS + 1),
|
|
340
|
+
],
|
|
293
341
|
)
|
|
294
342
|
|
|
295
343
|
stat = result.statistic[np.newaxis, :, :, :]
|
|
296
344
|
|
|
297
345
|
exposure_time = xr.DataArray(
|
|
298
346
|
data=stat.astype(np.float16),
|
|
299
|
-
dims=
|
|
347
|
+
dims=PSET_DIMS,
|
|
300
348
|
)
|
|
301
349
|
|
|
302
350
|
return exposure_time
|
|
@@ -328,8 +376,6 @@ def create_datasets(
|
|
|
328
376
|
# can be used direction
|
|
329
377
|
epoch_converted_time = [1]
|
|
330
378
|
|
|
331
|
-
# Create a data array for the epoch time
|
|
332
|
-
# TODO: might need to update the attrs to use new YAML file
|
|
333
379
|
epoch_time = xr.DataArray(
|
|
334
380
|
data=epoch_converted_time,
|
|
335
381
|
name="epoch",
|
|
@@ -338,38 +384,54 @@ def create_datasets(
|
|
|
338
384
|
)
|
|
339
385
|
|
|
340
386
|
if logical_source == "imap_lo_l1c_pset":
|
|
341
|
-
|
|
342
|
-
data=
|
|
343
|
-
name="
|
|
344
|
-
dims=["
|
|
345
|
-
attrs=attr_mgr.get_variable_attributes("
|
|
346
|
-
)
|
|
347
|
-
pointing_bins = xr.DataArray(
|
|
348
|
-
data=np.arange(3600),
|
|
349
|
-
name="pointing_bins",
|
|
350
|
-
dims=["pointing_bins"],
|
|
351
|
-
attrs=attr_mgr.get_variable_attributes("pointing_bins"),
|
|
387
|
+
esa_energy_step = xr.DataArray(
|
|
388
|
+
data=ESA_ENERGY_STEPS,
|
|
389
|
+
name="esa_energy_step",
|
|
390
|
+
dims=["esa_energy_step"],
|
|
391
|
+
attrs=attr_mgr.get_variable_attributes("esa_energy_step"),
|
|
352
392
|
)
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
esa_step.values.astype(str),
|
|
393
|
+
esa_energy_step_label = xr.DataArray(
|
|
394
|
+
esa_energy_step.values.astype(str),
|
|
356
395
|
name="esa_step_label",
|
|
357
396
|
dims=["esa_step_label"],
|
|
358
397
|
attrs=attr_mgr.get_variable_attributes("esa_step_label"),
|
|
359
398
|
)
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
399
|
+
|
|
400
|
+
spin_angle = xr.DataArray(
|
|
401
|
+
data=SPIN_ANGLE_BIN_CENTERS,
|
|
402
|
+
name="spin_angle",
|
|
403
|
+
dims=["spin_angle"],
|
|
404
|
+
attrs=attr_mgr.get_variable_attributes("spin_angle"),
|
|
405
|
+
)
|
|
406
|
+
spin_angle_label = xr.DataArray(
|
|
407
|
+
spin_angle.values.astype(str),
|
|
408
|
+
name="spin_angle_label",
|
|
409
|
+
dims=["spin_angle_label"],
|
|
410
|
+
attrs=attr_mgr.get_variable_attributes("spin_angle_label"),
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
off_angle = xr.DataArray(
|
|
414
|
+
data=OFF_ANGLE_BIN_CENTERS,
|
|
415
|
+
name="off_angle",
|
|
416
|
+
dims=["off_angle"],
|
|
417
|
+
attrs=attr_mgr.get_variable_attributes("off_angle"),
|
|
418
|
+
)
|
|
419
|
+
off_angle_label = xr.DataArray(
|
|
420
|
+
off_angle.values.astype(str),
|
|
421
|
+
name="off_angle_label",
|
|
422
|
+
dims=["off_angle_label"],
|
|
423
|
+
attrs=attr_mgr.get_variable_attributes("off_angle_label"),
|
|
365
424
|
)
|
|
425
|
+
|
|
366
426
|
dataset = xr.Dataset(
|
|
367
427
|
coords={
|
|
368
428
|
"epoch": epoch_time,
|
|
369
|
-
"
|
|
370
|
-
"
|
|
371
|
-
"
|
|
372
|
-
"
|
|
429
|
+
"esa_energy_step": esa_energy_step,
|
|
430
|
+
"esa_energy_step_label": esa_energy_step_label,
|
|
431
|
+
"spin_angle": spin_angle,
|
|
432
|
+
"spin_angle_label": spin_angle_label,
|
|
433
|
+
"off_angle": off_angle,
|
|
434
|
+
"off_angle_label": off_angle_label,
|
|
373
435
|
},
|
|
374
436
|
attrs=attr_mgr.get_global_attributes(logical_source),
|
|
375
437
|
)
|
|
@@ -389,30 +451,34 @@ def create_datasets(
|
|
|
389
451
|
|
|
390
452
|
# Create a data array for the current field and add it to the dataset
|
|
391
453
|
# TODO: TEMPORARY. need to update to use l1b data once that's available.
|
|
392
|
-
if field in [
|
|
454
|
+
if field in [
|
|
455
|
+
"pointing_start_met",
|
|
456
|
+
"pointing_end_met",
|
|
457
|
+
"esa_mode",
|
|
458
|
+
"pivot_angle",
|
|
459
|
+
]:
|
|
393
460
|
dataset[field] = xr.DataArray(
|
|
394
461
|
data=[1],
|
|
395
462
|
dims=dims,
|
|
396
463
|
attrs=attr_mgr.get_variable_attributes(field),
|
|
397
464
|
)
|
|
398
465
|
# TODO: This is temporary.
|
|
399
|
-
# The data type will be set in the data class when that's created
|
|
400
466
|
elif field == "exposure_time":
|
|
401
467
|
dataset[field] = xr.DataArray(
|
|
402
|
-
data=np.ones((1, 7), dtype=np.float16),
|
|
468
|
+
data=np.ones((1, 7, 3600, 40), dtype=np.float16),
|
|
403
469
|
dims=dims,
|
|
404
470
|
attrs=attr_mgr.get_variable_attributes(field),
|
|
405
471
|
)
|
|
406
472
|
|
|
407
|
-
elif "
|
|
473
|
+
elif "rates" in field:
|
|
408
474
|
dataset[field] = xr.DataArray(
|
|
409
|
-
data=np.ones(
|
|
475
|
+
data=np.ones(PSET_SHAPE, dtype=np.float16),
|
|
410
476
|
dims=dims,
|
|
411
477
|
attrs=attr_mgr.get_variable_attributes(field),
|
|
412
478
|
)
|
|
413
479
|
else:
|
|
414
480
|
dataset[field] = xr.DataArray(
|
|
415
|
-
data=np.ones(
|
|
481
|
+
data=np.ones(PSET_SHAPE, dtype=np.int16),
|
|
416
482
|
dims=dims,
|
|
417
483
|
attrs=attr_mgr.get_variable_attributes(field),
|
|
418
484
|
)
|
imap_processing/lo/l2/lo_l2.py
CHANGED
|
@@ -5,12 +5,13 @@ import xarray as xr
|
|
|
5
5
|
|
|
6
6
|
from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
|
|
7
7
|
from imap_processing.ena_maps import ena_maps
|
|
8
|
-
from imap_processing.ena_maps.ena_maps import RectangularSkyMap
|
|
9
|
-
from imap_processing.
|
|
10
|
-
from imap_processing.spice.geometry import SpiceFrame
|
|
8
|
+
from imap_processing.ena_maps.ena_maps import AbstractSkyMap, RectangularSkyMap
|
|
9
|
+
from imap_processing.ena_maps.utils.naming import MapDescriptor
|
|
11
10
|
|
|
12
11
|
|
|
13
|
-
def lo_l2(
|
|
12
|
+
def lo_l2(
|
|
13
|
+
sci_dependencies: dict, anc_dependencies: list, descriptor: str
|
|
14
|
+
) -> list[xr.Dataset]:
|
|
14
15
|
"""
|
|
15
16
|
Will process IMAP-Lo L1C data into Le CDF data products.
|
|
16
17
|
|
|
@@ -20,6 +21,8 @@ def lo_l2(sci_dependencies: dict, anc_dependencies: list) -> list[xr.Dataset]:
|
|
|
20
21
|
Dictionary of datasets needed for L2 data product creation in xarray Datasets.
|
|
21
22
|
anc_dependencies : list
|
|
22
23
|
Ancillary files needed for L2 data product creation.
|
|
24
|
+
descriptor : str
|
|
25
|
+
The map descriptor to be produced.
|
|
23
26
|
|
|
24
27
|
Returns
|
|
25
28
|
-------
|
|
@@ -37,18 +40,19 @@ def lo_l2(sci_dependencies: dict, anc_dependencies: list) -> list[xr.Dataset]:
|
|
|
37
40
|
logical_source = "imap_lo_l2_l090-ena-h-sf-nsp-ram-hae-6deg-3mo"
|
|
38
41
|
psets = sci_dependencies["imap_lo_l1c_pset"]
|
|
39
42
|
|
|
40
|
-
# Create
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
# Create an AbstractSkyMap (Rectangular or HEALPIX) from the pointing set
|
|
44
|
+
lo_sky_map = project_pset_to_sky_map(psets, descriptor)
|
|
45
|
+
if not isinstance(lo_sky_map, RectangularSkyMap):
|
|
46
|
+
raise NotImplementedError("HEALPix map output not supported for Lo")
|
|
47
|
+
|
|
44
48
|
# Add the hydrogen rates to the rectangular map dataset.
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
lo_sky_map.data_1d["h_rate"] = calculate_rates(
|
|
50
|
+
lo_sky_map.data_1d["h_counts"], lo_sky_map.data_1d["exposure_time"]
|
|
47
51
|
)
|
|
48
52
|
# Add the hydrogen flux to the rectangular map dataset.
|
|
49
|
-
|
|
53
|
+
lo_sky_map.data_1d["h_flux"] = calculate_fluxes(lo_sky_map.data_1d["h_rate"])
|
|
50
54
|
# Create the dataset from the rectangular map.
|
|
51
|
-
lo_rect_map_ds =
|
|
55
|
+
lo_rect_map_ds = lo_sky_map.to_dataset()
|
|
52
56
|
# Add the attributes to the dataset.
|
|
53
57
|
lo_rect_map_ds = add_attributes(
|
|
54
58
|
lo_rect_map_ds, attr_mgr, logical_source=logical_source
|
|
@@ -57,41 +61,37 @@ def lo_l2(sci_dependencies: dict, anc_dependencies: list) -> list[xr.Dataset]:
|
|
|
57
61
|
return [lo_rect_map_ds]
|
|
58
62
|
|
|
59
63
|
|
|
60
|
-
def
|
|
61
|
-
psets: list[xr.Dataset], spacing_deg: int, spice_frame: SpiceFrame
|
|
62
|
-
) -> RectangularSkyMap:
|
|
64
|
+
def project_pset_to_sky_map(psets: list[xr.Dataset], descriptor: str) -> AbstractSkyMap:
|
|
63
65
|
"""
|
|
64
|
-
Project the pointing set to a
|
|
66
|
+
Project the pointing set to a sky map.
|
|
65
67
|
|
|
66
|
-
This function is used to create a
|
|
68
|
+
This function is used to create a sky map from the pointing set
|
|
67
69
|
data in the L1C dataset.
|
|
68
70
|
|
|
69
71
|
Parameters
|
|
70
72
|
----------
|
|
71
73
|
psets : list[xr.Dataset]
|
|
72
74
|
List of pointing sets in xarray Dataset format.
|
|
73
|
-
|
|
74
|
-
The
|
|
75
|
-
|
|
76
|
-
The SPICE frame to use for the rectangular sky map projection.
|
|
75
|
+
descriptor : str
|
|
76
|
+
The map descriptor for the map to be produced,
|
|
77
|
+
contains details about the map projection.
|
|
77
78
|
|
|
78
79
|
Returns
|
|
79
80
|
-------
|
|
80
|
-
|
|
81
|
-
The
|
|
81
|
+
AbstractSkyMap
|
|
82
|
+
The sky map created from the pointing set data.
|
|
82
83
|
"""
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
)
|
|
84
|
+
map_descriptor = MapDescriptor.from_string(descriptor)
|
|
85
|
+
output_map = map_descriptor.to_empty_map()
|
|
86
|
+
|
|
87
87
|
for pset in psets:
|
|
88
88
|
lo_pset = ena_maps.LoPointingSet(pset)
|
|
89
|
-
|
|
89
|
+
output_map.project_pset_values_to_map(
|
|
90
90
|
pointing_set=lo_pset,
|
|
91
91
|
value_keys=["h_counts", "exposure_time"],
|
|
92
92
|
index_match_method=ena_maps.IndexMatchMethod.PUSH,
|
|
93
93
|
)
|
|
94
|
-
return
|
|
94
|
+
return output_map
|
|
95
95
|
|
|
96
96
|
|
|
97
97
|
def calculate_rates(counts: xr.DataArray, exposure_time: xr.DataArray) -> xr.DataArray:
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Ancillary file reading for IMAP-Lo processing."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
# convert the YYYYDDD datetime format directly upon reading
|
|
8
|
+
_CONVERTERS = {
|
|
9
|
+
"YYYYDDD": lambda x: pd.to_datetime(str(x), format="%Y%j"),
|
|
10
|
+
"#YYYYDDD": lambda x: pd.to_datetime(str(x), format="%Y%j"),
|
|
11
|
+
"YYYYDDD_strt": lambda x: pd.to_datetime(str(x), format="%Y%j"),
|
|
12
|
+
"YYYYDDD_end": lambda x: pd.to_datetime(str(x), format="%Y%j"),
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
# Columns in the csv files to rename for consistency
|
|
16
|
+
_RENAME_COLUMNS = {
|
|
17
|
+
"YYYYDDD": "Date",
|
|
18
|
+
"#YYYYDDD": "Date",
|
|
19
|
+
"#Comments": "Comments",
|
|
20
|
+
"YYYYDDD_strt": "StartDate",
|
|
21
|
+
"YYYYDDD_end": "EndDate",
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def read_ancillary_file(ancillary_file: str | Path) -> pd.DataFrame:
|
|
26
|
+
"""
|
|
27
|
+
Read a generic ancillary CSV file into a pandas DataFrame.
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
ancillary_file : str or Path
|
|
32
|
+
Path to the ancillary CSV file.
|
|
33
|
+
|
|
34
|
+
Returns
|
|
35
|
+
-------
|
|
36
|
+
pd.DataFrame
|
|
37
|
+
DataFrame containing the ancillary data.
|
|
38
|
+
"""
|
|
39
|
+
skiprows = None
|
|
40
|
+
if "esa-mode-lut" in str(ancillary_file):
|
|
41
|
+
# skip the first row which is a comment
|
|
42
|
+
skiprows = [0]
|
|
43
|
+
elif "geometric-factor" in str(ancillary_file):
|
|
44
|
+
# skip the rows with comment headers indicating Hi_Res and Hi_Thr
|
|
45
|
+
skiprows = [1, 38]
|
|
46
|
+
df = pd.read_csv(ancillary_file, converters=_CONVERTERS, skiprows=skiprows)
|
|
47
|
+
df = df.rename(columns=_RENAME_COLUMNS)
|
|
48
|
+
|
|
49
|
+
if "geometric-factor" in str(ancillary_file):
|
|
50
|
+
# Add an ESA mode column based on the known structure of the file.
|
|
51
|
+
# The first 36 rows are ESA mode 0 (HiRes), the second 36 are ESA mode 1 (HiThr)
|
|
52
|
+
df["esa_mode"] = 0
|
|
53
|
+
df.loc[36:, "esa_mode"] = 1
|
|
54
|
+
|
|
55
|
+
return df
|