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.
Files changed (58) hide show
  1. imap_processing/_version.py +2 -2
  2. imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +18 -0
  3. imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +101 -258
  4. imap_processing/cdf/config/imap_enamaps_l2-common_variable_attrs.yaml +1 -1
  5. imap_processing/cdf/config/imap_hi_variable_attrs.yaml +12 -2
  6. imap_processing/cdf/config/imap_idex_global_cdf_attrs.yaml +1 -8
  7. imap_processing/cdf/config/imap_idex_l1b_variable_attrs.yaml +16 -5
  8. imap_processing/cdf/config/imap_idex_l2a_variable_attrs.yaml +27 -25
  9. imap_processing/cdf/config/imap_idex_l2b_variable_attrs.yaml +16 -16
  10. imap_processing/cdf/config/imap_idex_l2c_variable_attrs.yaml +2 -2
  11. imap_processing/cdf/config/imap_swapi_variable_attrs.yaml +2 -13
  12. imap_processing/cdf/config/imap_ultra_l1c_variable_attrs.yaml +12 -0
  13. imap_processing/cdf/utils.py +2 -2
  14. imap_processing/cli.py +4 -16
  15. imap_processing/codice/codice_l1a_lo_angular.py +362 -0
  16. imap_processing/codice/codice_l1a_lo_species.py +282 -0
  17. imap_processing/codice/codice_l1b.py +80 -97
  18. imap_processing/codice/codice_l2.py +270 -103
  19. imap_processing/codice/codice_new_l1a.py +64 -0
  20. imap_processing/codice/constants.py +37 -2
  21. imap_processing/codice/utils.py +270 -0
  22. imap_processing/ena_maps/ena_maps.py +51 -39
  23. imap_processing/ena_maps/utils/corrections.py +196 -14
  24. imap_processing/ena_maps/utils/naming.py +3 -1
  25. imap_processing/hi/hi_l1c.py +57 -19
  26. imap_processing/hi/hi_l2.py +89 -36
  27. imap_processing/ialirt/calculate_ingest.py +19 -1
  28. imap_processing/ialirt/constants.py +12 -6
  29. imap_processing/ialirt/generate_coverage.py +6 -1
  30. imap_processing/ialirt/l0/parse_mag.py +1 -0
  31. imap_processing/ialirt/l0/process_hit.py +1 -0
  32. imap_processing/ialirt/l0/process_swapi.py +1 -0
  33. imap_processing/ialirt/l0/process_swe.py +2 -0
  34. imap_processing/ialirt/process_ephemeris.py +6 -2
  35. imap_processing/ialirt/utils/create_xarray.py +3 -2
  36. imap_processing/lo/l1b/lo_l1b.py +12 -2
  37. imap_processing/lo/l1c/lo_l1c.py +4 -4
  38. imap_processing/lo/l2/lo_l2.py +101 -8
  39. imap_processing/quality_flags.py +1 -0
  40. imap_processing/swapi/constants.py +4 -0
  41. imap_processing/swapi/l1/swapi_l1.py +47 -20
  42. imap_processing/swapi/l2/swapi_l2.py +17 -3
  43. imap_processing/ultra/l1a/ultra_l1a.py +121 -72
  44. imap_processing/ultra/l1b/de.py +57 -1
  45. imap_processing/ultra/l1b/ultra_l1b_annotated.py +0 -1
  46. imap_processing/ultra/l1b/ultra_l1b_extended.py +24 -11
  47. imap_processing/ultra/l1c/helio_pset.py +34 -8
  48. imap_processing/ultra/l1c/l1c_lookup_utils.py +4 -2
  49. imap_processing/ultra/l1c/spacecraft_pset.py +13 -7
  50. imap_processing/ultra/l1c/ultra_l1c.py +6 -6
  51. imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +79 -20
  52. imap_processing/ultra/l2/ultra_l2.py +2 -2
  53. imap_processing/ultra/utils/ultra_l1_utils.py +6 -0
  54. {imap_processing-1.0.1.dist-info → imap_processing-1.0.3.dist-info}/METADATA +1 -1
  55. {imap_processing-1.0.1.dist-info → imap_processing-1.0.3.dist-info}/RECORD +58 -54
  56. {imap_processing-1.0.1.dist-info → imap_processing-1.0.3.dist-info}/LICENSE +0 -0
  57. {imap_processing-1.0.1.dist-info → imap_processing-1.0.3.dist-info}/WHEEL +0 -0
  58. {imap_processing-1.0.1.dist-info → imap_processing-1.0.3.dist-info}/entry_points.txt +0 -0
