imap-processing 0.18.0__py3-none-any.whl → 0.19.2__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 (122) hide show
  1. imap_processing/_version.py +2 -2
  2. imap_processing/ancillary/ancillary_dataset_combiner.py +161 -1
  3. imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +6 -0
  4. imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +221 -1057
  5. imap_processing/cdf/config/imap_codice_l1b_variable_attrs.yaml +307 -283
  6. imap_processing/cdf/config/imap_codice_l2_variable_attrs.yaml +1044 -203
  7. imap_processing/cdf/config/imap_constant_attrs.yaml +4 -2
  8. imap_processing/cdf/config/imap_enamaps_l2-common_variable_attrs.yaml +11 -0
  9. imap_processing/cdf/config/imap_glows_l1b_variable_attrs.yaml +15 -1
  10. imap_processing/cdf/config/imap_hi_global_cdf_attrs.yaml +5 -0
  11. imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +10 -4
  12. imap_processing/cdf/config/imap_idex_l2a_variable_attrs.yaml +33 -4
  13. imap_processing/cdf/config/imap_idex_l2b_variable_attrs.yaml +8 -91
  14. imap_processing/cdf/config/imap_idex_l2c_variable_attrs.yaml +106 -16
  15. imap_processing/cdf/config/imap_lo_global_cdf_attrs.yaml +5 -4
  16. imap_processing/cdf/config/imap_lo_l1a_variable_attrs.yaml +4 -15
  17. imap_processing/cdf/config/imap_lo_l1c_variable_attrs.yaml +189 -98
  18. imap_processing/cdf/config/imap_mag_global_cdf_attrs.yaml +85 -2
  19. imap_processing/cdf/config/imap_mag_l1c_variable_attrs.yaml +24 -1
  20. imap_processing/cdf/config/imap_ultra_global_cdf_attrs.yaml +20 -8
  21. imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +45 -35
  22. imap_processing/cdf/config/imap_ultra_l1c_variable_attrs.yaml +110 -7
  23. imap_processing/cli.py +138 -93
  24. imap_processing/codice/codice_l0.py +2 -1
  25. imap_processing/codice/codice_l1a.py +167 -69
  26. imap_processing/codice/codice_l1b.py +42 -32
  27. imap_processing/codice/codice_l2.py +215 -9
  28. imap_processing/codice/constants.py +790 -603
  29. imap_processing/codice/data/lo_stepping_values.csv +1 -1
  30. imap_processing/decom.py +1 -4
  31. imap_processing/ena_maps/ena_maps.py +71 -43
  32. imap_processing/ena_maps/utils/corrections.py +291 -0
  33. imap_processing/ena_maps/utils/map_utils.py +20 -4
  34. imap_processing/ena_maps/utils/naming.py +8 -2
  35. imap_processing/glows/ancillary/imap_glows_exclusions-by-instr-team_20250923_v002.dat +10 -0
  36. imap_processing/glows/ancillary/imap_glows_map-of-excluded-regions_20250923_v002.dat +393 -0
  37. imap_processing/glows/ancillary/imap_glows_map-of-uv-sources_20250923_v002.dat +593 -0
  38. imap_processing/glows/ancillary/imap_glows_pipeline-settings_20250923_v002.json +54 -0
  39. imap_processing/glows/ancillary/imap_glows_suspected-transients_20250923_v002.dat +10 -0
  40. imap_processing/glows/l1b/glows_l1b.py +123 -18
  41. imap_processing/glows/l1b/glows_l1b_data.py +358 -47
  42. imap_processing/glows/l2/glows_l2.py +11 -0
  43. imap_processing/hi/hi_l1a.py +124 -3
  44. imap_processing/hi/hi_l1b.py +154 -71
  45. imap_processing/hi/hi_l1c.py +4 -109
  46. imap_processing/hi/hi_l2.py +104 -60
  47. imap_processing/hi/utils.py +262 -8
  48. imap_processing/hit/l0/constants.py +3 -0
  49. imap_processing/hit/l0/decom_hit.py +3 -6
  50. imap_processing/hit/l1a/hit_l1a.py +311 -21
  51. imap_processing/hit/l1b/hit_l1b.py +54 -126
  52. imap_processing/hit/l2/hit_l2.py +6 -6
  53. imap_processing/ialirt/calculate_ingest.py +219 -0
  54. imap_processing/ialirt/constants.py +12 -2
  55. imap_processing/ialirt/generate_coverage.py +15 -2
  56. imap_processing/ialirt/l0/ialirt_spice.py +6 -2
  57. imap_processing/ialirt/l0/parse_mag.py +293 -42
  58. imap_processing/ialirt/l0/process_hit.py +5 -3
  59. imap_processing/ialirt/l0/process_swapi.py +41 -25
  60. imap_processing/ialirt/process_ephemeris.py +70 -14
  61. imap_processing/ialirt/utils/create_xarray.py +1 -1
  62. imap_processing/idex/idex_l0.py +2 -2
  63. imap_processing/idex/idex_l1a.py +2 -3
  64. imap_processing/idex/idex_l1b.py +2 -3
  65. imap_processing/idex/idex_l2a.py +130 -4
  66. imap_processing/idex/idex_l2b.py +158 -143
  67. imap_processing/idex/idex_utils.py +1 -3
  68. imap_processing/lo/ancillary_data/imap_lo_hydrogen-geometric-factor_v001.csv +75 -0
  69. imap_processing/lo/ancillary_data/imap_lo_oxygen-geometric-factor_v001.csv +75 -0
  70. imap_processing/lo/l0/lo_science.py +25 -24
  71. imap_processing/lo/l1b/lo_l1b.py +93 -19
  72. imap_processing/lo/l1c/lo_l1c.py +273 -93
  73. imap_processing/lo/l2/lo_l2.py +949 -135
  74. imap_processing/lo/lo_ancillary.py +55 -0
  75. imap_processing/mag/l1a/mag_l1a.py +1 -0
  76. imap_processing/mag/l1a/mag_l1a_data.py +26 -0
  77. imap_processing/mag/l1b/mag_l1b.py +3 -2
  78. imap_processing/mag/l1c/interpolation_methods.py +14 -15
  79. imap_processing/mag/l1c/mag_l1c.py +23 -6
  80. imap_processing/mag/l1d/mag_l1d.py +57 -14
  81. imap_processing/mag/l1d/mag_l1d_data.py +202 -32
  82. imap_processing/mag/l2/mag_l2.py +2 -0
  83. imap_processing/mag/l2/mag_l2_data.py +14 -5
  84. imap_processing/quality_flags.py +23 -1
  85. imap_processing/spice/geometry.py +89 -39
  86. imap_processing/spice/pointing_frame.py +4 -8
  87. imap_processing/spice/repoint.py +78 -2
  88. imap_processing/spice/spin.py +28 -8
  89. imap_processing/spice/time.py +12 -22
  90. imap_processing/swapi/l1/swapi_l1.py +10 -4
  91. imap_processing/swapi/l2/swapi_l2.py +15 -17
  92. imap_processing/swe/l1b/swe_l1b.py +1 -2
  93. imap_processing/ultra/constants.py +30 -24
  94. imap_processing/ultra/l0/ultra_utils.py +9 -11
  95. imap_processing/ultra/l1a/ultra_l1a.py +1 -2
  96. imap_processing/ultra/l1b/badtimes.py +35 -11
  97. imap_processing/ultra/l1b/de.py +95 -31
  98. imap_processing/ultra/l1b/extendedspin.py +31 -16
  99. imap_processing/ultra/l1b/goodtimes.py +112 -0
  100. imap_processing/ultra/l1b/lookup_utils.py +281 -28
  101. imap_processing/ultra/l1b/quality_flag_filters.py +10 -1
  102. imap_processing/ultra/l1b/ultra_l1b.py +7 -7
  103. imap_processing/ultra/l1b/ultra_l1b_culling.py +169 -7
  104. imap_processing/ultra/l1b/ultra_l1b_extended.py +311 -69
  105. imap_processing/ultra/l1c/helio_pset.py +139 -37
  106. imap_processing/ultra/l1c/l1c_lookup_utils.py +289 -0
  107. imap_processing/ultra/l1c/spacecraft_pset.py +140 -29
  108. imap_processing/ultra/l1c/ultra_l1c.py +33 -24
  109. imap_processing/ultra/l1c/ultra_l1c_culling.py +92 -0
  110. imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +400 -292
  111. imap_processing/ultra/l2/ultra_l2.py +54 -11
  112. imap_processing/ultra/utils/ultra_l1_utils.py +37 -7
  113. imap_processing/utils.py +3 -4
  114. {imap_processing-0.18.0.dist-info → imap_processing-0.19.2.dist-info}/METADATA +2 -2
  115. {imap_processing-0.18.0.dist-info → imap_processing-0.19.2.dist-info}/RECORD +118 -109
  116. imap_processing/idex/idex_l2c.py +0 -84
  117. imap_processing/spice/kernels.py +0 -187
  118. imap_processing/ultra/l1b/cullingmask.py +0 -87
  119. imap_processing/ultra/l1c/histogram.py +0 -36
  120. {imap_processing-0.18.0.dist-info → imap_processing-0.19.2.dist-info}/LICENSE +0 -0
  121. {imap_processing-0.18.0.dist-info → imap_processing-0.19.2.dist-info}/WHEEL +0 -0
  122. {imap_processing-0.18.0.dist-info → imap_processing-0.19.2.dist-info}/entry_points.txt +0 -0
