imap-processing 0.19.0__py3-none-any.whl → 0.19.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.

Potentially problematic release.


This version of imap-processing might be problematic. Click here for more details.

Files changed (73) hide show
  1. imap_processing/_version.py +2 -2
  2. imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +6 -0
  3. imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +31 -894
  4. imap_processing/cdf/config/imap_codice_l1b_variable_attrs.yaml +279 -255
  5. imap_processing/cdf/config/imap_enamaps_l2-common_variable_attrs.yaml +55 -0
  6. imap_processing/cdf/config/imap_enamaps_l2-healpix_variable_attrs.yaml +29 -0
  7. imap_processing/cdf/config/imap_enamaps_l2-rectangular_variable_attrs.yaml +32 -0
  8. imap_processing/cdf/config/imap_glows_l1b_variable_attrs.yaml +3 -1
  9. imap_processing/cdf/config/imap_lo_global_cdf_attrs.yaml +5 -4
  10. imap_processing/cdf/config/imap_ultra_global_cdf_attrs.yaml +28 -16
  11. imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +33 -31
  12. imap_processing/cdf/config/imap_ultra_l1c_variable_attrs.yaml +61 -1
  13. imap_processing/cli.py +62 -71
  14. imap_processing/codice/codice_l0.py +2 -1
  15. imap_processing/codice/codice_l1a.py +47 -49
  16. imap_processing/codice/codice_l1b.py +42 -32
  17. imap_processing/codice/codice_l2.py +105 -7
  18. imap_processing/codice/constants.py +50 -8
  19. imap_processing/codice/data/lo_stepping_values.csv +1 -1
  20. imap_processing/ena_maps/ena_maps.py +39 -18
  21. imap_processing/ena_maps/utils/corrections.py +291 -0
  22. imap_processing/ena_maps/utils/map_utils.py +20 -4
  23. imap_processing/glows/l1b/glows_l1b.py +38 -23
  24. imap_processing/glows/l1b/glows_l1b_data.py +10 -11
  25. imap_processing/hi/hi_l1c.py +4 -109
  26. imap_processing/hi/hi_l2.py +34 -23
  27. imap_processing/hi/utils.py +109 -0
  28. imap_processing/ialirt/l0/ialirt_spice.py +1 -1
  29. imap_processing/ialirt/l0/parse_mag.py +18 -4
  30. imap_processing/ialirt/l0/process_hit.py +9 -4
  31. imap_processing/ialirt/l0/process_swapi.py +9 -4
  32. imap_processing/ialirt/l0/process_swe.py +9 -4
  33. imap_processing/ialirt/utils/create_xarray.py +1 -1
  34. imap_processing/lo/ancillary_data/imap_lo_hydrogen-geometric-factor_v001.csv +75 -0
  35. imap_processing/lo/ancillary_data/imap_lo_oxygen-geometric-factor_v001.csv +75 -0
  36. imap_processing/lo/l1b/lo_l1b.py +90 -16
  37. imap_processing/lo/l1c/lo_l1c.py +164 -50
  38. imap_processing/lo/l2/lo_l2.py +941 -127
  39. imap_processing/mag/l1d/mag_l1d_data.py +36 -3
  40. imap_processing/mag/l2/mag_l2.py +2 -0
  41. imap_processing/mag/l2/mag_l2_data.py +4 -3
  42. imap_processing/quality_flags.py +14 -0
  43. imap_processing/spice/geometry.py +13 -8
  44. imap_processing/spice/pointing_frame.py +4 -2
  45. imap_processing/spice/repoint.py +49 -0
  46. imap_processing/ultra/constants.py +29 -0
  47. imap_processing/ultra/l0/decom_tools.py +58 -46
  48. imap_processing/ultra/l0/decom_ultra.py +21 -9
  49. imap_processing/ultra/l0/ultra_utils.py +4 -4
  50. imap_processing/ultra/l1b/badtimes.py +35 -11
  51. imap_processing/ultra/l1b/de.py +15 -9
  52. imap_processing/ultra/l1b/extendedspin.py +24 -12
  53. imap_processing/ultra/l1b/goodtimes.py +112 -0
  54. imap_processing/ultra/l1b/lookup_utils.py +1 -1
  55. imap_processing/ultra/l1b/ultra_l1b.py +7 -7
  56. imap_processing/ultra/l1b/ultra_l1b_culling.py +8 -4
  57. imap_processing/ultra/l1b/ultra_l1b_extended.py +79 -43
  58. imap_processing/ultra/l1c/helio_pset.py +68 -39
  59. imap_processing/ultra/l1c/l1c_lookup_utils.py +45 -12
  60. imap_processing/ultra/l1c/spacecraft_pset.py +81 -37
  61. imap_processing/ultra/l1c/ultra_l1c.py +27 -22
  62. imap_processing/ultra/l1c/ultra_l1c_culling.py +7 -0
  63. imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +41 -41
  64. imap_processing/ultra/l2/ultra_l2.py +75 -18
  65. imap_processing/ultra/utils/ultra_l1_utils.py +10 -5
  66. {imap_processing-0.19.0.dist-info → imap_processing-0.19.3.dist-info}/METADATA +2 -2
  67. {imap_processing-0.19.0.dist-info → imap_processing-0.19.3.dist-info}/RECORD +71 -69
  68. imap_processing/ultra/l1b/cullingmask.py +0 -90
  69. imap_processing/ultra/l1c/histogram.py +0 -36
  70. /imap_processing/glows/ancillary/{imap_glows_pipeline_settings_20250923_v002.json → imap_glows_pipeline-settings_20250923_v002.json} +0 -0
  71. {imap_processing-0.19.0.dist-info → imap_processing-0.19.3.dist-info}/LICENSE +0 -0
  72. {imap_processing-0.19.0.dist-info → imap_processing-0.19.3.dist-info}/WHEEL +0 -0
  73. {imap_processing-0.19.0.dist-info → imap_processing-0.19.3.dist-info}/entry_points.txt +0 -0