@@ -15,20 +15,15 @@ from pathlib import Path
15
15
  import numpy as np
16
16
  import xarray as xr
17
17
 
18
- from imap_processing import imap_module_directory
19
18
  from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
20
19
  from imap_processing.cdf.utils import load_cdf
21
20
  from imap_processing.codice import constants
22
- from imap_processing.codice.utils import CODICEAPID
23
- from imap_processing.utils import packet_file_to_datasets
24
21
 
25
22
  logger = logging.getLogger(__name__)
26
23
  logger.setLevel(logging.INFO)
27
24
 
28
25
 
29
- def convert_to_rates(
30
- dataset: xr.Dataset, descriptor: str, variable_name: str
31
- ) -> np.ndarray:
26
+ def convert_to_rates(dataset: xr.Dataset, descriptor: str) -> np.ndarray:
32
27
  """
33
28
  Apply a conversion from counts to rates.
34
29
 
@@ -41,14 +36,34 @@ def convert_to_rates(
41
36
  The L1b dataset containing the data to convert.
42
37
  descriptor : str
43
38
  The descriptor of the data product of interest.
44
- variable_name : str
45
- The variable name to apply the conversion to.
46
39
 
47
40
  Returns
48
41
  -------
49
42
  rates_data : np.ndarray
50
43
  The converted data array.
51
44
  """