@@ -1,187 +0,0 @@
1
- """Functions that generate, furnish, and retrieve metadata from SPICE kernels."""
2
-
3
- import functools
4
- import logging
5
- import os
6
- from typing import Any, Callable, Optional, Union, overload
7
-
8
- import spiceypy
9
- from spiceypy.utils.exceptions import SpiceyError
10
-
11
- from imap_processing import imap_module_directory
12
-
13
- logger = logging.getLogger(__name__)
14
-
15
-
16
- # Declarations to help with typing. Taken from mypy documentation on
17
- # decorator-factories:
18
- # https://mypy.readthedocs.io/en/stable/generics.html#decorator-factories
19
- # Bare decorator usage
20
- @overload
21
- def ensure_spice(
22
- __func: Callable[..., Any],
23
- ) -> Callable[..., Any]: ... # numpydoc ignore=GL08
24
- # Decorator with arguments
25
- @overload
26
- def ensure_spice(
27
- *, time_kernels_only: bool = False
28
- ) -> Callable[[Callable[..., Any]], Callable[..., Any]]: ... # numpydoc ignore=GL08
29
- # Implementation
30
- def ensure_spice(
31
- __func: Optional[Callable[..., Any]] = None, *, time_kernels_only: bool = False
32
- ) -> Union[Callable[..., Any], Callable[[Callable[..., Any]], Callable[..., Any]]]:
33
- """
34
- Decorator/wrapper that automatically furnishes SPICE kernels.
35
-
36
- Parameters
37
- ----------
38
- __func : Callable
39
- The function requiring SPICE that we are going to wrap if being used
40
- explicitly, otherwise None, in which case ensure_spice is being used,
41
- not as a function wrapper (see l2a_processing.py) but as a true
42
- decorator without an explicit function argument.
43
- time_kernels_only : bool
44
- Specify that we only need to furnish time kernels (if SPICE_METAKERNEL
45
- is set, we still just furnish that metakernel and assume the time
46
- kernels are included.
47
-
48
- Returns
49
- -------
50
- Callable
51
- Decorated function, with spice error handling.
52
-
53
- Notes
54
- -----
55
- Before trying to understand this piece of code, read this:
56
- https://stackoverflow.com/questions/5929107/decorators-with-parameters/60832711#60832711
57
-
58
- **Control flow overview:**
59
-
60
- 1. Try simply calling the wrapped function naively.
61
- * SUCCESS? Great! We're done.
62
- * SpiceyError? Go to step 2.
63
-
64
- 2. Furnish metakernel at SPICE_METAKERNEL
65
- * SUCCESS? Great, return the original function again (so it can be
66
- re-run).
67
- * KeyError? Seems like SPICE_METAKERNEL isn't set, no problem. Go to
68
- step 3.
69
-
70
- 3. Did we get the parameter time_kernels_only=True?
71
- * YES? We only need LSK and SCLK kernels to run this function. Go fetch
72
- those and furnish and return the original function (so it can be re-run).
73
- * NO? Dang. This is sort of the end of the line. Re-raise the error
74
- generated from the failed spiceypy function call but add a better
75
- message to it.
76
-
77
- Examples
78
- --------
79
- There are three ways to use this object
80
-
81
- 1. A decorator with no arguments
82
-
83
- >>> @ensure_spice
84
- ... def my_spicey_func(a, b):
85
- ... pass
86
-
87
- 2. A decorator with parameters. This is useful
88
- if we only need the latest SCLK and LSK kernels for the function involved.
89
-
90
- >>> @ensure_spice(time_kernels_only=True)
91
- ... def my_spicey_time_func(a, b):
92
- ... pass
93
-
94
- 3. An explicit wrapper function, providing a dynamically set value for
95
- parameters, e.g. time_kernels_only
96
-
97
- >>> wrapped = ensure_spice(spicey_func, time_kernels_only=True)
98
- ... result = wrapped(args, kwargs)
99
- """
100
-
101
- def _decorator(func: Callable[..., Callable]) -> Callable:
102
- """
103
- Decorate or wrap input function depending on how ensure_spice is used.
104
-
105
- Parameters
106
- ----------
107
- func : Callable
108
- The function to be decorated/wrapped.
109
-
110
- Returns
111
- -------
112
- Callable
113
- If used as a function wrapper, the decorated function is returned.
114
- """
115
-
116
- @functools.wraps(func)
117
- def wrapper_ensure_spice(*args: Any, **kwargs: Any) -> Any:
118
- """
119
- Wrap the function that ensure_spice is used on.
120
-
121
- Parameters
122
- ----------
123
- *args : list
124
- The positional arguments passed to the decorated function.
125
- **kwargs
126
- The keyword arguments passed to the decorated function.
127
-
128
- Returns
129
- -------
130
- Object
131
- Output from wrapped function.
132
- """
133
- try:
134
- # Step 1.
135
- return func(
136
- *args, **kwargs
137
- ) # Naive first try. Maybe SPICE is already furnished.
138
- except SpiceyError as spicey_err:
139
- try:
140
- # Step 2.
141
- if os.getenv("SPICE_METAKERNEL"):
142
- metakernel_path = os.getenv("SPICE_METAKERNEL")
143
- spiceypy.furnsh(metakernel_path)
144
- else:
145
- furnish_time_kernel()
146
- except KeyError:
147
- # TODO: An additional step that was used on EMUS was to get
148
- # a custom metakernel from the SDC API based on an input
149
- # time range.
150
- if time_kernels_only:
151
- # Step 3.
152
- # TODO: Decide if this is useful for IMAP. Possible
153
- # implementation could include downloading
154
- # the most recent leapsecond kernel from NAIF (see:
155
- # https://lasp.colorado.edu/nucleus/projects/LIBSDC/repos/libera_utils/browse/libera_utils/spice_utils.py
156
- # for LIBERA implementation of downloading and caching
157
- # kernels) and finding the most recent IMAP clock
158
- # kernel in EFS.
159
- raise NotImplementedError from spicey_err
160
- else:
161
- raise SpiceyError(
162
- "When calling a function requiring SPICE, we failed "
163
- "to load a metakernel. SPICE_METAKERNEL is not set,"
164
- "and time_kernels_only is not set to True"
165
- ) from spicey_err
166
- return func(*args, **kwargs)
167
-
168
- return wrapper_ensure_spice
169
-
170
- # Note: This return was originally implemented as a ternary operator, but
171
- # this caused mypy to fail due to this bug:
172
- # https://github.com/python/mypy/issues/4134
173
- if callable(__func):
174
- return _decorator(__func)
175
- else:
176
- return _decorator
177
-
178
-
179
- def furnish_time_kernel() -> None:
180
- """Furnish the time kernels."""
181
- spice_test_data_path = imap_module_directory / "tests/spice/test_data"
182
-
183
- # TODO: we need to load these kernels from EFS volumen that is
184
- # mounted to batch volume and extend this to generate metakernell
185
- # which is TBD.
186
- spiceypy.furnsh(str(spice_test_data_path / "imap_sclk_0000.tsc"))
187
- spiceypy.furnsh(str(spice_test_data_path / "naif0012.tls"))
@@ -1,87 +0,0 @@
1
- """Calculate Culling Mask."""
2
-
3
- import numpy as np
4
- import xarray as xr
5
-
6
- from imap_processing.ultra.l1b.quality_flag_filters import QUALITY_FLAG_FILTERS
7
- from imap_processing.ultra.utils.ultra_l1_utils import create_dataset, extract_data_dict
8
-
9
- FILLVAL_UINT16 = 65535
10
- FILLVAL_FLOAT64 = -1.0e31
11
- FILLVAL_UINT32 = 4294967295
12
-
13
-
14
- def calculate_cullingmask(extendedspin_dataset: xr.Dataset, name: str) -> xr.Dataset:
15
- """
16
- Create dataset with defined datatype for Culling Mask Data.
17
-
18
- Parameters
19
- ----------
20
- extendedspin_dataset : xarray.Dataset
21
- Dataset containing the data.
22
- name : str
23
- Name of the dataset.
24
-
25
- Returns
26
- -------
27
- cullingmask_dataset : xarray.Dataset
28
- Dataset containing the extendedspin data that remains after culling.
29
- """
30
- # If the spin rate was too high or low then the spin should be thrown out.
31
- # If the rates at any energy level are too high then throw out the entire spin.
32
- good_mask = (
33
- (
34
- extendedspin_dataset["quality_attitude"]
35
- & sum(flag.value for flag in QUALITY_FLAG_FILTERS["quality_attitude"])
36
- )
37
- == 0
38
- ) & (
39
- (
40
- (
41
- extendedspin_dataset["quality_ena_rates"]
42
- & sum(flag.value for flag in QUALITY_FLAG_FILTERS["quality_ena_rates"])
43
- )
44
- == 0
45
- ).all(dim="energy_bin_geometric_mean")
46
- )
47
- extendedspin_dataset = extendedspin_dataset.assign_coords(
48
- epoch=("spin_number", extendedspin_dataset["epoch"].values)
49
- )
50
- filtered_dataset = extendedspin_dataset.sel(
51
- spin_number=extendedspin_dataset["spin_number"][good_mask]
52
- )
53
-
54
- data_dict = extract_data_dict(filtered_dataset)
55
-
56
- cullingmask_dataset = create_dataset(data_dict, name, "l1b")
57
-
58
- if cullingmask_dataset["spin_number"].size == 0:
59
- cullingmask_dataset = cullingmask_dataset.drop_dims("spin_number")
60
- cullingmask_dataset = cullingmask_dataset.expand_dims(
61
- spin_number=[FILLVAL_UINT32]
62
- )
63
- cullingmask_dataset = cullingmask_dataset.assign_coords(
64
- epoch=("spin_number", [extendedspin_dataset["epoch"].values[0]])
65
- )
66
- cullingmask_dataset["spin_start_time"] = xr.DataArray(
67
- np.array([FILLVAL_FLOAT64], dtype="float64"), dims=["spin_number"]
68
- )
69
- cullingmask_dataset["spin_period"] = xr.DataArray(
70
- np.array([FILLVAL_FLOAT64], dtype="float64"), dims=["spin_number"]
71
- )
72
- cullingmask_dataset["spin_rate"] = xr.DataArray(
73
- np.array([FILLVAL_FLOAT64], dtype="float64"), dims=["spin_number"]
74
- )
75
- cullingmask_dataset["quality_attitude"] = xr.DataArray(
76
- np.array([FILLVAL_UINT16], dtype="uint16"), dims=["spin_number"]
77
- )
78
- cullingmask_dataset["quality_ena_rates"] = (
79
- ("energy_bin_geometric_mean", "spin_number"),
80
- np.full((3, 1), FILLVAL_UINT16, dtype="uint16"),
81
- )
82
- cullingmask_dataset["ena_rates"] = (
83
- ("energy_bin_geometric_mean", "spin_number"),
84
- np.full((3, 1), FILLVAL_FLOAT64, dtype="float64"),
85
- )
86
-
87
- return cullingmask_dataset
@@ -1,36 +0,0 @@
1
- """Calculate Histogram."""
2
-
3
- import numpy as np
4
- import xarray as xr
5
-
6
- from imap_processing.ultra.utils.ultra_l1_utils import create_dataset
7
-
8
-
9
- def calculate_histogram(histogram_dataset: xr.Dataset, name: str) -> xr.Dataset:
10
- """
11
- Create dictionary with defined datatype for Histogram Data.
12
-
13
- Parameters
14
- ----------
15
- histogram_dataset : xarray.Dataset
16
- Dataset containing histogram data.
17
- name : str
18
- Name of dataset.
19
-
20
- Returns
21
- -------
22
- dataset : xarray.Dataset
23
- Dataset containing the data.
24
- """
25
- histogram_dict = {}
26
-
27
- # Placeholder for calculations
28
- # TODO: come back and update this data structure.
29
- epoch = histogram_dataset.coords["epoch"].values
30
-
31
- histogram_dict["epoch"] = epoch
32
- histogram_dict["sid"] = np.zeros(len(epoch), dtype=np.uint8)
33
-
34
- dataset = create_dataset(histogram_dict, name, "l1c")
35
-
36
- return dataset