imap-processing 1.0.1__py3-none-any.whl → 1.0.3__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.
- imap_processing/_version.py +2 -2
- imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +18 -0
- imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +101 -258
- imap_processing/cdf/config/imap_enamaps_l2-common_variable_attrs.yaml +1 -1
- imap_processing/cdf/config/imap_hi_variable_attrs.yaml +12 -2
- imap_processing/cdf/config/imap_idex_global_cdf_attrs.yaml +1 -8
- imap_processing/cdf/config/imap_idex_l1b_variable_attrs.yaml +16 -5
- imap_processing/cdf/config/imap_idex_l2a_variable_attrs.yaml +27 -25
- imap_processing/cdf/config/imap_idex_l2b_variable_attrs.yaml +16 -16
- imap_processing/cdf/config/imap_idex_l2c_variable_attrs.yaml +2 -2
- imap_processing/cdf/config/imap_swapi_variable_attrs.yaml +2 -13
- imap_processing/cdf/config/imap_ultra_l1c_variable_attrs.yaml +12 -0
- imap_processing/cdf/utils.py +2 -2
- imap_processing/cli.py +4 -16
- imap_processing/codice/codice_l1a_lo_angular.py +362 -0
- imap_processing/codice/codice_l1a_lo_species.py +282 -0
- imap_processing/codice/codice_l1b.py +80 -97
- imap_processing/codice/codice_l2.py +270 -103
- imap_processing/codice/codice_new_l1a.py +64 -0
- imap_processing/codice/constants.py +37 -2
- imap_processing/codice/utils.py +270 -0
- imap_processing/ena_maps/ena_maps.py +51 -39
- imap_processing/ena_maps/utils/corrections.py +196 -14
- imap_processing/ena_maps/utils/naming.py +3 -1
- imap_processing/hi/hi_l1c.py +57 -19
- imap_processing/hi/hi_l2.py +89 -36
- imap_processing/ialirt/calculate_ingest.py +19 -1
- imap_processing/ialirt/constants.py +12 -6
- imap_processing/ialirt/generate_coverage.py +6 -1
- imap_processing/ialirt/l0/parse_mag.py +1 -0
- imap_processing/ialirt/l0/process_hit.py +1 -0
- imap_processing/ialirt/l0/process_swapi.py +1 -0
- imap_processing/ialirt/l0/process_swe.py +2 -0
- imap_processing/ialirt/process_ephemeris.py +6 -2
- imap_processing/ialirt/utils/create_xarray.py +3 -2
- imap_processing/lo/l1b/lo_l1b.py +12 -2
- imap_processing/lo/l1c/lo_l1c.py +4 -4
- imap_processing/lo/l2/lo_l2.py +101 -8
- imap_processing/quality_flags.py +1 -0
- imap_processing/swapi/constants.py +4 -0
- imap_processing/swapi/l1/swapi_l1.py +47 -20
- imap_processing/swapi/l2/swapi_l2.py +17 -3
- imap_processing/ultra/l1a/ultra_l1a.py +121 -72
- imap_processing/ultra/l1b/de.py +57 -1
- imap_processing/ultra/l1b/ultra_l1b_annotated.py +0 -1
- imap_processing/ultra/l1b/ultra_l1b_extended.py +24 -11
- imap_processing/ultra/l1c/helio_pset.py +34 -8
- imap_processing/ultra/l1c/l1c_lookup_utils.py +4 -2
- imap_processing/ultra/l1c/spacecraft_pset.py +13 -7
- imap_processing/ultra/l1c/ultra_l1c.py +6 -6
- imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +79 -20
- imap_processing/ultra/l2/ultra_l2.py +2 -2
- imap_processing/ultra/utils/ultra_l1_utils.py +6 -0
- {imap_processing-1.0.1.dist-info → imap_processing-1.0.3.dist-info}/METADATA +1 -1
- {imap_processing-1.0.1.dist-info → imap_processing-1.0.3.dist-info}/RECORD +58 -54
- {imap_processing-1.0.1.dist-info → imap_processing-1.0.3.dist-info}/LICENSE +0 -0
- {imap_processing-1.0.1.dist-info → imap_processing-1.0.3.dist-info}/WHEEL +0 -0
- {imap_processing-1.0.1.dist-info → imap_processing-1.0.3.dist-info}/entry_points.txt +0 -0
|
@@ -26,11 +26,16 @@ from imap_processing.codice.constants import (
|
|
|
26
26
|
L2_GEOMETRIC_FACTOR,
|
|
27
27
|
L2_HI_NUMBER_OF_SSD,
|
|
28
28
|
L2_HI_SECTORED_ANGLE,
|
|
29
|
+
LO_NSW_ANGULAR_VARIABLE_NAMES,
|
|
29
30
|
LO_NSW_SPECIES_VARIABLE_NAMES,
|
|
31
|
+
LO_POSITION_TO_ELEVATION_ANGLE,
|
|
32
|
+
LO_SW_ANGULAR_VARIABLE_NAMES,
|
|
30
33
|
LO_SW_PICKUP_ION_SPECIES_VARIABLE_NAMES,
|
|
31
|
-
|
|
34
|
+
LO_SW_SOLAR_WIND_SPECIES_VARIABLE_NAMES,
|
|
32
35
|
NSW_POSITIONS,
|
|
36
|
+
PIXEL_ORIENTATIONS,
|
|
33
37
|
PUI_POSITIONS,
|
|
38
|
+
SOLAR_WIND_POSITIONS,
|
|
34
39
|
SW_POSITIONS,
|
|
35
40
|
)
|
|
36
41
|
|
|
@@ -94,7 +99,7 @@ def get_efficiency_lut(dependencies: ProcessingInputCollection) -> pd.DataFrame:
|
|
|
94
99
|
return pd.read_csv(dependencies.get_file_paths(descriptor="l2-lo-efficiency")[0])
|
|
95
100
|
|
|
96
101
|
|
|
97
|
-
def get_species_efficiency(species: str, efficiency: pd.DataFrame) ->
|
|
102
|
+
def get_species_efficiency(species: str, efficiency: pd.DataFrame) -> xr.DataArray:
|
|
98
103
|
"""
|
|
99
104
|
Get the efficiency values for a given species.
|
|
100
105
|
|
|
@@ -107,7 +112,7 @@ def get_species_efficiency(species: str, efficiency: pd.DataFrame) -> np.ndarray
|
|
|
107
112
|
|
|
108
113
|
Returns
|
|
109
114
|
-------
|
|
110
|
-
efficiency :
|
|
115
|
+
efficiency : xarray.DataArray
|
|
111
116
|
A 2D array of efficiencies with shape (epoch, esa_steps).
|
|
112
117
|
"""
|
|
113
118
|
species_efficiency = efficiency[efficiency["species"] == species].sort_values(
|
|
@@ -118,13 +123,16 @@ def get_species_efficiency(species: str, efficiency: pd.DataFrame) -> np.ndarray
|
|
|
118
123
|
[col for col in species_efficiency if col.startswith("position")],
|
|
119
124
|
key=lambda x: int(x.split("_")[-1]),
|
|
120
125
|
)
|
|
121
|
-
# Shape: (
|
|
122
|
-
return
|
|
126
|
+
# Shape: (energy_table, inst_az)
|
|
127
|
+
return xr.DataArray(
|
|
128
|
+
species_efficiency[position_names_sorted].to_numpy(),
|
|
129
|
+
dims=("energy_table", "inst_az"),
|
|
130
|
+
)
|
|
123
131
|
|
|
124
132
|
|
|
125
133
|
def compute_geometric_factors(
|
|
126
134
|
dataset: xr.Dataset, geometric_factor_lookup: dict
|
|
127
|
-
) ->
|
|
135
|
+
) -> xr.DataArray:
|
|
128
136
|
"""
|
|
129
137
|
Calculate geometric factors needed for intensity calculations.
|
|
130
138
|
|
|
@@ -148,7 +156,7 @@ def compute_geometric_factors(
|
|
|
148
156
|
|
|
149
157
|
Returns
|
|
150
158
|
-------
|
|
151
|
-
geometric_factors :
|
|
159
|
+
geometric_factors : xarray.DataArray
|
|
152
160
|
A 3D array of geometric factors with shape (epoch, esa_steps, positions).
|
|
153
161
|
"""
|
|
154
162
|
# Convert the HALF_SPIN_LUT to a reverse mapping of esa_step to half_spin
|
|
@@ -170,22 +178,26 @@ def compute_geometric_factors(
|
|
|
170
178
|
|
|
171
179
|
# Get the geometric factors based on the modes
|
|
172
180
|
gf = np.where(
|
|
173
|
-
modes[:, :, np.newaxis], # Shape (epoch,
|
|
174
|
-
geometric_factor_lookup[
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
181
|
+
modes[:, :, np.newaxis], # Shape (epoch, energy_table, 1)
|
|
182
|
+
geometric_factor_lookup[
|
|
183
|
+
"reduced"
|
|
184
|
+
], # Shape (1, energy_table, 24) - reduced mode
|
|
185
|
+
geometric_factor_lookup["full"], # Shape (1, energy_table, 24) - full mode
|
|
186
|
+
) # Shape: (epoch, energy_table, inst_az)
|
|
178
187
|
|
|
188
|
+
return xr.DataArray(gf, dims=("epoch", "energy_table", "inst_az"))
|
|
179
189
|
|
|
180
|
-
|
|
190
|
+
|
|
191
|
+
def calculate_intensity(
|
|
181
192
|
dataset: xr.Dataset,
|
|
182
193
|
species_list: list,
|
|
183
|
-
geometric_factors:
|
|
194
|
+
geometric_factors: xr.DataArray,
|
|
184
195
|
efficiency: pd.DataFrame,
|
|
185
196
|
positions: list,
|
|
197
|
+
average_across_positions: bool = False,
|
|
186
198
|
) -> xr.Dataset:
|
|
187
199
|
"""
|
|
188
|
-
|
|
200
|
+
Calculate species or angular intensities.
|
|
189
201
|
|
|
190
202
|
Parameters
|
|
191
203
|
----------
|
|
@@ -200,6 +212,9 @@ def process_lo_species_intensity(
|
|
|
200
212
|
positions : list
|
|
201
213
|
A list of position indices to select from the geometric factor and
|
|
202
214
|
efficiency lookup tables.
|
|
215
|
+
average_across_positions : bool
|
|
216
|
+
Whether to average the efficiencies and geometric factors across the selected
|
|
217
|
+
positions. Default is False.
|
|
203
218
|
|
|
204
219
|
Returns
|
|
205
220
|
-------
|
|
@@ -207,33 +222,207 @@ def process_lo_species_intensity(
|
|
|
207
222
|
The updated L2 dataset with species intensities calculated.
|
|
208
223
|
"""
|
|
209
224
|
# Select the relevant positions from the geometric factors
|
|
210
|
-
geometric_factors = geometric_factors
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
225
|
+
geometric_factors = geometric_factors.isel(inst_az=positions)
|
|
226
|
+
if average_across_positions:
|
|
227
|
+
# take the mean geometric factor across positions
|
|
228
|
+
geometric_factors = geometric_factors.mean(dim="inst_az")
|
|
229
|
+
scalar = len(positions)
|
|
230
|
+
else:
|
|
231
|
+
scalar = 1
|
|
232
|
+
# Calculate the angular intensities using the provided geometric factors and
|
|
233
|
+
# efficiency.
|
|
234
|
+
# intensity = species_rate / (gm * eff * esa_step) for position and spin angle
|
|
216
235
|
for species in species_list:
|
|
217
236
|
# Select the relevant positions for the species from the efficiency LUT
|
|
218
|
-
# Shape: (epoch,
|
|
219
|
-
species_eff = get_species_efficiency(species, efficiency)
|
|
220
|
-
|
|
221
|
-
|
|
237
|
+
# Shape: (epoch, energy_table, inst_az)
|
|
238
|
+
species_eff = get_species_efficiency(species, efficiency).isel(
|
|
239
|
+
inst_az=positions
|
|
240
|
+
)
|
|
222
241
|
if species_eff.size == 0:
|
|
223
|
-
logger.warning("No efficiency data found for species {species}. Skipping.")
|
|
242
|
+
logger.warning(f"No efficiency data found for species {species}. Skipping.")
|
|
224
243
|
continue
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
244
|
+
|
|
245
|
+
if average_across_positions:
|
|
246
|
+
# Take the mean efficiency across positions
|
|
247
|
+
species_eff = species_eff.mean(dim="inst_az")
|
|
248
|
+
|
|
249
|
+
# Shape: (epoch, energy_table, inst_az) or
|
|
250
|
+
# (epoch, energy_table) if averaged
|
|
251
|
+
denominator = scalar * geometric_factors * species_eff * dataset["energy_table"]
|
|
230
252
|
if species not in dataset:
|
|
231
253
|
logger.warning(
|
|
232
254
|
f"Species {species} not found in dataset. Filling with NaNS."
|
|
233
255
|
)
|
|
234
256
|
dataset[species] = np.full(dataset["energy_table"].data.shape, np.nan)
|
|
235
257
|
else:
|
|
236
|
-
dataset[species] = dataset[species] / denominator
|
|
258
|
+
dataset[species] = dataset[species] / denominator
|
|
259
|
+
|
|
260
|
+
# Also calculate uncertainty if available
|
|
261
|
+
species_uncertainty = f"unc_{species}"
|
|
262
|
+
if species_uncertainty not in dataset:
|
|
263
|
+
logger.warning(
|
|
264
|
+
f"Uncertainty {species_uncertainty} not found in dataset."
|
|
265
|
+
f" Filling with NaNS."
|
|
266
|
+
)
|
|
267
|
+
dataset[species_uncertainty] = np.full(
|
|
268
|
+
dataset["energy_table"].data.shape, np.nan
|
|
269
|
+
)
|
|
270
|
+
else:
|
|
271
|
+
dataset[species_uncertainty] = dataset[species_uncertainty] / denominator
|
|
272
|
+
|
|
273
|
+
return dataset
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def process_lo_species_intensity(
|
|
277
|
+
dataset: xr.Dataset,
|
|
278
|
+
species_list: list,
|
|
279
|
+
geometric_factors: xr.DataArray,
|
|
280
|
+
efficiency: pd.DataFrame,
|
|
281
|
+
positions: list,
|
|
282
|
+
) -> xr.Dataset:
|
|
283
|
+
"""
|
|
284
|
+
Process the lo-species L2 dataset to calculate species intensities.
|
|
285
|
+
|
|
286
|
+
Parameters
|
|
287
|
+
----------
|
|
288
|
+
dataset : xarray.Dataset
|
|
289
|
+
The L2 dataset to process.
|
|
290
|
+
species_list : list
|
|
291
|
+
List of species variable names to calculate intensity.
|
|
292
|
+
geometric_factors : xarray.DataArray
|
|
293
|
+
The geometric factors array with shape (epoch, esa_steps).
|
|
294
|
+
efficiency : pandas.DataFrame
|
|
295
|
+
The efficiency lookup table.
|
|
296
|
+
positions : list
|
|
297
|
+
A list of position indices to select from the geometric factor and
|
|
298
|
+
efficiency lookup tables.
|
|
299
|
+
|
|
300
|
+
Returns
|
|
301
|
+
-------
|
|
302
|
+
xarray.Dataset
|
|
303
|
+
The updated L2 dataset with species intensities calculated.
|
|
304
|
+
"""
|
|
305
|
+
# Calculate the species intensities using the provided geometric factors and
|
|
306
|
+
# efficiency.
|
|
307
|
+
dataset = calculate_intensity(
|
|
308
|
+
dataset,
|
|
309
|
+
species_list,
|
|
310
|
+
geometric_factors,
|
|
311
|
+
efficiency,
|
|
312
|
+
positions,
|
|
313
|
+
average_across_positions=True,
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
return dataset
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def process_lo_angular_intensity(
|
|
320
|
+
dataset: xr.Dataset,
|
|
321
|
+
species_list: list,
|
|
322
|
+
geometric_factors: xr.DataArray,
|
|
323
|
+
efficiency: pd.DataFrame,
|
|
324
|
+
positions: list,
|
|
325
|
+
) -> xr.Dataset:
|
|
326
|
+
"""
|
|
327
|
+
Process the lo-species L2 dataset to calculate angular intensities.
|
|
328
|
+
|
|
329
|
+
Parameters
|
|
330
|
+
----------
|
|
331
|
+
dataset : xarray.Dataset
|
|
332
|
+
The L2 dataset to process.
|
|
333
|
+
species_list : list
|
|
334
|
+
List of species variable names to calculate intensity.
|
|
335
|
+
geometric_factors : xarray.DataArray
|
|
336
|
+
The geometric factors array with shape (epoch, esa_steps).
|
|
337
|
+
efficiency : pandas.DataFrame
|
|
338
|
+
The efficiency lookup table.
|
|
339
|
+
positions : list
|
|
340
|
+
A list of position indices to select from the geometric factor and
|
|
341
|
+
efficiency lookup tables.
|
|
342
|
+
|
|
343
|
+
Returns
|
|
344
|
+
-------
|
|
345
|
+
xarray.Dataset
|
|
346
|
+
The updated L2 dataset with angular intensities calculated.
|
|
347
|
+
"""
|
|
348
|
+
# Calculate the angular intensities using the provided geometric factors and
|
|
349
|
+
# efficiency.
|
|
350
|
+
dataset = calculate_intensity(
|
|
351
|
+
dataset,
|
|
352
|
+
species_list,
|
|
353
|
+
geometric_factors,
|
|
354
|
+
efficiency,
|
|
355
|
+
positions,
|
|
356
|
+
average_across_positions=False,
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
# transform positions to elevation angles
|
|
360
|
+
if positions == SW_POSITIONS:
|
|
361
|
+
pos_to_el = LO_POSITION_TO_ELEVATION_ANGLE["sw"]
|
|
362
|
+
position_index_to_adjust = 0
|
|
363
|
+
elif positions == NSW_POSITIONS:
|
|
364
|
+
pos_to_el = LO_POSITION_TO_ELEVATION_ANGLE["nsw"]
|
|
365
|
+
position_index_to_adjust = 9
|
|
366
|
+
else:
|
|
367
|
+
raise ValueError("Unknown positions for elevation angle mapping.")
|
|
368
|
+
|
|
369
|
+
# Create a new coordinate for elevation_angle based on inst_az
|
|
370
|
+
dataset = dataset.assign_coords(
|
|
371
|
+
elevation_angle=(
|
|
372
|
+
"inst_az",
|
|
373
|
+
[pos_to_el[pos] for pos in dataset["inst_az"].data],
|
|
374
|
+
)
|
|
375
|
+
)
|
|
376
|
+
# add uncertainties to species list
|
|
377
|
+
species_list = species_list + [f"unc_{var}" for var in species_list]
|
|
378
|
+
# Take the mean across elevation angles and restore the original dimension order
|
|
379
|
+
dataset_converted = (
|
|
380
|
+
dataset[species_list]
|
|
381
|
+
.groupby("elevation_angle")
|
|
382
|
+
.sum(keep_attrs=True) # One position should always contain zeros so sum is safe
|
|
383
|
+
# Restore original dimension order because groupby moves the grouped
|
|
384
|
+
# dimension to the front
|
|
385
|
+
.transpose("epoch", "energy_table", "spin_sector", "elevation_angle", ...)
|
|
386
|
+
)
|
|
387
|
+
# Create a new coordinate for spin angle based on spin_sector
|
|
388
|
+
# Use equation from section 11.2.2 of algorithm document
|
|
389
|
+
dataset = dataset.assign_coords(
|
|
390
|
+
spin_angle=("spin_sector", dataset["spin_sector"].data * 15.0 + 7.5)
|
|
391
|
+
)
|
|
392
|
+
dataset = dataset.drop_vars(species_list).merge(dataset_converted)
|
|
393
|
+
# Positions 0 and 10 only observe half of the 24 spins for each esa step.
|
|
394
|
+
# To account for this, we replicate the counts observed in position 0 and 10 for
|
|
395
|
+
# each esa step to either spin angles 0-11 or 12-23, depending on the pixel
|
|
396
|
+
# orientation (A/B). See section 11.2.2 of the CoDICE algorithm document
|
|
397
|
+
a_inds = np.array(
|
|
398
|
+
[pos for pos, orientation in PIXEL_ORIENTATIONS.items() if orientation == "A"]
|
|
399
|
+
)
|
|
400
|
+
b_inds = np.array(
|
|
401
|
+
[pos for pos, orientation in PIXEL_ORIENTATIONS.items() if orientation == "B"]
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
position_index = position_index_to_adjust
|
|
405
|
+
for species in species_list:
|
|
406
|
+
# Determine the correct spin indices based on the position
|
|
407
|
+
spin_sectors = dataset["spin_sector"].data
|
|
408
|
+
spin_inds_1 = np.where(spin_sectors >= 12)[0]
|
|
409
|
+
spin_inds_2 = np.where(spin_sectors < 12)[0]
|
|
410
|
+
# if position_index is 9, swap the spin indices
|
|
411
|
+
if position_index == 9:
|
|
412
|
+
spin_inds_1, spin_inds_2 = spin_inds_2, spin_inds_1
|
|
413
|
+
|
|
414
|
+
# Assign the values to the correct positions and spin sectors
|
|
415
|
+
dataset[species].values[
|
|
416
|
+
:, a_inds[:, np.newaxis], spin_inds_1, position_index
|
|
417
|
+
] = dataset[species].values[
|
|
418
|
+
:, a_inds[:, np.newaxis], spin_inds_2, position_index
|
|
419
|
+
]
|
|
420
|
+
|
|
421
|
+
dataset[species].values[
|
|
422
|
+
:, b_inds[:, np.newaxis], spin_inds_2, position_index
|
|
423
|
+
] = dataset[species].values[
|
|
424
|
+
:, b_inds[:, np.newaxis], spin_inds_1, position_index
|
|
425
|
+
]
|
|
237
426
|
|
|
238
427
|
return dataset
|
|
239
428
|
|
|
@@ -627,7 +816,6 @@ def process_codice_l2(
|
|
|
627
816
|
# This should get science files since ancillary or spice doesn't have data_type
|
|
628
817
|
# as data level.
|
|
629
818
|
file_path = dependencies.get_file_paths(descriptor=descriptor)[0]
|
|
630
|
-
|
|
631
819
|
# Now form product name from descriptor
|
|
632
820
|
descriptor = ScienceFilePath(file_path).descriptor
|
|
633
821
|
dataset_name = f"imap_codice_l2_{descriptor}"
|
|
@@ -637,7 +825,12 @@ def process_codice_l2(
|
|
|
637
825
|
if dataset_name in [
|
|
638
826
|
"imap_codice_l2_lo-sw-species",
|
|
639
827
|
"imap_codice_l2_lo-nsw-species",
|
|
828
|
+
"imap_codice_l2_lo-nsw-angular",
|
|
829
|
+
"imap_codice_l2_lo-sw-angular",
|
|
640
830
|
]:
|
|
831
|
+
cdf_attrs = ImapCdfAttributes()
|
|
832
|
+
cdf_attrs.add_instrument_global_attrs("codice")
|
|
833
|
+
|
|
641
834
|
l2_dataset = load_cdf(file_path).copy()
|
|
642
835
|
|
|
643
836
|
geometric_factor_lookup = get_geometric_factor_lut(dependencies)
|
|
@@ -645,12 +838,13 @@ def process_codice_l2(
|
|
|
645
838
|
geometric_factors = compute_geometric_factors(
|
|
646
839
|
l2_dataset, geometric_factor_lookup
|
|
647
840
|
)
|
|
841
|
+
|
|
648
842
|
if dataset_name == "imap_codice_l2_lo-sw-species":
|
|
649
843
|
# Filter the efficiency lookup table for solar wind efficiencies
|
|
650
844
|
efficiencies = efficiency_lookup[efficiency_lookup["product"] == "sw"]
|
|
651
845
|
# Calculate the pickup ion sunward solar wind intensities using equation
|
|
652
|
-
# described in section 11.2.
|
|
653
|
-
process_lo_species_intensity(
|
|
846
|
+
# described in section 11.2.3 of algorithm document.
|
|
847
|
+
l2_dataset = process_lo_species_intensity(
|
|
654
848
|
l2_dataset,
|
|
655
849
|
LO_SW_PICKUP_ION_SPECIES_VARIABLE_NAMES,
|
|
656
850
|
geometric_factors,
|
|
@@ -658,27 +852,59 @@ def process_codice_l2(
|
|
|
658
852
|
PUI_POSITIONS,
|
|
659
853
|
)
|
|
660
854
|
# Calculate the sunward solar wind species intensities using equation
|
|
661
|
-
# described in section 11.2.
|
|
662
|
-
process_lo_species_intensity(
|
|
855
|
+
# described in section 11.2.3 of algorithm document.
|
|
856
|
+
l2_dataset = process_lo_species_intensity(
|
|
663
857
|
l2_dataset,
|
|
664
|
-
|
|
858
|
+
LO_SW_SOLAR_WIND_SPECIES_VARIABLE_NAMES,
|
|
665
859
|
geometric_factors,
|
|
666
860
|
efficiencies,
|
|
667
|
-
|
|
861
|
+
SOLAR_WIND_POSITIONS,
|
|
668
862
|
)
|
|
669
|
-
|
|
670
|
-
|
|
863
|
+
l2_dataset.attrs.update(
|
|
864
|
+
cdf_attrs.get_global_attributes("imap_codice_l2_lo-sw-species")
|
|
865
|
+
)
|
|
866
|
+
elif dataset_name == "imap_codice_l2_lo-nsw-species":
|
|
867
|
+
# Filter the efficiency lookup table for non-solar wind efficiencies
|
|
671
868
|
efficiencies = efficiency_lookup[efficiency_lookup["product"] == "nsw"]
|
|
672
869
|
# Calculate the non-sunward species intensities using equation
|
|
673
|
-
# described in section 11.2.
|
|
674
|
-
process_lo_species_intensity(
|
|
870
|
+
# described in section 11.2.3 of algorithm document.
|
|
871
|
+
l2_dataset = process_lo_species_intensity(
|
|
675
872
|
l2_dataset,
|
|
676
873
|
LO_NSW_SPECIES_VARIABLE_NAMES,
|
|
677
874
|
geometric_factors,
|
|
678
875
|
efficiencies,
|
|
679
876
|
NSW_POSITIONS,
|
|
680
877
|
)
|
|
681
|
-
|
|
878
|
+
l2_dataset.attrs.update(
|
|
879
|
+
cdf_attrs.get_global_attributes("imap_codice_l2_lo-nsw-species")
|
|
880
|
+
)
|
|
881
|
+
elif dataset_name == "imap_codice_l2_lo-sw-angular":
|
|
882
|
+
efficiencies = efficiency_lookup[efficiency_lookup["product"] == "sw"]
|
|
883
|
+
# Calculate the sunward solar wind angular intensities using equation
|
|
884
|
+
# described in section 11.2.2 of algorithm document.
|
|
885
|
+
l2_dataset = process_lo_angular_intensity(
|
|
886
|
+
l2_dataset,
|
|
887
|
+
LO_SW_ANGULAR_VARIABLE_NAMES,
|
|
888
|
+
geometric_factors,
|
|
889
|
+
efficiencies,
|
|
890
|
+
SW_POSITIONS,
|
|
891
|
+
)
|
|
892
|
+
l2_dataset.attrs.update(
|
|
893
|
+
cdf_attrs.get_global_attributes("imap_codice_l2_lo-sw-angular")
|
|
894
|
+
)
|
|
895
|
+
if dataset_name == "imap_codice_l2_lo-nsw-angular":
|
|
896
|
+
# Calculate the non sunward angular intensities
|
|
897
|
+
efficiencies = efficiency_lookup[efficiency_lookup["product"] == "nsw"]
|
|
898
|
+
l2_dataset = process_lo_angular_intensity(
|
|
899
|
+
l2_dataset,
|
|
900
|
+
LO_NSW_ANGULAR_VARIABLE_NAMES,
|
|
901
|
+
geometric_factors,
|
|
902
|
+
efficiencies,
|
|
903
|
+
NSW_POSITIONS,
|
|
904
|
+
)
|
|
905
|
+
l2_dataset.attrs.update(
|
|
906
|
+
cdf_attrs.get_global_attributes("imap_codice_l2_lo-nsw-angular")
|
|
907
|
+
)
|
|
682
908
|
if dataset_name in [
|
|
683
909
|
"imap_codice_l2_hi-counters-singles",
|
|
684
910
|
"imap_codice_l2_hi-counters-aggregated",
|
|
@@ -727,65 +953,6 @@ def process_codice_l2(
|
|
|
727
953
|
# See section 11.1.2 of algorithm document
|
|
728
954
|
pass
|
|
729
955
|
|
|
730
|
-
elif dataset_name == "imap_codice_l2_lo-sw-angular":
|
|
731
|
-
# Calculate the sunward angular intensities using equation described in
|
|
732
|
-
# section 11.2.3 of algorithm document.
|
|
733
|
-
pass
|
|
734
|
-
|
|
735
|
-
elif dataset_name == "imap_codice_l2_lo-nsw-angular":
|
|
736
|
-
# Calculate the non-sunward angular intensities using equation described
|
|
737
|
-
# in section 11.2.3 of algorithm document.
|
|
738
|
-
pass
|
|
739
|
-
|
|
740
956
|
# logger.info(f"\nFinal data product:\n{l2_dataset}\n")
|
|
741
957
|
|
|
742
958
|
return l2_dataset
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
def add_dataset_attributes(
|
|
746
|
-
dataset: xr.Dataset, dataset_name: str, cdf_attrs: ImapCdfAttributes
|
|
747
|
-
) -> xr.Dataset:
|
|
748
|
-
"""
|
|
749
|
-
Add the global and variable attributes to the dataset.
|
|
750
|
-
|
|
751
|
-
Parameters
|
|
752
|
-
----------
|
|
753
|
-
dataset : xarray.Dataset
|
|
754
|
-
The dataset to update.
|
|
755
|
-
dataset_name : str
|
|
756
|
-
The name of the dataset.
|
|
757
|
-
cdf_attrs : ImapCdfAttributes
|
|
758
|
-
The attribute manager for CDF attributes.
|
|
759
|
-
|
|
760
|
-
Returns
|
|
761
|
-
-------
|
|
762
|
-
xarray.Dataset
|
|
763
|
-
The updated dataset.
|
|
764
|
-
"""
|
|
765
|
-
cdf_attrs.add_instrument_global_attrs("codice")
|
|
766
|
-
cdf_attrs.add_instrument_variable_attrs("codice", "l2")
|
|
767
|
-
|
|
768
|
-
# Update the global attributes
|
|
769
|
-
dataset.attrs = cdf_attrs.get_global_attributes(dataset_name)
|
|
770
|
-
|
|
771
|
-
# Set the variable attributes
|
|
772
|
-
for variable_name in dataset.data_vars.keys():
|
|
773
|
-
try:
|
|
774
|
-
dataset[variable_name].attrs = cdf_attrs.get_variable_attributes(
|
|
775
|
-
variable_name, check_schema=False
|
|
776
|
-
)
|
|
777
|
-
except KeyError:
|
|
778
|
-
# Some variables may have a product descriptor prefix in the
|
|
779
|
-
# cdf attributes key if they are common to multiple products.
|
|
780
|
-
descriptor = dataset_name.split("imap_codice_l2_")[-1]
|
|
781
|
-
cdf_attrs_key = f"{descriptor}-{variable_name}"
|
|
782
|
-
try:
|
|
783
|
-
dataset[variable_name].attrs = cdf_attrs.get_variable_attributes(
|
|
784
|
-
f"{cdf_attrs_key}", check_schema=False
|
|
785
|
-
)
|
|
786
|
-
except KeyError:
|
|
787
|
-
logger.error(
|
|
788
|
-
f"Field '{variable_name}' and '{cdf_attrs_key}' not found in "
|
|
789
|
-
f"attribute manager."
|
|
790
|
-
)
|
|
791
|
-
return dataset
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""CoDICE L1A processing functions."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
import xarray as xr
|
|
6
|
+
from imap_data_access import ProcessingInputCollection
|
|
7
|
+
|
|
8
|
+
from imap_processing import imap_module_directory
|
|
9
|
+
from imap_processing.codice.codice_l1a_lo_angular import l1a_lo_angular
|
|
10
|
+
from imap_processing.codice.codice_l1a_lo_species import l1a_lo_species
|
|
11
|
+
from imap_processing.codice.utils import (
|
|
12
|
+
CODICEAPID,
|
|
13
|
+
)
|
|
14
|
+
from imap_processing.utils import packet_file_to_datasets
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def process_l1a(dependency: ProcessingInputCollection) -> list[xr.Dataset]:
|
|
20
|
+
"""
|
|
21
|
+
Process L1A data based on descriptor and dependencies.
|
|
22
|
+
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
25
|
+
dependency : ProcessingInputCollection
|
|
26
|
+
Collection of processing inputs required for L1A processing.
|
|
27
|
+
|
|
28
|
+
Returns
|
|
29
|
+
-------
|
|
30
|
+
list[xarray.Dataset]
|
|
31
|
+
List of processed L1A datasets generated from available APIDs.
|
|
32
|
+
"""
|
|
33
|
+
# Get science data which is L0 packet file
|
|
34
|
+
science_file = dependency.get_file_paths(data_type="l0")[0]
|
|
35
|
+
# Get LUT file
|
|
36
|
+
lut_file = dependency.get_file_paths(descriptor="l1a-sci-lut")[0]
|
|
37
|
+
|
|
38
|
+
logger.info(f"Processing L1A for {science_file.name} with {lut_file.name}")
|
|
39
|
+
|
|
40
|
+
xtce_file = (
|
|
41
|
+
imap_module_directory / "codice/packet_definitions/codice_packet_definition.xml"
|
|
42
|
+
)
|
|
43
|
+
# Decom packet
|
|
44
|
+
datasets_by_apid = packet_file_to_datasets(
|
|
45
|
+
science_file,
|
|
46
|
+
xtce_file,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
datasets = []
|
|
50
|
+
for apid in datasets_by_apid:
|
|
51
|
+
if apid == CODICEAPID.COD_LO_SW_SPECIES_COUNTS:
|
|
52
|
+
logger.info("Processing Lo SW Species Counts")
|
|
53
|
+
datasets.append(l1a_lo_species(datasets_by_apid[apid], lut_file))
|
|
54
|
+
elif apid == CODICEAPID.COD_LO_NSW_SPECIES_COUNTS:
|
|
55
|
+
logger.info("Processing Lo NSW Species Counts")
|
|
56
|
+
datasets.append(l1a_lo_species(datasets_by_apid[apid], lut_file))
|
|
57
|
+
elif apid == CODICEAPID.COD_LO_SW_ANGULAR_COUNTS:
|
|
58
|
+
logger.info("Processing Lo SW Angular Counts")
|
|
59
|
+
datasets.append(l1a_lo_angular(datasets_by_apid[apid], lut_file))
|
|
60
|
+
elif apid == CODICEAPID.COD_LO_NSW_ANGULAR_COUNTS:
|
|
61
|
+
logger.info("Processing Lo NSW Angular Counts")
|
|
62
|
+
datasets.append(l1a_lo_angular(datasets_by_apid[apid], lut_file))
|
|
63
|
+
|
|
64
|
+
return datasets
|
|
@@ -61,6 +61,8 @@ CODICEAPID_MAPPING = {
|
|
|
61
61
|
SPIN_PERIOD_CONVERSION = 0.00032
|
|
62
62
|
K_FACTOR = 5.76 # This is used to convert voltages to energies in L2
|
|
63
63
|
HI_ACQUISITION_TIME = 0.59916
|
|
64
|
+
NUM_ESA_STEPS = 128
|
|
65
|
+
LO_DESPIN_SPIN_SECTORS = 24
|
|
64
66
|
|
|
65
67
|
# CDF variable names used for lo data products
|
|
66
68
|
LO_COUNTERS_SINGLES_VARIABLE_NAMES = ["apd_singles"]
|
|
@@ -2281,8 +2283,9 @@ HALF_SPIN_LUT = {
|
|
|
2281
2283
|
}
|
|
2282
2284
|
|
|
2283
2285
|
NSW_POSITIONS = [x for x in range(3, 22)]
|
|
2284
|
-
SW_POSITIONS = [0]
|
|
2285
|
-
|
|
2286
|
+
SW_POSITIONS = [0, 1, 2, 22, 23]
|
|
2287
|
+
SOLAR_WIND_POSITIONS = [0]
|
|
2288
|
+
PUI_POSITIONS = SW_POSITIONS
|
|
2286
2289
|
L2_GEOMETRIC_FACTOR = 0.013
|
|
2287
2290
|
L2_HI_NUMBER_OF_SSD = 12.0
|
|
2288
2291
|
|
|
@@ -2320,3 +2323,35 @@ HI_L2_ELEVATION_ANGLE = np.array(
|
|
|
2320
2323
|
],
|
|
2321
2324
|
dtype=float,
|
|
2322
2325
|
)
|
|
2326
|
+
|
|
2327
|
+
|
|
2328
|
+
LO_POSITION_TO_ELEVATION_ANGLE = {
|
|
2329
|
+
"sw": {
|
|
2330
|
+
1: 0,
|
|
2331
|
+
2: 15,
|
|
2332
|
+
24: 15,
|
|
2333
|
+
3: 30,
|
|
2334
|
+
23: 30,
|
|
2335
|
+
},
|
|
2336
|
+
"nsw": {
|
|
2337
|
+
4: 45,
|
|
2338
|
+
22: 45,
|
|
2339
|
+
5: 60,
|
|
2340
|
+
21: 60,
|
|
2341
|
+
6: 75,
|
|
2342
|
+
20: 75,
|
|
2343
|
+
7: 90,
|
|
2344
|
+
19: 90,
|
|
2345
|
+
8: 105,
|
|
2346
|
+
18: 105,
|
|
2347
|
+
9: 120,
|
|
2348
|
+
17: 120,
|
|
2349
|
+
10: 135,
|
|
2350
|
+
16: 135,
|
|
2351
|
+
11: 150,
|
|
2352
|
+
15: 150,
|
|
2353
|
+
12: 165,
|
|
2354
|
+
14: 165,
|
|
2355
|
+
13: 180,
|
|
2356
|
+
},
|
|
2357
|
+
}
|