@@ -1,5 +1,6 @@
1
1
  """IMAP-Lo L1C Data Processing."""
2
2
 
3
+ import logging
3
4
  from dataclasses import Field
4
5
  from enum import Enum
5
6
 
@@ -67,14 +68,36 @@ def lo_l1c(sci_dependencies: dict, anc_dependencies: list) -> list[xr.Dataset]:
67
68
  logical_source = "imap_lo_l1c_pset"
68
69
  l1b_de = sci_dependencies["imap_lo_l1b_de"]
69
70
  l1b_goodtimes_only = filter_goodtimes(l1b_de, anc_dependencies)
70
- pset = initialize_pset(l1b_goodtimes_only, attr_mgr, logical_source)
71
- full_counts = create_pset_counts(l1b_goodtimes_only)
72
71
 
73
72
  # Set the pointing start and end times based on the first epoch
74
73
  pointing_start_met, pointing_end_met = get_pointing_times(
75
74
  ttj2000ns_to_met(l1b_goodtimes_only["epoch"][0].item())
76
75
  )
77
76
 
77
+ pset = xr.Dataset(
78
+ coords={"epoch": np.array([met_to_ttj2000ns(pointing_start_met)])},
79
+ attrs=attr_mgr.get_global_attributes(logical_source),
80
+ )
81
+
82
+ # ESA mode needs to be added to L1B DE. Adding try statement
83
+ # to avoid error until it's available in the dataset
84
+ if "esa_mode" not in l1b_de:
85
+ logging.debug(
86
+ "ESA mode not found in L1B DE dataset. \
87
+ Setting to default value of 0 for Hi-Res."
88
+ )
89
+ pset["esa_mode"] = xr.DataArray(
90
+ np.array([0]),
91
+ dims=["epoch"],
92
+ attrs=attr_mgr.get_variable_attributes("esa_mode"),
93
+ )
94
+ else:
95
+ pset["esa_mode"] = xr.DataArray(
96
+ np.array([l1b_de["esa_mode"].values[0]]),
97
+ dims=["epoch"],
98
+ attrs=attr_mgr.get_variable_attributes("esa_mode"),
99
+ )
100
+
78
101
  pset["pointing_start_met"] = xr.DataArray(
79
102
  np.array([pointing_start_met]),
80
103
  dims="epoch",
@@ -86,12 +109,6 @@ def lo_l1c(sci_dependencies: dict, anc_dependencies: list) -> list[xr.Dataset]:
86
109
  attrs=attr_mgr.get_variable_attributes("pointing_end_met"),
87
110
  )
