Skip to content

Commit cbff0a3

Browse files
authored
Allow customization of MAG L2 frames (#2881)
* feat: Allow customization of MAG L2 frames - in mag_l2 function and update tests accordingly - used (externally) by MAG team to generate l2 files without needing all files * chore: QA fixes * docs: Add docs for new parameter in MAG L2
1 parent e2b9a69 commit cbff0a3

2 files changed

Lines changed: 64 additions & 23 deletions

File tree

imap_processing/mag/l2/mag_l2.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,22 @@
1212

1313
logger = logging.getLogger(__name__)
1414

15+
DEFAULT_L2_FRAMES = [
16+
ValidFrames.SRF,
17+
ValidFrames.GSE,
18+
ValidFrames.GSM,
19+
ValidFrames.RTN,
20+
ValidFrames.DSRF, # should be last as some vectors may become NaN
21+
]
22+
1523

1624
def mag_l2(
1725
calibration_dataset: xr.Dataset,
1826
offsets_dataset: xr.Dataset,
1927
input_data: xr.Dataset,
2028
day_to_process: np.datetime64,
2129
mode: DataMode = DataMode.NORM,
30+
frames: list[ValidFrames] = DEFAULT_L2_FRAMES,
2231
) -> list[xr.Dataset]:
2332
"""
2433
Complete MAG L2 processing.
@@ -70,6 +79,9 @@ def mag_l2(
7079
mode : DataMode
7180
The data mode to process. Default is DataMode.NORM (normal mode).
7281
Can also be DataMode.BURST for burst mode processing.
82+
frames : list[ValidFrames]
83+
List of frames to output. DEFAULT_L2_FRAMES is [SRF, GSE, GSM, RTN, DSRF]
84+
Note that DSRF should be last as some vectors may become NaN after rotation.
7385
7486
Returns
7587
-------
@@ -79,6 +91,9 @@ def mag_l2(
7991
"""
8092
always_output_mago = configuration.ALWAYS_OUTPUT_MAGO
8193

94+
if not frames:
95+
frames = DEFAULT_L2_FRAMES
96+
8297
# TODO Check that the input file matches the offsets file
8398
if not np.array_equal(input_data["epoch"].data, offsets_dataset["epoch"].data):
8499
raise ValueError("Input file and offsets file must have the same timestamps.")
@@ -118,19 +133,13 @@ def mag_l2(
118133
attributes.add_instrument_variable_attrs("mag", "l2")
119134

120135
# Rotate from the MAG frame into the SRF frame
121-
frames: list[xr.Dataset] = []
122-
123-
for frame in [
124-
ValidFrames.SRF,
125-
ValidFrames.GSE,
126-
ValidFrames.GSM,
127-
ValidFrames.RTN,
128-
ValidFrames.DSRF, # should be last as some vectors may become NaN
129-
]:
136+
datasets: list[xr.Dataset] = []
137+
138+
for frame in frames:
130139
l2_data.rotate_frame(frame)
131-
frames.append(l2_data.generate_dataset(attributes, day))
140+
datasets.append(l2_data.generate_dataset(attributes, day))
132141

133-
return frames
142+
return datasets
134143

135144

136145
def retrieve_matrix_from_l2_calibration(

imap_processing/tests/mag/test_mag_l2.py

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,29 @@
1818
from imap_processing.tests.mag.conftest import mag_l1a_dataset_generator
1919

2020

21-
@pytest.mark.parametrize("data_mode", ["norm", "burst"])
22-
def test_mag_l2_attributes(norm_dataset, mag_test_l2_data, data_mode):
23-
"""Test that L2 datasets have correct attributes based on frame and mode."""
21+
@pytest.mark.parametrize(
22+
"data_mode,frames,expected_frames",
23+
[
24+
(
25+
"norm",
26+
[
27+
ValidFrames.SRF,
28+
ValidFrames.GSE,
29+
ValidFrames.GSM,
30+
ValidFrames.RTN,
31+
ValidFrames.DSRF,
32+
],
33+
5,
34+
),
35+
("norm", [], 5),
36+
("burst", [ValidFrames.SRF], 1),
37+
("burst", [], 5),
38+
],
39+
)
40+
def test_mag_l2_attributes(
41+
norm_dataset, mag_test_l2_data, data_mode, frames, expected_frames
42+
):
43+
"""Test that correct L2 datasets have correct attributes based on frame and mode."""
2444
calibration_dataset = mag_test_l2_data[0]
2545
offset_dataset = mag_test_l2_data[1]
2646

@@ -35,18 +55,30 @@ def test_mag_l2_attributes(norm_dataset, mag_test_l2_data, data_mode):
3555
"imap_processing.mag.l2.mag_l2_data.frame_transform",
3656
side_effect=lambda *args, **kwargs: args[1],
3757
):
38-
l2_datasets = mag_l2(
39-
calibration_dataset,
40-
offset_dataset,
41-
test_dataset,
42-
np.datetime64("2025-10-17"),
43-
mode=mode,
44-
)
58+
if frames:
59+
# ensure when a subset of frames is needed only those are generated
60+
l2_datasets = mag_l2(
61+
calibration_dataset,
62+
offset_dataset,
63+
test_dataset,
64+
np.datetime64("2025-10-17"),
65+
mode=mode,
66+
frames=frames,
67+
)
68+
else:
69+
# be default all frames are generated
70+
l2_datasets = mag_l2(
71+
calibration_dataset,
72+
offset_dataset,
73+
test_dataset,
74+
np.datetime64("2025-10-17"),
75+
mode=mode,
76+
)
4577

4678
# Verify we have the expected number of datasets
4779
# L2 produces 5 frames: SRF, GSE, GSM, RTN, DSRF
48-
assert len(l2_datasets) == 5, (
49-
f"Expected 5 {data_mode} datasets, got {len(l2_datasets)}"
80+
assert len(l2_datasets) == expected_frames, (
81+
f"Expected {expected_frames} {data_mode} datasets, got {len(l2_datasets)}"
5082
)
5183

5284
for dataset in l2_datasets:

0 commit comments

Comments
 (0)