Skip to content

Commit a0554e2

Browse files
authored
MNT: Separate out l1b dataset logic into individual functions (IMAP-Science-Operations-Center#2601)
The l1b dataset logic was within the if-blocks based on what dependency data was passed to the routine. This can be determined based on the descriptor requested and it can also be broken up into individual helper routines where the main code logic can be more easily identified and separated.
1 parent 5c619ad commit a0554e2

3 files changed

Lines changed: 159 additions & 96 deletions

File tree

imap_processing/cli.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1026,7 +1026,7 @@ def do_processing(
10261026
for file in science_files:
10271027
dataset = load_cdf(file)
10281028
data_dict[dataset.attrs["Logical_source"]] = dataset
1029-
datasets = lo_l1b.lo_l1b(data_dict, ancillary_files)
1029+
datasets = lo_l1b.lo_l1b(data_dict, ancillary_files, self.descriptor)
10301030

10311031
elif self.data_level == "l1c":
10321032
data_dict = {}

imap_processing/lo/l1b/lo_l1b.py

Lines changed: 147 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@
3434
logger = logging.getLogger(__name__)
3535

3636

37-
def lo_l1b(sci_dependencies: dict, anc_dependencies: list) -> list[Path]:
37+
def lo_l1b(
38+
sci_dependencies: dict, anc_dependencies: list, descriptor: str
39+
) -> list[Path]:
3840
"""
3941
Will process IMAP-Lo L1A data into L1B CDF data products.
4042
@@ -44,6 +46,8 @@ def lo_l1b(sci_dependencies: dict, anc_dependencies: list) -> list[Path]:
4446
Dictionary of datasets needed for L1B data product creation in xarray Datasets.
4547
anc_dependencies : list
4648
List of ancillary file paths needed for L1B data product creation.
49+
descriptor : str
50+
Determines which datasets are produced.
4751
4852
Returns
4953
-------
@@ -61,109 +65,159 @@ def lo_l1b(sci_dependencies: dict, anc_dependencies: list) -> list[Path]:
6165

6266
datasets_to_return = []
6367

64-
badtimes_ds = create_badtimes_dataset()
65-
if badtimes_ds.data_vars:
66-
# If it was an empty dataset, then we don't want to
68+
if descriptor == "badtimes":
69+
logger.info("\nProcessing IMAP-Lo L1B Bad Times...")
70+
badtimes_ds = create_badtimes_dataset()
6771
badtimes_ds.attrs = attr_mgr_l1b.get_global_attributes("imap_lo_l1b_badtimes")
68-
datasets_to_return.append(badtimes_ds)
72+
if len(badtimes_ds["epoch"]) > 0:
73+
# Only add the dataset if there are bad times added
74+
datasets_to_return.append(badtimes_ds)
6975

7076
# if the dependencies are used to create Annotated Direct Events
71-
if "imap_lo_l1a_de" in sci_dependencies and "imap_lo_l1a_spin" in sci_dependencies:
77+
if descriptor == "de":
7278
logger.info("\nProcessing IMAP-Lo L1B Direct Events...")
73-
logical_source = "imap_lo_l1b_de"
74-
# get the dependency dataset for l1b direct events
75-
l1a_de = sci_dependencies["imap_lo_l1a_de"]
76-
spin_data = sci_dependencies["imap_lo_l1a_spin"]
77-
78-
# Initialize the L1B DE dataset
79-
l1b_de = initialize_l1b_de(l1a_de, attr_mgr_l1b, logical_source)
80-
pointing_start_met, pointing_end_met = get_pointing_times(
81-
l1a_de["met"].values[0].item()
82-
)
83-
84-
# Get the average spin durations for each epoch
85-
avg_spin_durations_per_cycle = get_avg_spin_durations_per_cycle(spin_data)
86-
# set the spin cycle for each direct event
87-
l1b_de = set_spin_cycle(pointing_start_met, l1a_de, l1b_de)
88-
# get spin start times for each event
89-
spin_start_time = get_spin_start_times(l1a_de, l1b_de, spin_data)
90-
91-
# get the absolute met for each event
92-
l1b_de = set_event_met(
93-
l1a_de, l1b_de, spin_start_time, avg_spin_durations_per_cycle
94-
)
95-
# set the epoch for each event
96-
l1b_de = set_each_event_epoch(l1b_de)
97-
# Set the ESA mode for each direct event
98-
l1b_de = set_esa_mode(
99-
pointing_start_met, pointing_end_met, anc_dependencies, l1b_de
100-
)
101-
# Set the average spin duration for each direct event
102-
l1b_de = set_avg_spin_durations_per_event(
103-
l1a_de, l1b_de, avg_spin_durations_per_cycle
104-
)
105-
# calculate the TOF1 for golden triples
106-
# store in the l1a dataset to use in l1b calculations
107-
l1a_de = calculate_tof1_for_golden_triples(l1a_de)
108-
# set the coincidence type string for each direct event
109-
l1b_de = set_coincidence_type(l1a_de, l1b_de, attr_mgr_l1a)
110-
# convert the TOFs to engineering units
111-
l1b_de = convert_tofs_to_eu(l1a_de, l1b_de, attr_mgr_l1a, attr_mgr_l1b)
112-
# set the species for each direct event
113-
l1b_de = identify_species(l1b_de)
114-
# set the pointing direction for each direct event
115-
l1b_de = set_pointing_direction(l1b_de)
116-
# calculate and set the pointing bin based on the spin phase
117-
# pointing bin is 3600 x 40 bins
118-
l1b_de = set_pointing_bin(l1b_de)
119-
# set the badtimes
120-
l1b_de = set_bad_times(l1b_de, anc_dependencies)
121-
datasets_to_return.append(l1b_de)
79+
ds = l1b_de(sci_dependencies, anc_dependencies, attr_mgr_l1b, attr_mgr_l1a)
80+
datasets_to_return.append(ds)
12281

12382
# If dependencies are used to create Histogram Rates
124-
if (
125-
"imap_lo_l1a_histogram" in sci_dependencies
126-
and "imap_lo_l1a_spin" in sci_dependencies
127-
):
83+
if descriptor == "histrates":
12884
logger.info("\nProcessing IMAP-Lo L1B Histogram Rates...")
129-
logical_source = "imap_lo_l1b_histrates"
130-
# get the dependency dataset for l1b histogram rates
131-
l1a_hist = sci_dependencies["imap_lo_l1a_histogram"]
132-
spin_data = sci_dependencies["imap_lo_l1a_spin"]
133-
# initialize the L1B Histogram Rates dataset from the L1A Histogram Rates
134-
# This carries over the epoch and count fields from L1A
135-
l1b_histrates = initialize_l1b_histrates(l1a_hist, attr_mgr_l1b, logical_source)
136-
# set spin cycle and remove invalid spin ASCs
137-
l1b_histrates = set_spin_cycle_from_spin_data(
138-
l1a_hist, l1b_histrates, spin_data
139-
)
140-
141-
pointing_start_met, pointing_end_met = get_pointing_times(
142-
ttj2000ns_to_met(l1a_hist["epoch"].values[0].item())
143-
)
144-
l1b_histrates = set_esa_mode(
145-
pointing_start_met, pointing_end_met, anc_dependencies, l1b_histrates
146-
)
147-
# resweep the histogram data
148-
l1b_histrates, exposure_factor = resweep_histogram_data(
149-
l1b_histrates, anc_dependencies
150-
)
151-
# Get the start and end times for each spin epoch
152-
acq_start, acq_end = convert_start_end_acq_times(spin_data)
153-
# Get the average spin durations for each epoch
154-
avg_spin_durations_per_cycle = get_avg_spin_durations_per_cycle(spin_data)
155-
l1b_histrates = calculate_histogram_rates(
156-
l1b_histrates,
157-
acq_start,
158-
acq_end,
159-
avg_spin_durations_per_cycle,
160-
exposure_factor,
161-
)
162-
datasets_to_return.append(l1b_histrates)
85+
ds = l1b_histrates(sci_dependencies, anc_dependencies, attr_mgr_l1b)
86+
datasets_to_return.append(ds)
16387

16488
return datasets_to_return
16589

16690

91+
def l1b_de(
92+
sci_dependencies: dict,
93+
anc_dependencies: list,
94+
attr_mgr_l1b: ImapCdfAttributes,
95+
attr_mgr_l1a: ImapCdfAttributes,
96+
) -> xr.Dataset:
97+
"""
98+
Create the IMAP-Lo L1B Direct Events dataset.
99+
100+
Parameters
101+
----------
102+
sci_dependencies : dict
103+
Dictionary of datasets needed for L1B data product creation in xarray Datasets.
104+
anc_dependencies : list
105+
List of ancillary file paths needed for L1B data product creation.
106+
attr_mgr_l1b : ImapCdfAttributes
107+
Attribute manager used to get the global attributes.
108+
attr_mgr_l1a : ImapCdfAttributes
109+
Attribute manager used to get the variable attributes.
110+
111+
Returns
112+
-------
113+
l1b_de : xr.Dataset
114+
The IMAP-Lo L1B Direct Events dataset.
115+
"""
116+
logical_source = "imap_lo_l1b_de"
117+
# get the dependency dataset for l1b direct events
118+
l1a_de = sci_dependencies["imap_lo_l1a_de"]
119+
spin_data = sci_dependencies["imap_lo_l1a_spin"]
120+
121+
# Initialize the L1B DE dataset
122+
l1b_de = initialize_l1b_de(l1a_de, attr_mgr_l1b, logical_source)
123+
pointing_start_met, pointing_end_met = get_pointing_times(
124+
l1a_de["met"].values[0].item()
125+
)
126+
127+
# Get the average spin durations for each epoch
128+
avg_spin_durations_per_cycle = get_avg_spin_durations_per_cycle(spin_data)
129+
# set the spin cycle for each direct event
130+
l1b_de = set_spin_cycle(pointing_start_met, l1a_de, l1b_de)
131+
# get spin start times for each event
132+
spin_start_time = get_spin_start_times(l1a_de, l1b_de, spin_data)
133+
134+
# get the absolute met for each event
135+
l1b_de = set_event_met(
136+
l1a_de, l1b_de, spin_start_time, avg_spin_durations_per_cycle
137+
)
138+
# set the epoch for each event
139+
l1b_de = set_each_event_epoch(l1b_de)
140+
# Set the ESA mode for each direct event
141+
l1b_de = set_esa_mode(
142+
pointing_start_met, pointing_end_met, anc_dependencies, l1b_de
143+
)
144+
# Set the average spin duration for each direct event
145+
l1b_de = set_avg_spin_durations_per_event(
146+
l1a_de, l1b_de, avg_spin_durations_per_cycle
147+
)
148+
# calculate the TOF1 for golden triples
149+
# store in the l1a dataset to use in l1b calculations
150+
l1a_de = calculate_tof1_for_golden_triples(l1a_de)
151+
# set the coincidence type string for each direct event
152+
l1b_de = set_coincidence_type(l1a_de, l1b_de, attr_mgr_l1a)
153+
# convert the TOFs to engineering units
154+
l1b_de = convert_tofs_to_eu(l1a_de, l1b_de, attr_mgr_l1a, attr_mgr_l1b)
155+
# set the species for each direct event
156+
l1b_de = identify_species(l1b_de)
157+
# set the pointing direction for each direct event
158+
l1b_de = set_pointing_direction(l1b_de)
159+
# calculate and set the pointing bin based on the spin phase
160+
# pointing bin is 3600 x 40 bins
161+
l1b_de = set_pointing_bin(l1b_de)
162+
# set the badtimes
163+
l1b_de = set_bad_times(l1b_de, anc_dependencies)
164+
return l1b_de
165+
166+
167+
def l1b_histrates(
168+
sci_dependencies: dict, anc_dependencies: list, attr_mgr_l1b: ImapCdfAttributes
169+
) -> xr.Dataset:
170+
"""
171+
Create the IMAP-Lo L1B Histogram Rates dataset.
172+
173+
Parameters
174+
----------
175+
sci_dependencies : dict
176+
Dictionary of datasets needed for L1B data product creation in xarray Datasets.
177+
anc_dependencies : list
178+
List of ancillary file paths needed for L1B data product creation.
179+
attr_mgr_l1b : ImapCdfAttributes
180+
Attribute manager used to get the global attributes.
181+
182+
Returns
183+
-------
184+
l1b_histrates : xr.Dataset
185+
The IMAP-Lo L1B Histogram Rates dataset.
186+
"""
187+
logical_source = "imap_lo_l1b_histrates"
188+
# get the dependency dataset for l1b histogram rates
189+
l1a_hist = sci_dependencies["imap_lo_l1a_histogram"]
190+
spin_data = sci_dependencies["imap_lo_l1a_spin"]
191+
# initialize the L1B Histogram Rates dataset from the L1A Histogram Rates
192+
# This carries over the epoch and count fields from L1A
193+
l1b_histrates = initialize_l1b_histrates(l1a_hist, attr_mgr_l1b, logical_source)
194+
# set spin cycle and remove invalid spin ASCs
195+
l1b_histrates = set_spin_cycle_from_spin_data(l1a_hist, l1b_histrates, spin_data)
196+
197+
pointing_start_met, pointing_end_met = get_pointing_times(
198+
ttj2000ns_to_met(l1a_hist["epoch"].values[0].item())
199+
)
200+
l1b_histrates = set_esa_mode(
201+
pointing_start_met, pointing_end_met, anc_dependencies, l1b_histrates
202+
)
203+
# resweep the histogram data
204+
l1b_histrates, exposure_factor = resweep_histogram_data(
205+
l1b_histrates, anc_dependencies
206+
)
207+
# Get the start and end times for each spin epoch
208+
acq_start, acq_end = convert_start_end_acq_times(spin_data)
209+
# Get the average spin durations for each epoch
210+
avg_spin_durations_per_cycle = get_avg_spin_durations_per_cycle(spin_data)
211+
l1b_histrates = calculate_histogram_rates(
212+
l1b_histrates,
213+
acq_start,
214+
acq_end,
215+
avg_spin_durations_per_cycle,
216+
exposure_factor,
217+
)
218+
return l1b_histrates
219+
220+
167221
def initialize_l1b_de(
168222
l1a_de: xr.Dataset, attr_mgr_l1b: ImapCdfAttributes, logical_source: str
169223
) -> xr.Dataset:

imap_processing/tests/lo/test_lo_l1b.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ def test_lo_l1b_de(
174174
expected_logical_source_de = "imap_lo_l1b_de"
175175

176176
# Act
177-
output_files = lo_l1b(data, anc_dependencies)
177+
output_files = lo_l1b(data, anc_dependencies, descriptor="de")
178178

179179
# Assert
180180
assert expected_logical_source_de == output_files[-1].attrs["Logical_source"]
@@ -210,7 +210,7 @@ def test_lo_l1b_histogram_rates(
210210
}
211211

212212
# Act
213-
l1b_datasets = lo_l1b(sci_dependencies, anc_dependencies)
213+
l1b_datasets = lo_l1b(sci_dependencies, anc_dependencies, descriptor="histrates")
214214

215215
# Assert
216216
assert "h_rates" in l1b_datasets[-1].data_vars
@@ -823,6 +823,15 @@ def test_badtimes_with_spin(spice_test_data_path, use_test_spin_data_csv):
823823
)
824824
np.testing.assert_array_equal(badtimes_ds["badtime_flag"], 1)
825825

826+
# There should be a dataset returned from the main code in this case
827+
datasets = lo_l1b({}, [], descriptor="badtimes")
828+
assert len(datasets) == 1
829+
830+
831+
def test_l1b_badtimes_skipped_if_empty():
832+
datasets = lo_l1b({}, [], descriptor="badtimes")
833+
assert len(datasets) == 0
834+
826835

827836
def test_resweep_histogram_success(anc_dependencies):
828837
# Arrange

0 commit comments

Comments
 (0)