45
+ # Variables to convert based on descriptor
46
+ variables_to_convert = getattr(
47
+ constants, f"{descriptor.upper().replace('-', '_')}_VARIABLE_NAMES"
48
+ )
49
+
50
+ if descriptor.startswith("lo-"):
51
+ # Calculate energy_table using voltage_table and k_factor
52
+ energy_attrs = dataset["voltage_table"].attrs | {
53
+ "UNITS": "keV/e",
54
+ "LABLAXIS": "E/q",
55
+ "CATDESC": "Energy per charge",
56
+ "FIELDNAM": "Energy per charge",
57
+ }
58
+ # 1e3 is to convert eV to keV
59
+ dataset["energy_table"] = xr.DataArray(
60
+ dataset["voltage_table"].values * dataset["k_factor"].values * 1e-3,
61
+ dims=[
62
+ "esa_step",
63
+ ],
64
+ attrs=energy_attrs,
65
+ )
66
+
52
67
  if descriptor in [
53
68
  "lo-counters-aggregated",
54
69
  "lo-counters-singles",
@@ -58,41 +73,50 @@ def convert_to_rates(
58
73
  "lo-sw-priority",
59
74
  "lo-ialirt",
60
75
  ]:
61
- # Applying rate calculation described in section 10.2 of the algorithm
62
- # document
63
- # In order to divide by acquisition times, we must reshape the acq
64
- # time data array to match the data variable shape
65
- dims = [1] * dataset[variable_name].data.ndim
66
- dims[1] = 128
67
- acq_times = dataset.acquisition_time_per_step.data.reshape(dims) # (128)
68
- # Now perform the calculation
69
- rates_data = dataset[variable_name].data / (
70
- acq_times
71
- * 1e-3 # Converting from milliseconds to seconds
76
+ # Denominator to convert counts to rates
77
+ denominator = (
78
+ dataset.acquisition_time_per_step
72
79
  * constants.L1B_DATA_PRODUCT_CONFIGURATIONS[descriptor]["num_spin_sectors"]
73
80
  )
81
+
82
+ # Do not carry these variable attributes from L1a to L1b for above products
83
+ drop_variables = [
84
+ "k_factor",
85
+ "nso_half_spin",
86
+ "sw_bias_gain_mode",
87
+ "st_bias_gain_mode",
88
+ "spin_period",
89
+ "voltage_table",
90
+ "acquisition_time_per_step",
91
+ ]
92
+ dataset = dataset.drop_vars(drop_variables)
74
93
  elif descriptor in [
75
94
  "lo-nsw-species",
76
95
  "lo-sw-species",
77
96
  ]:
78
- # Applying rate calculation described in section 10.2 of the algorithm
79
- # document
80
- # In order to divide by acquisition times, we must reshape the acq
81
- # time data array to match the data variable shape (epoch, esa_step, sector)
82
- dims = [1] * dataset[variable_name].data.ndim
83
- dims[1] = 128
84
- acq_times = dataset.acquisition_time_per_step.data.reshape(dims) # (128)
85
- # acquisition time have an array of shape (128,). We match n_sector to that.
97
+ # Create n_sector with 'esa_step' dimension. This is done by xr.full_like
98
+ # with input dataset.acquisition_time_per_step. This ensures that the resulting
99
+ # n_sector has the same dimensions as acquisition_time_per_step.
86
100
  # Per CoDICE, fill first 127 with default value of 12. Then fill last with 11.
87
- n_sector = np.full(128, 12, dtype=int)
88
- n_sector[-1] = 11
89
-
90
- # Now perform the calculation
91
- rates_data = dataset[variable_name].data / (
92
- acq_times
93
- * 1e-3 # Converting from milliseconds to seconds
94
- * n_sector[:, np.newaxis] # Spin sectors
101
+ n_sector = xr.full_like(
102
+ dataset.acquisition_time_per_step, 12.0, dtype=np.float64
95
103
  )
104
+ n_sector[-1] = 11.0
105
+
106
+ # Denominator to convert counts to rates
107
+ denominator = dataset.acquisition_time_per_step * n_sector
108
+ # Do not carry these variable attributes from L1a to L1b for above products
109
+ drop_variables = [
110
+ "k_factor",
111
+ "nso_half_spin",
112
+ "sw_bias_gain_mode",
113
+ "st_bias_gain_mode",
114
+ "spin_period",
115
+ "voltage_table",
116
+ "acquisition_time_per_step",
117
+ ]
118
+ dataset = dataset.drop_vars(drop_variables)
119
+
96
120
  elif descriptor in [
97
121
  "hi-counters-aggregated",
98
122
  "hi-counters-singles",
@@ -101,15 +125,27 @@ def convert_to_rates(
101
125
  "hi-sectored",
102
126
  "hi-ialirt",
103
127
  ]:
104
- # Applying rate calculation described in section 10.1 of the algorithm
105
- # document
106
- rates_data = dataset[variable_name].data / (
128
+ # Denominator to convert counts to rates
129
+ denominator = (
107
130
  constants.L1B_DATA_PRODUCT_CONFIGURATIONS[descriptor]["num_spin_sectors"]
108
131
  * constants.L1B_DATA_PRODUCT_CONFIGURATIONS[descriptor]["num_spins"]
109
132
  * constants.HI_ACQUISITION_TIME
110
133
  )
111
134
 
112
- return rates_data
135
+ # For each variable, convert counts and uncertainty to rates
136
+ for variable in variables_to_convert:
137
+ dataset[variable].data = dataset[variable].astype(np.float64) / denominator
138
+ # Carry over attrs and update as needed
139
+ dataset[variable].attrs["UNITS"] = "counts/s"
140
+
141
+ # Uncertainty calculation
142
+ unc_variable = f"unc_{variable}"
143
+ dataset[unc_variable].data = (
144
+ dataset[unc_variable].astype(np.float64) / denominator
145
+ )
146
+ dataset[unc_variable].attrs["UNITS"] = "1/s"
147
+
148
+ return dataset
113
149
 
114
150
 
115
151
  def process_codice_l1b(file_path: Path) -> xr.Dataset:
@@ -136,70 +172,17 @@ def process_codice_l1b(file_path: Path) -> xr.Dataset:
136
172
  dataset_name = l1a_dataset.attrs["Logical_source"].replace("_l1a_", "_l1b_")
137
173
  descriptor = dataset_name.removeprefix("imap_codice_l1b_")
138
174
 
139
- # Direct event data products do not have a level L1B
140
- if descriptor in ["lo-direct-events", "hi-direct-events"]:
141
- logger.warning("Encountered direct event data product. Skipping L1b processing")
142
- return None
143
-
144
175
  # Get the L1b CDF attributes
145
176
  cdf_attrs = ImapCdfAttributes()
146
177
  cdf_attrs.add_instrument_global_attrs("codice")
147
178
  cdf_attrs.add_instrument_variable_attrs("codice", "l1b")
148
179
 
149
180
  # Use the L1a data product as a starting point for L1b
150
- l1b_dataset = l1a_dataset.copy()
181
+ l1b_dataset = l1a_dataset.copy(deep=True)
151
182
 
152
183
  # Update the global attributes
153
184
  l1b_dataset.attrs = cdf_attrs.get_global_attributes(dataset_name)
154
-
155
- # TODO: This was thrown together quickly and should be double-checked
156
- if descriptor == "hskp":
157
- xtce_filename = "codice_packet_definition.xml"
158
- xtce_packet_definition = Path(
159
- f"{imap_module_directory}/codice/packet_definitions/{xtce_filename}"
160
- )
161
- packet_file = (
162
- imap_module_directory
163
- / "tests"
164
- / "codice"
165
- / "data"
166
- / "imap_codice_l0_raw_20241110_v001.pkts"
167
- )
168
- datasets: dict[int, xr.Dataset] = packet_file_to_datasets(
169
- packet_file, xtce_packet_definition, use_derived_value=True
170
- )
171
- l1b_dataset = datasets[CODICEAPID.COD_NHK]
172
-
173
- # TODO: Drop the same variables as we do in L1a? (see line 1103 in
174
- # codice_l1a.py
175
-
176
- else:
177
- variables_to_convert = getattr(
178
- constants, f"{descriptor.upper().replace('-', '_')}_VARIABLE_NAMES"
179
- )
180
-
181
- # Apply the conversion to rates
182
- for variable_name in variables_to_convert:
183
- l1b_dataset[variable_name].data = convert_to_rates(
184
- l1b_dataset, descriptor, variable_name
185
- )
186
- # Set the variable attributes
187
- cdf_attrs_key = f"{descriptor}-{variable_name}"
188
- l1b_dataset[variable_name].attrs = cdf_attrs.get_variable_attributes(
189
- cdf_attrs_key, check_schema=False
190
- )
191
-
192
- if descriptor in ["lo-sw-species", "lo-nsw-species"]:
193
- # Do not carry these variable attributes from L1a to L1b
194
- drop_variables = [
195
- "k_factor",
196
- "nso_half_spin",
197
- "sw_bias_gain_mode",
198
- "st_bias_gain_mode",
199
- "spin_period",
200
- ]
201
- l1b_dataset = l1b_dataset.drop_vars(drop_variables)
202
-
203
- logger.info(f"\nFinal data product:\n{l1b_dataset}\n")
204
-
205
- return l1b_dataset
185
+ return convert_to_rates(
186
+ l1b_dataset,
187
+ descriptor,
188
+ )