88
111
 
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
112
  # Get the start and end spin numbers based on the pointing start and end MET
96
113
  pset["start_spin_number"] = xr.DataArray(
97
114
  [get_spin_number(pset["pointing_start_met"].item())],
@@ -104,6 +121,8 @@ def lo_l1c(sci_dependencies: dict, anc_dependencies: list) -> list[xr.Dataset]:
104
121
  attrs=attr_mgr.get_variable_attributes("end_spin_number"),
105
122
  )
106
123
 
124
+ full_counts = create_pset_counts(l1b_de, FilterType.NONE)
125
+
107
126
  # Set the counts
108
127
  pset["triples_counts"] = create_pset_counts(
109
128
  l1b_goodtimes_only, FilterType.TRIPLES
@@ -118,6 +137,32 @@ def lo_l1c(sci_dependencies: dict, anc_dependencies: list) -> list[xr.Dataset]:
118
137
  pset["exposure_time"] = calculate_exposure_times(
119
138
  full_counts, l1b_goodtimes_only
120
139
  )
140
+
141
+ # Set backgrounds
142
+ (
143
+ pset["h_background_rates"],
144
+ pset["h_background_rates_stat_uncert"],
145
+ pset["h_background_rates_sys_err"],
146
+ ) = set_background_rates(
147
+ pset["pointing_start_met"].item(),
148
+ pset["pointing_end_met"].item(),
149
+ FilterType.HYDROGEN,
150
+ anc_dependencies,
151
+ attr_mgr,
152
+ )
153
+
154
+ (
155
+ pset["o_background_rates"],
156
+ pset["o_background_rates_stat_uncert"],
157
+ pset["o_background_rates_sys_err"],
158
+ ) = set_background_rates(
159
+ pset["pointing_start_met"].item(),
160
+ pset["pointing_end_met"].item(),
161
+ FilterType.OXYGEN,
162
+ anc_dependencies,
163
+ attr_mgr,
164
+ )
165
+
121
166
  pset.attrs = attr_mgr.get_global_attributes(logical_source)
122
167
 
123
168
  pset = pset.assign_coords(
@@ -131,44 +176,6 @@ def lo_l1c(sci_dependencies: dict, anc_dependencies: list) -> list[xr.Dataset]:
131
176
  return [pset]
132
177
 
133
178
 
134
- def initialize_pset(
135
- l1b_de: xr.Dataset, attr_mgr: ImapCdfAttributes, logical_source: str
136
- ) -> xr.Dataset:
137
- """
138
- Initialize the PSET dataset and set the Epoch.
139
-
140
- The Epoch time is set to the first of the L1B
141
- Direct Event times. There is one Epoch per PSET file.
142
-
143
- Parameters
144
- ----------
145
- l1b_de : xarray.Dataset
146
- L1B Direct Event dataset.
147
- attr_mgr : ImapCdfAttributes
148
- Attribute manager used to get the L1C attributes.
149
- logical_source : str
150
- The logical source of the pset.
151
-
152
- Returns
153
- -------
154
- pset : xarray.Dataset
155
- Initialized PSET dataset.
156
- """
157
- pset = xr.Dataset(
158
- attrs=attr_mgr.get_global_attributes(logical_source),
159
- )
160
- # TODO: Need to create utility to get start of repointing to use
161
- # for the pset epoch time. Setting to first DE for now
162
- pset_epoch = l1b_de["epoch"][0].item()
163
- pset["epoch"] = xr.DataArray(
164
- np.array([pset_epoch]),
165
- dims=["epoch"],
166
- attrs=attr_mgr.get_variable_attributes("epoch"),
167
- )
168
-
169
- return pset
170
-
171
-
172
179
  def filter_goodtimes(l1b_de: xr.Dataset, anc_dependencies: list) -> xr.Dataset:
173
180
  """
174
181
  Filter the L1B Direct Event dataset to only include good times.
@@ -189,10 +196,12 @@ def filter_goodtimes(l1b_de: xr.Dataset, anc_dependencies: list) -> xr.Dataset:
189
196
  Filtered L1B Direct Event dataset.
190
197
  """
191
198
  # the goodtimes are currently the only ancillary file needed for L1C processing
192
- goodtimes_table_df = lo_ancillary.read_ancillary_file(anc_dependencies[0])
199
+ goodtimes_table_df = lo_ancillary.read_ancillary_file(
200
+ next(str(s) for s in anc_dependencies if "good-times" in str(s))
201
+ )
193
202
 
194
203
  # convert goodtimes from MET to TTJ2000
195
- goodtimes_start = met_to_ttj2000ns(goodtimes_table_df["GoodTime_strt"])
204
+ goodtimes_start = met_to_ttj2000ns(goodtimes_table_df["GoodTime_start"])
196
205
  goodtimes_end = met_to_ttj2000ns(goodtimes_table_df["GoodTime_end"])
197
206
 
198
207
  # Create a mask for epochs within any of the start/end time ranges
@@ -254,9 +263,9 @@ def create_pset_counts(
254
263
  "001000",
255
264
  ],
256
265
  # hydrogen species identifier
257
- FilterType.HYDROGEN: "h",
266
+ FilterType.HYDROGEN: "H",
258
267
  # oxygen species identifier
259
- FilterType.OXYGEN: "o",
268
+ FilterType.OXYGEN: "O",
260
269
  }
261
270
 
262
271
  # if the filter string is triples or doubles, filter using the coincidence type
@@ -484,3 +493,108 @@ def create_datasets(
484
493
  )
485
494
 
486
495
  return dataset
496
+
497
+
498
+ def set_background_rates(
499
+ pointing_start_met: float,
500
+ pointing_end_met: float,
501
+ species: FilterType,
502
+ anc_dependencies: list,
503
+ attr_mgr: ImapCdfAttributes,
504
+ ) -> tuple[xr.DataArray, xr.DataArray, xr.DataArray]:
505
+ """
506
+ Set the background rates for the specified species.
507
+
508
+ The background rates are set to a constant value of 0.01 counts/s for all bins.
509
+
510
+ Parameters
511
+ ----------
512
+ pointing_start_met : float
513
+ The start MET time of the pointing.
514
+ pointing_end_met : float
515
+ The end MET time of the pointing.
516
+ species : FilterType
517
+ The species to set the background rates for. Can be "h" or "o".
518
+ anc_dependencies : list
519
+ Ancillary files needed for L1C data product creation.
520
+ attr_mgr : ImapCdfAttributes
521
+ Attribute manager used to get the L1C attributes.
522
+
523
+ Returns
524
+ -------
525
+ background_rates : tuple[xr.DataArray, xr.DataArray, xr.DataArray]
526
+ Tuple containing:
527
+ - The background rates for the specified species.
528
+ - The statistical uncertainties for the background rates.
529
+ - The systematic errors for the background rates.
530
+ """
531
+ if species not in {FilterType.HYDROGEN, FilterType.OXYGEN}:
532
+ raise ValueError(f"Species must be 'h' or 'o', but got {species.value}.")
533
+
534
+ bg_rates = np.zeros(
535
+ (N_ESA_ENERGY_STEPS, N_SPIN_ANGLE_BINS, N_OFF_ANGLE_BINS), dtype=np.float16
536
+ )
537
+ bg_stat_uncert = np.zeros(
538
+ (N_ESA_ENERGY_STEPS, N_SPIN_ANGLE_BINS, N_OFF_ANGLE_BINS), dtype=np.float16
539
+ )
540
+ bg_sys_err = np.zeros(
541
+ (N_ESA_ENERGY_STEPS, N_SPIN_ANGLE_BINS, N_OFF_ANGLE_BINS), dtype=np.float16
542
+ )
543
+
544
+ # read in the background rates from ancillary file
545
+ if species == FilterType.HYDROGEN:
546
+ background_df = lo_ancillary.read_ancillary_file(
547
+ next(str(s) for s in anc_dependencies if "hydrogen-background" in str(s))
548
+ )
549
+ else:
550
+ background_df = lo_ancillary.read_ancillary_file(
551
+ next(str(s) for s in anc_dependencies if "oxygen-background" in str(s))
552
+ )
553
+
554
+ # find to the rows for the current pointing
555
+ pointing_bg_df = background_df[
556
+ (background_df["GoodTime_strt"] >= pointing_start_met)
557
+ & (background_df["GoodTime_end"] <= pointing_end_met)
558
+ ]
559
+
560
+ # convert the bin start and end resolution from 6 degrees to .1 degrees
561
+ pointing_bg_df["bin_strt"] = pointing_bg_df["bin_strt"] * 60
562
+ # The last bin end in the file is 0, which means 60 degrees. This is
563
+ # converted to 0.1 degree resolution of 3600
564
+ pointing_bg_df["bin_end"] = pointing_bg_df["bin_end"] * 60
565
+ pointing_bg_df.loc[pointing_bg_df["bin_end"] == 0, "bin_end"] = 3600
566
+ # for each row in the bg ancillary file for this pointing
567
+ for _, row in pointing_bg_df.iterrows():
568
+ bin_start = int(row["bin_strt"])
569
+ bin_end = int(row["bin_end"])
570
+ # for each energy step, set the background rate and uncertainty
571
+ for esa_step in range(0, 7):
572
+ value = row[f"E-Step{esa_step + 1}"]
573
+ if row["type"] == "rate":
574
+ bg_rates[esa_step, bin_start:bin_end, :] = value
575
+ elif row["type"] == "sigma":
576
+ bg_stat_uncert[esa_step, bin_start:bin_end, :] = value
577
+ else:
578
+ raise ValueError("Unknown background type in ancillary file.")
579
+ # set the background rates, uncertainties, and systematic errors
580
+ bg_rates_data = xr.DataArray(
581
+ data=bg_rates,
582
+ dims=["esa_energy_step", "spin_angle", "off_angle"],
583
+ attrs=attr_mgr.get_variable_attributes(f"{species.value}_background_rates"),
584
+ )
585
+ bg_stat_uncert_data = xr.DataArray(
586
+ data=bg_stat_uncert,
587
+ dims=["esa_energy_step", "spin_angle", "off_angle"],
588
+ attrs=attr_mgr.get_variable_attributes(
589
+ f"{species.value}_background_rates_stat_uncert"
590
+ ),
591
+ )
592
+ bg_sys_err_data = xr.DataArray(
593
+ data=bg_sys_err,
594
+ dims=["esa_energy_step", "spin_angle", "off_angle"],
595
+ attrs=attr_mgr.get_variable_attributes(
596
+ f"{species.value}_background_rates_sys_err"
597
+ ),
598
+ )
599
+
600
+ return bg_rates_data, bg_stat_uncert_data, bg_sys_err_data