imap-processing 0.12.0__py3-none-any.whl → 0.13.0__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.
- imap_processing/__init__.py +1 -0
- imap_processing/_version.py +2 -2
- imap_processing/ccsds/ccsds_data.py +1 -2
- imap_processing/ccsds/excel_to_xtce.py +1 -2
- imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +18 -12
- imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +569 -0
- imap_processing/cdf/config/imap_codice_l1b_variable_attrs.yaml +1846 -128
- imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +5 -5
- imap_processing/cdf/config/imap_idex_global_cdf_attrs.yaml +20 -1
- imap_processing/cdf/config/imap_idex_l1a_variable_attrs.yaml +6 -4
- imap_processing/cdf/config/imap_idex_l1b_variable_attrs.yaml +3 -3
- imap_processing/cdf/config/imap_mag_global_cdf_attrs.yaml +15 -0
- imap_processing/cdf/config/imap_swapi_variable_attrs.yaml +22 -0
- imap_processing/cdf/config/imap_swe_l1b_variable_attrs.yaml +16 -0
- imap_processing/cdf/config/imap_ultra_global_cdf_attrs.yaml +178 -5
- imap_processing/cdf/config/imap_ultra_l1a_variable_attrs.yaml +5045 -41
- imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +33 -19
- imap_processing/cdf/config/imap_ultra_l1c_variable_attrs.yaml +8 -48
- imap_processing/cdf/utils.py +41 -33
- imap_processing/cli.py +463 -234
- imap_processing/codice/codice_l1a.py +260 -47
- imap_processing/codice/codice_l1b.py +51 -152
- imap_processing/codice/constants.py +38 -1
- imap_processing/ena_maps/ena_maps.py +658 -65
- imap_processing/ena_maps/utils/coordinates.py +1 -1
- imap_processing/ena_maps/utils/spatial_utils.py +10 -5
- imap_processing/glows/l1a/glows_l1a.py +28 -99
- imap_processing/glows/l1a/glows_l1a_data.py +2 -2
- imap_processing/glows/l1b/glows_l1b.py +1 -4
- imap_processing/glows/l1b/glows_l1b_data.py +1 -3
- imap_processing/glows/l2/glows_l2.py +2 -5
- imap_processing/hi/l1a/hi_l1a.py +31 -12
- imap_processing/hi/l1b/hi_l1b.py +80 -43
- imap_processing/hi/l1c/hi_l1c.py +12 -16
- imap_processing/hit/ancillary/imap_hit_l1b-to-l2-sector-dt0-factors_20250219_v002.csv +81 -0
- imap_processing/hit/hit_utils.py +93 -35
- imap_processing/hit/l0/decom_hit.py +3 -1
- imap_processing/hit/l1a/hit_l1a.py +30 -25
- imap_processing/hit/l1b/constants.py +6 -2
- imap_processing/hit/l1b/hit_l1b.py +279 -318
- imap_processing/hit/l2/constants.py +37 -0
- imap_processing/hit/l2/hit_l2.py +373 -264
- imap_processing/ialirt/l0/parse_mag.py +138 -10
- imap_processing/ialirt/l0/process_swapi.py +69 -0
- imap_processing/ialirt/l0/process_swe.py +318 -22
- imap_processing/ialirt/packet_definitions/ialirt.xml +216 -212
- imap_processing/ialirt/packet_definitions/ialirt_codicehi.xml +1 -1
- imap_processing/ialirt/packet_definitions/ialirt_codicelo.xml +1 -1
- imap_processing/ialirt/packet_definitions/ialirt_swapi.xml +14 -14
- imap_processing/ialirt/utils/grouping.py +1 -1
- imap_processing/idex/idex_constants.py +9 -1
- imap_processing/idex/idex_l0.py +22 -8
- imap_processing/idex/idex_l1a.py +75 -44
- imap_processing/idex/idex_l1b.py +9 -8
- imap_processing/idex/idex_l2a.py +79 -45
- imap_processing/idex/idex_l2b.py +120 -0
- imap_processing/idex/idex_variable_unpacking_and_eu_conversion.csv +33 -39
- imap_processing/idex/packet_definitions/idex_housekeeping_packet_definition.xml +9130 -0
- imap_processing/lo/l0/lo_science.py +1 -2
- imap_processing/lo/l1a/lo_l1a.py +1 -4
- imap_processing/lo/l1b/lo_l1b.py +527 -6
- imap_processing/lo/l1b/tof_conversions.py +11 -0
- imap_processing/lo/l1c/lo_l1c.py +1 -4
- imap_processing/mag/constants.py +43 -0
- imap_processing/mag/imap_mag_sdc_configuration_v001.py +8 -0
- imap_processing/mag/l1a/mag_l1a.py +2 -9
- imap_processing/mag/l1a/mag_l1a_data.py +10 -10
- imap_processing/mag/l1b/mag_l1b.py +84 -17
- imap_processing/mag/l1c/interpolation_methods.py +180 -3
- imap_processing/mag/l1c/mag_l1c.py +236 -70
- imap_processing/mag/l2/mag_l2.py +140 -0
- imap_processing/mag/l2/mag_l2_data.py +288 -0
- imap_processing/spacecraft/quaternions.py +1 -3
- imap_processing/spice/geometry.py +3 -3
- imap_processing/spice/kernels.py +0 -276
- imap_processing/spice/pointing_frame.py +257 -0
- imap_processing/spice/repoint.py +48 -19
- imap_processing/spice/spin.py +38 -33
- imap_processing/spice/time.py +24 -0
- imap_processing/swapi/l1/swapi_l1.py +16 -12
- imap_processing/swapi/l2/swapi_l2.py +116 -4
- imap_processing/swapi/swapi_utils.py +32 -0
- imap_processing/swe/l1a/swe_l1a.py +2 -9
- imap_processing/swe/l1a/swe_science.py +8 -11
- imap_processing/swe/l1b/swe_l1b.py +898 -23
- imap_processing/swe/l2/swe_l2.py +21 -77
- imap_processing/swe/utils/swe_constants.py +1 -0
- imap_processing/tests/ccsds/test_excel_to_xtce.py +1 -1
- imap_processing/tests/cdf/test_utils.py +14 -16
- imap_processing/tests/codice/conftest.py +44 -33
- imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-pha_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-pha_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/test_codice_l1a.py +20 -11
- imap_processing/tests/codice/test_codice_l1b.py +6 -7
- imap_processing/tests/conftest.py +78 -22
- imap_processing/tests/ena_maps/test_ena_maps.py +462 -33
- imap_processing/tests/ena_maps/test_spatial_utils.py +1 -1
- imap_processing/tests/glows/conftest.py +10 -14
- imap_processing/tests/glows/test_glows_decom.py +4 -4
- imap_processing/tests/glows/test_glows_l1a_cdf.py +6 -27
- imap_processing/tests/glows/test_glows_l1a_data.py +6 -8
- imap_processing/tests/glows/test_glows_l1b.py +11 -11
- imap_processing/tests/glows/test_glows_l1b_data.py +5 -5
- imap_processing/tests/glows/test_glows_l2.py +2 -8
- imap_processing/tests/hi/conftest.py +1 -1
- imap_processing/tests/hi/test_hi_l1b.py +10 -12
- imap_processing/tests/hi/test_hi_l1c.py +27 -24
- imap_processing/tests/hi/test_l1a.py +7 -9
- imap_processing/tests/hi/test_science_direct_event.py +2 -2
- imap_processing/tests/hit/helpers/l1_validation.py +44 -43
- imap_processing/tests/hit/test_decom_hit.py +1 -1
- imap_processing/tests/hit/test_hit_l1a.py +9 -9
- imap_processing/tests/hit/test_hit_l1b.py +172 -217
- imap_processing/tests/hit/test_hit_l2.py +380 -118
- imap_processing/tests/hit/test_hit_utils.py +122 -55
- imap_processing/tests/hit/validation_data/hit_l1b_standard_sample2_nsrl_v4_3decimals.csv +62 -62
- imap_processing/tests/hit/validation_data/sci_sample_raw.csv +1 -1
- imap_processing/tests/ialirt/unit/test_decom_ialirt.py +16 -81
- imap_processing/tests/ialirt/unit/test_grouping.py +2 -2
- imap_processing/tests/ialirt/unit/test_parse_mag.py +71 -16
- imap_processing/tests/ialirt/unit/test_process_codicehi.py +3 -3
- imap_processing/tests/ialirt/unit/test_process_codicelo.py +3 -10
- imap_processing/tests/ialirt/unit/test_process_ephemeris.py +4 -4
- imap_processing/tests/ialirt/unit/test_process_hit.py +3 -3
- imap_processing/tests/ialirt/unit/test_process_swapi.py +24 -16
- imap_processing/tests/ialirt/unit/test_process_swe.py +115 -7
- imap_processing/tests/idex/conftest.py +72 -7
- imap_processing/tests/idex/test_data/imap_idex_l0_raw_20241206_v001.pkts +0 -0
- imap_processing/tests/idex/test_data/imap_idex_l0_raw_20250108_v001.pkts +0 -0
- imap_processing/tests/idex/test_idex_l0.py +33 -11
- imap_processing/tests/idex/test_idex_l1a.py +50 -23
- imap_processing/tests/idex/test_idex_l1b.py +104 -25
- imap_processing/tests/idex/test_idex_l2a.py +48 -32
- imap_processing/tests/idex/test_idex_l2b.py +93 -0
- imap_processing/tests/lo/test_lo_l1a.py +3 -3
- imap_processing/tests/lo/test_lo_l1b.py +371 -6
- imap_processing/tests/lo/test_lo_l1c.py +1 -1
- imap_processing/tests/lo/test_lo_science.py +6 -7
- imap_processing/tests/lo/test_star_sensor.py +1 -1
- imap_processing/tests/mag/conftest.py +58 -9
- imap_processing/tests/mag/test_mag_decom.py +4 -3
- imap_processing/tests/mag/test_mag_l1a.py +13 -7
- imap_processing/tests/mag/test_mag_l1b.py +9 -9
- imap_processing/tests/mag/test_mag_l1c.py +151 -47
- imap_processing/tests/mag/test_mag_l2.py +130 -0
- imap_processing/tests/mag/test_mag_validation.py +144 -7
- imap_processing/tests/mag/validation/L1c/T013/mag-l1b-l1c-t013-magi-normal-in.csv +1217 -0
- imap_processing/tests/mag/validation/L1c/T013/mag-l1b-l1c-t013-magi-normal-out.csv +1857 -0
- imap_processing/tests/mag/validation/L1c/T013/mag-l1b-l1c-t013-mago-normal-in.csv +1217 -0
- imap_processing/tests/mag/validation/L1c/T013/mag-l1b-l1c-t013-mago-normal-out.csv +1857 -0
- imap_processing/tests/mag/validation/L1c/T014/mag-l1b-l1c-t014-magi-normal-in.csv +1217 -0
- imap_processing/tests/mag/validation/L1c/T014/mag-l1b-l1c-t014-magi-normal-out.csv +1793 -0
- imap_processing/tests/mag/validation/L1c/T014/mag-l1b-l1c-t014-mago-normal-in.csv +1217 -0
- imap_processing/tests/mag/validation/L1c/T014/mag-l1b-l1c-t014-mago-normal-out.csv +1793 -0
- imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-magi-burst-in.csv +2561 -0
- imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-magi-normal-in.csv +961 -0
- imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-magi-normal-out.csv +1539 -0
- imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-mago-normal-in.csv +1921 -0
- imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-mago-normal-out.csv +2499 -0
- imap_processing/tests/mag/validation/L1c/T016/mag-l1b-l1c-t016-magi-normal-in.csv +865 -0
- imap_processing/tests/mag/validation/L1c/T016/mag-l1b-l1c-t016-magi-normal-out.csv +1196 -0
- imap_processing/tests/mag/validation/L1c/T016/mag-l1b-l1c-t016-mago-normal-in.csv +1729 -0
- imap_processing/tests/mag/validation/L1c/T016/mag-l1b-l1c-t016-mago-normal-out.csv +3053 -0
- imap_processing/tests/mag/validation/L2/imap_mag_l1b_norm-mago_20251017_v002.cdf +0 -0
- imap_processing/tests/mag/validation/calibration/imap_mag_l2-calibration-matrices_20251017_v004.cdf +0 -0
- imap_processing/tests/mag/validation/calibration/imap_mag_l2-offsets-norm_20251017_20251017_v001.cdf +0 -0
- imap_processing/tests/spacecraft/test_quaternions.py +1 -1
- imap_processing/tests/spice/test_data/fake_repoint_data.csv +4 -4
- imap_processing/tests/spice/test_data/fake_spin_data.csv +11 -11
- imap_processing/tests/spice/test_geometry.py +3 -3
- imap_processing/tests/spice/test_kernels.py +1 -200
- imap_processing/tests/spice/test_pointing_frame.py +185 -0
- imap_processing/tests/spice/test_repoint.py +20 -10
- imap_processing/tests/spice/test_spin.py +50 -9
- imap_processing/tests/spice/test_time.py +14 -0
- imap_processing/tests/swapi/lut/imap_swapi_esa-unit-conversion_20250211_v000.csv +73 -0
- imap_processing/tests/swapi/lut/imap_swapi_lut-notes_20250211_v000.csv +1025 -0
- imap_processing/tests/swapi/test_swapi_l1.py +7 -9
- imap_processing/tests/swapi/test_swapi_l2.py +180 -8
- imap_processing/tests/swe/lut/checker-board-indices.csv +24 -0
- imap_processing/tests/swe/lut/imap_swe_esa-lut_20250301_v000.csv +385 -0
- imap_processing/tests/swe/lut/imap_swe_l1b-in-flight-cal_20240510_20260716_v000.csv +3 -0
- imap_processing/tests/swe/test_swe_l1a.py +6 -6
- imap_processing/tests/swe/test_swe_l1a_science.py +3 -3
- imap_processing/tests/swe/test_swe_l1b.py +162 -24
- imap_processing/tests/swe/test_swe_l2.py +82 -102
- imap_processing/tests/test_cli.py +171 -88
- imap_processing/tests/test_utils.py +2 -1
- imap_processing/tests/ultra/data/mock_data.py +49 -21
- imap_processing/tests/ultra/unit/conftest.py +53 -70
- imap_processing/tests/ultra/unit/test_badtimes.py +2 -4
- imap_processing/tests/ultra/unit/test_cullingmask.py +4 -6
- imap_processing/tests/ultra/unit/test_de.py +3 -10
- imap_processing/tests/ultra/unit/test_decom_apid_880.py +27 -76
- imap_processing/tests/ultra/unit/test_decom_apid_881.py +15 -16
- imap_processing/tests/ultra/unit/test_decom_apid_883.py +12 -10
- imap_processing/tests/ultra/unit/test_decom_apid_896.py +202 -55
- imap_processing/tests/ultra/unit/test_lookup_utils.py +23 -1
- imap_processing/tests/ultra/unit/test_spacecraft_pset.py +3 -4
- imap_processing/tests/ultra/unit/test_ultra_l1a.py +84 -307
- imap_processing/tests/ultra/unit/test_ultra_l1b.py +30 -12
- imap_processing/tests/ultra/unit/test_ultra_l1b_annotated.py +2 -2
- imap_processing/tests/ultra/unit/test_ultra_l1b_culling.py +4 -1
- imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py +163 -29
- imap_processing/tests/ultra/unit/test_ultra_l1c.py +5 -5
- imap_processing/tests/ultra/unit/test_ultra_l1c_pset_bins.py +32 -43
- imap_processing/tests/ultra/unit/test_ultra_l2.py +230 -0
- imap_processing/ultra/constants.py +1 -1
- imap_processing/ultra/l0/decom_tools.py +21 -34
- imap_processing/ultra/l0/decom_ultra.py +168 -204
- imap_processing/ultra/l0/ultra_utils.py +152 -136
- imap_processing/ultra/l1a/ultra_l1a.py +55 -243
- imap_processing/ultra/l1b/badtimes.py +1 -4
- imap_processing/ultra/l1b/cullingmask.py +2 -6
- imap_processing/ultra/l1b/de.py +62 -47
- imap_processing/ultra/l1b/extendedspin.py +8 -4
- imap_processing/ultra/l1b/lookup_utils.py +72 -9
- imap_processing/ultra/l1b/ultra_l1b.py +3 -8
- imap_processing/ultra/l1b/ultra_l1b_culling.py +4 -4
- imap_processing/ultra/l1b/ultra_l1b_extended.py +236 -78
- imap_processing/ultra/l1c/histogram.py +2 -6
- imap_processing/ultra/l1c/spacecraft_pset.py +2 -4
- imap_processing/ultra/l1c/ultra_l1c.py +1 -5
- imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +107 -60
- imap_processing/ultra/l2/ultra_l2.py +299 -0
- imap_processing/ultra/lookup_tables/Angular_Profiles_FM45_LeftSlit.csv +526 -0
- imap_processing/ultra/lookup_tables/Angular_Profiles_FM45_RightSlit.csv +526 -0
- imap_processing/ultra/lookup_tables/Angular_Profiles_FM90_LeftSlit.csv +526 -0
- imap_processing/ultra/lookup_tables/Angular_Profiles_FM90_RightSlit.csv +526 -0
- imap_processing/ultra/lookup_tables/FM45_Startup1_ULTRA_IMGPARAMS_20240719.csv +2 -2
- imap_processing/ultra/lookup_tables/FM90_Startup1_ULTRA_IMGPARAMS_20240719.csv +2 -0
- imap_processing/ultra/packet_definitions/README.md +38 -0
- imap_processing/ultra/packet_definitions/ULTRA_SCI_COMBINED.xml +15302 -482
- imap_processing/ultra/utils/ultra_l1_utils.py +13 -12
- imap_processing/utils.py +1 -1
- {imap_processing-0.12.0.dist-info → imap_processing-0.13.0.dist-info}/METADATA +3 -2
- {imap_processing-0.12.0.dist-info → imap_processing-0.13.0.dist-info}/RECORD +264 -225
- imap_processing/hi/l1b/hi_eng_unit_convert_table.csv +0 -154
- imap_processing/mag/imap_mag_sdc-configuration_v001.yaml +0 -6
- imap_processing/mag/l1b/__init__.py +0 -0
- imap_processing/swe/l1b/swe_esa_lookup_table.csv +0 -1441
- imap_processing/swe/l1b/swe_l1b_science.py +0 -699
- imap_processing/tests/swe/test_swe_l1b_science.py +0 -103
- imap_processing/ultra/lookup_tables/dps_sensitivity45.cdf +0 -0
- imap_processing/ultra/lookup_tables/ultra_90_dps_exposure_compressed.cdf +0 -0
- /imap_processing/idex/packet_definitions/{idex_packet_definition.xml → idex_science_packet_definition.xml} +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/20240827095047_SWE_IALIRT_packet.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/461971383-404.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/461971384-405.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/461971385-406.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/461971386-407.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/461971387-408.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/461971388-409.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/461971389-410.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/461971390-411.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/461971391-412.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/BinLog CCSDS_FRAG_TLM_20240826_152323Z_IALIRT_data_for_SDC.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/IALiRT Raw Packet Telemetry.txt +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/apid01152.tlm +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/eu_SWP_IAL_20240826_152033.csv +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/hi_fsw_view_1_ccsds.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/hit_ialirt_sample.ccsds +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/hit_ialirt_sample.csv +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/idle_export_eu.SWE_IALIRT_20240827_093852.csv +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/imap_codice_l1a_hi-ialirt_20240523200000_v0.0.0.cdf +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/imap_codice_l1a_lo-ialirt_20241110193700_v0.0.0.cdf +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/sample_decoded_i-alirt_data.csv +0 -0
- /imap_processing/tests/mag/validation/{imap_calibration_mag_20240229_v01.cdf → calibration/imap_mag_l1b-calibration_20240229_v001.cdf} +0 -0
- /imap_processing/{swe/l1b/engineering_unit_convert_table.csv → tests/swe/lut/imap_swe_eu-conversion_20240510_v000.csv} +0 -0
- {imap_processing-0.12.0.dist-info → imap_processing-0.13.0.dist-info}/LICENSE +0 -0
- {imap_processing-0.12.0.dist-info → imap_processing-0.13.0.dist-info}/WHEEL +0 -0
- {imap_processing-0.12.0.dist-info → imap_processing-0.13.0.dist-info}/entry_points.txt +0 -0
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import json
|
|
6
|
+
import tempfile
|
|
5
7
|
from copy import deepcopy
|
|
8
|
+
from pathlib import Path
|
|
6
9
|
from unittest import mock
|
|
7
10
|
|
|
8
11
|
import astropy_healpix.healpy as hp
|
|
@@ -10,6 +13,7 @@ import numpy as np
|
|
|
10
13
|
import pytest
|
|
11
14
|
import xarray as xr
|
|
12
15
|
|
|
16
|
+
from imap_processing.cdf.utils import load_cdf, write_cdf
|
|
13
17
|
from imap_processing.ena_maps import ena_maps
|
|
14
18
|
from imap_processing.ena_maps.utils import spatial_utils
|
|
15
19
|
from imap_processing.ena_maps.utils.coordinates import CoordNames
|
|
@@ -52,8 +56,8 @@ class TestUltraPointingSet:
|
|
|
52
56
|
"""Test instantiation of UltraPointingSet"""
|
|
53
57
|
ultra_psets = [
|
|
54
58
|
ena_maps.UltraPointingSet(
|
|
59
|
+
l1c_product,
|
|
55
60
|
spice_reference_frame=geometry.SpiceFrame.IMAP_DPS,
|
|
56
|
-
l1c_dataset=l1c_product,
|
|
57
61
|
)
|
|
58
62
|
for l1c_product in self.l1c_pset_products
|
|
59
63
|
]
|
|
@@ -78,15 +82,40 @@ class TestUltraPointingSet:
|
|
|
78
82
|
# Check that the unwrapped_dims_dict is as expected
|
|
79
83
|
assert ultra_pset.unwrapped_dims_dict["counts"] == (
|
|
80
84
|
"epoch",
|
|
81
|
-
"
|
|
85
|
+
"energy_bin_geometric_mean",
|
|
82
86
|
"pixel",
|
|
83
87
|
)
|
|
84
88
|
# Check the non_spatial_coords are as expected
|
|
85
89
|
assert tuple(ultra_pset.non_spatial_coords.keys()) == (
|
|
86
90
|
"epoch",
|
|
87
|
-
"
|
|
91
|
+
"energy_bin_geometric_mean",
|
|
88
92
|
)
|
|
89
93
|
|
|
94
|
+
@pytest.mark.usefixtures("_setup_ultra_l1c_pset_products")
|
|
95
|
+
def test_init_cdf(
|
|
96
|
+
self,
|
|
97
|
+
):
|
|
98
|
+
ultra_pset = self.l1c_pset_products[0]
|
|
99
|
+
|
|
100
|
+
cdf_filepath = write_cdf(ultra_pset, istp=False)
|
|
101
|
+
|
|
102
|
+
ultra_pset_from_dataset = ena_maps.UltraPointingSet(ultra_pset)
|
|
103
|
+
|
|
104
|
+
ultra_pset_from_str = ena_maps.UltraPointingSet(cdf_filepath)
|
|
105
|
+
ultra_pset_from_path = ena_maps.UltraPointingSet(Path(cdf_filepath))
|
|
106
|
+
|
|
107
|
+
np.testing.assert_allclose(
|
|
108
|
+
ultra_pset_from_dataset.data["counts"].values,
|
|
109
|
+
ultra_pset_from_str.data["counts"].values,
|
|
110
|
+
rtol=1e-6,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
np.testing.assert_allclose(
|
|
114
|
+
ultra_pset_from_dataset.data["counts"].values,
|
|
115
|
+
ultra_pset_from_path.data["counts"].values,
|
|
116
|
+
rtol=1e-6,
|
|
117
|
+
)
|
|
118
|
+
|
|
90
119
|
@pytest.mark.usefixtures("_setup_ultra_l1c_pset_products")
|
|
91
120
|
@pytest.mark.usefixtures("_setup_ultra_l1c_pset_products")
|
|
92
121
|
def test_different_spacing_raises_error(self):
|
|
@@ -100,11 +129,44 @@ class TestUltraPointingSet:
|
|
|
100
129
|
|
|
101
130
|
with pytest.raises(ValueError, match="do not match"):
|
|
102
131
|
ena_maps.UltraPointingSet(
|
|
132
|
+
ultra_pset_ds,
|
|
103
133
|
spice_reference_frame=geometry.SpiceFrame.IMAP_DPS,
|
|
104
|
-
l1c_dataset=ultra_pset_ds,
|
|
105
134
|
)
|
|
106
135
|
|
|
107
136
|
|
|
137
|
+
@pytest.fixture(scope="module")
|
|
138
|
+
def hi_pset_cdf_path(imap_tests_path):
|
|
139
|
+
return imap_tests_path / "hi/data/l1/imap_hi_l1c_45sensor-pset_20250415_v999.cdf"
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@pytest.mark.external_test_data
|
|
143
|
+
class TestHiPointingSet:
|
|
144
|
+
"""Test suite for HiPointingSet class."""
|
|
145
|
+
|
|
146
|
+
def test_init(self, hi_pset_cdf_path):
|
|
147
|
+
"""Test coverage for __init__ method."""
|
|
148
|
+
pset_ds = load_cdf(hi_pset_cdf_path)
|
|
149
|
+
hi_pset = ena_maps.HiPointingSet(pset_ds)
|
|
150
|
+
assert isinstance(hi_pset, ena_maps.HiPointingSet)
|
|
151
|
+
assert hi_pset.spice_reference_frame == geometry.SpiceFrame.ECLIPJ2000
|
|
152
|
+
assert hi_pset.num_points == 3600
|
|
153
|
+
np.testing.assert_array_equal(hi_pset.az_el_points.shape, (3600, 2))
|
|
154
|
+
|
|
155
|
+
def test_from_cdf(self, hi_pset_cdf_path):
|
|
156
|
+
"""Test coverage for from_cdf method."""
|
|
157
|
+
hi_pset = ena_maps.HiPointingSet(hi_pset_cdf_path)
|
|
158
|
+
assert isinstance(hi_pset, ena_maps.HiPointingSet)
|
|
159
|
+
|
|
160
|
+
def test_plays_nice_with_rectangular_sky_map(self, hi_pset_cdf_path):
|
|
161
|
+
"""Test that HiPointingSet works with RectangularSkyMap"""
|
|
162
|
+
hi_pset = ena_maps.HiPointingSet(hi_pset_cdf_path)
|
|
163
|
+
rect_map = ena_maps.RectangularSkyMap(
|
|
164
|
+
spacing_deg=2, spice_frame=geometry.SpiceFrame.ECLIPJ2000
|
|
165
|
+
)
|
|
166
|
+
rect_map.project_pset_values_to_map(hi_pset, ["counts", "exposure_times"])
|
|
167
|
+
assert rect_map.data_1d["counts"].max() > 0
|
|
168
|
+
|
|
169
|
+
|
|
108
170
|
class TestRectangularSkyMap:
|
|
109
171
|
@pytest.fixture(autouse=True)
|
|
110
172
|
def _setup_ultra_l1c_pset_products(self, setup_all_pset_products):
|
|
@@ -115,8 +177,8 @@ class TestRectangularSkyMap:
|
|
|
115
177
|
)
|
|
116
178
|
self.ultra_psets = [
|
|
117
179
|
ena_maps.UltraPointingSet(
|
|
180
|
+
l1c_product,
|
|
118
181
|
spice_reference_frame=geometry.SpiceFrame.IMAP_DPS,
|
|
119
|
-
l1c_dataset=l1c_product,
|
|
120
182
|
)
|
|
121
183
|
for l1c_product in self.ultra_l1c_pset_products
|
|
122
184
|
]
|
|
@@ -130,8 +192,8 @@ class TestRectangularSkyMap:
|
|
|
130
192
|
)
|
|
131
193
|
self.rectangular_psets = [
|
|
132
194
|
ena_maps.RectangularPointingSet(
|
|
195
|
+
l1c_product,
|
|
133
196
|
spice_reference_frame=geometry.SpiceFrame.IMAP_DPS,
|
|
134
|
-
l1c_dataset=l1c_product,
|
|
135
197
|
)
|
|
136
198
|
for l1c_product in self.rectangular_l1c_pset_products
|
|
137
199
|
]
|
|
@@ -188,7 +250,7 @@ class TestRectangularSkyMap:
|
|
|
188
250
|
for ultra_pset in self.ultra_psets:
|
|
189
251
|
rectangular_map.project_pset_values_to_map(
|
|
190
252
|
ultra_pset,
|
|
191
|
-
value_keys=["counts", "
|
|
253
|
+
value_keys=["counts", "exposure_factor"],
|
|
192
254
|
index_match_method=index_matching_method,
|
|
193
255
|
)
|
|
194
256
|
|
|
@@ -199,20 +261,22 @@ class TestRectangularSkyMap:
|
|
|
199
261
|
simple_summed_pset_counts_by_energy = np.zeros(
|
|
200
262
|
shape=(
|
|
201
263
|
self.ultra_l1c_pset_products[0]["counts"].sizes[
|
|
202
|
-
CoordNames.
|
|
264
|
+
CoordNames.ENERGY_ULTRA.value
|
|
203
265
|
],
|
|
204
266
|
)
|
|
205
267
|
)
|
|
206
268
|
for pset in self.ultra_l1c_pset_products:
|
|
207
269
|
simple_summed_pset_counts_by_energy += pset["counts"].sum(
|
|
208
|
-
dim=[
|
|
270
|
+
dim=[
|
|
271
|
+
d for d in pset["counts"].dims if d != CoordNames.ENERGY_ULTRA.value
|
|
272
|
+
]
|
|
209
273
|
)
|
|
210
274
|
|
|
211
275
|
rmap_counts_per_energy_bin = rectangular_map.data_1d["counts"].sum(
|
|
212
276
|
dim=[
|
|
213
277
|
d
|
|
214
278
|
for d in rectangular_map.data_1d["counts"].dims
|
|
215
|
-
if d != CoordNames.
|
|
279
|
+
if d != CoordNames.ENERGY_ULTRA.value
|
|
216
280
|
]
|
|
217
281
|
)
|
|
218
282
|
|
|
@@ -235,7 +299,7 @@ class TestRectangularSkyMap:
|
|
|
235
299
|
"""
|
|
236
300
|
index_matching_method = ena_maps.IndexMatchMethod.PUSH
|
|
237
301
|
|
|
238
|
-
pset_spacing_deg = self.rectangular_psets[0].spacing_deg
|
|
302
|
+
pset_spacing_deg = self.rectangular_psets[0].sky_grid.spacing_deg
|
|
239
303
|
|
|
240
304
|
# Mock frame_transform to return the az and el unchanged
|
|
241
305
|
mock_frame_transform_az_el.side_effect = (
|
|
@@ -251,7 +315,7 @@ class TestRectangularSkyMap:
|
|
|
251
315
|
for rectangular_pset in self.rectangular_psets:
|
|
252
316
|
rectangular_map.project_pset_values_to_map(
|
|
253
317
|
rectangular_pset,
|
|
254
|
-
value_keys=["counts", "
|
|
318
|
+
value_keys=["counts", "exposure_factor"],
|
|
255
319
|
index_match_method=index_matching_method,
|
|
256
320
|
)
|
|
257
321
|
|
|
@@ -262,20 +326,22 @@ class TestRectangularSkyMap:
|
|
|
262
326
|
simple_summed_pset_counts_by_energy = np.zeros(
|
|
263
327
|
shape=(
|
|
264
328
|
self.rectangular_l1c_pset_products[0]["counts"].sizes[
|
|
265
|
-
CoordNames.
|
|
329
|
+
CoordNames.ENERGY_ULTRA.value
|
|
266
330
|
],
|
|
267
331
|
)
|
|
268
332
|
)
|
|
269
333
|
for pset in self.rectangular_l1c_pset_products:
|
|
270
334
|
simple_summed_pset_counts_by_energy += pset["counts"].sum(
|
|
271
|
-
dim=[
|
|
335
|
+
dim=[
|
|
336
|
+
d for d in pset["counts"].dims if d != CoordNames.ENERGY_ULTRA.value
|
|
337
|
+
]
|
|
272
338
|
)
|
|
273
339
|
|
|
274
340
|
rmap_counts_per_energy_bin = rectangular_map.data_1d["counts"].sum(
|
|
275
341
|
dim=[
|
|
276
342
|
d
|
|
277
343
|
for d in rectangular_map.data_1d["counts"].dims
|
|
278
|
-
if d != CoordNames.
|
|
344
|
+
if d != CoordNames.ENERGY_ULTRA.value
|
|
279
345
|
]
|
|
280
346
|
)
|
|
281
347
|
|
|
@@ -344,7 +410,7 @@ class TestRectangularSkyMap:
|
|
|
344
410
|
|
|
345
411
|
rectangular_map.project_pset_values_to_map(
|
|
346
412
|
rectangular_pset,
|
|
347
|
-
value_keys=["counts", "
|
|
413
|
+
value_keys=["counts", "exposure_factor"],
|
|
348
414
|
index_match_method=index_matching_method,
|
|
349
415
|
)
|
|
350
416
|
expected_value_every_pixel += pset_num
|
|
@@ -372,13 +438,13 @@ class TestRectangularSkyMap:
|
|
|
372
438
|
assert "counts" in rect_map_ds.data_vars
|
|
373
439
|
assert rect_map_ds["counts"].shape == (
|
|
374
440
|
1,
|
|
375
|
-
rectangular_pset.data["counts"].sizes[CoordNames.
|
|
441
|
+
rectangular_pset.data["counts"].sizes[CoordNames.ENERGY_ULTRA.value],
|
|
376
442
|
360 / skymap_spacing,
|
|
377
443
|
180 / skymap_spacing,
|
|
378
444
|
)
|
|
379
445
|
assert rect_map_ds["counts"].dims == (
|
|
380
446
|
CoordNames.TIME.value,
|
|
381
|
-
CoordNames.
|
|
447
|
+
CoordNames.ENERGY_ULTRA.value,
|
|
382
448
|
CoordNames.AZIMUTH_L2.value,
|
|
383
449
|
CoordNames.ELEVATION_L2.value,
|
|
384
450
|
)
|
|
@@ -403,8 +469,8 @@ class TestHealpixSkyMap:
|
|
|
403
469
|
)
|
|
404
470
|
self.ultra_psets = [
|
|
405
471
|
ena_maps.UltraPointingSet(
|
|
472
|
+
l1c_product,
|
|
406
473
|
spice_reference_frame=geometry.SpiceFrame.IMAP_DPS,
|
|
407
|
-
l1c_dataset=l1c_product,
|
|
408
474
|
)
|
|
409
475
|
for l1c_product in self.ultra_l1c_pset_products
|
|
410
476
|
]
|
|
@@ -418,8 +484,8 @@ class TestHealpixSkyMap:
|
|
|
418
484
|
)
|
|
419
485
|
self.rectangular_psets = [
|
|
420
486
|
ena_maps.RectangularPointingSet(
|
|
487
|
+
l1c_product,
|
|
421
488
|
spice_reference_frame=geometry.SpiceFrame.IMAP_DPS,
|
|
422
|
-
l1c_dataset=l1c_product,
|
|
423
489
|
)
|
|
424
490
|
for l1c_product in self.rectangular_l1c_pset_products
|
|
425
491
|
]
|
|
@@ -491,7 +557,7 @@ class TestHealpixSkyMap:
|
|
|
491
557
|
|
|
492
558
|
# Create a PointingSet with a bright spot
|
|
493
559
|
mock_pset_input_frame = ena_maps.UltraPointingSet(
|
|
494
|
-
|
|
560
|
+
self.ultra_l1c_pset_products[0],
|
|
495
561
|
spice_reference_frame=geometry.SpiceFrame.IMAP_DPS,
|
|
496
562
|
)
|
|
497
563
|
mock_pset_input_frame.data["counts"].values = np.zeros_like(
|
|
@@ -534,7 +600,7 @@ class TestHealpixSkyMap:
|
|
|
534
600
|
assert "counts" in hp_map.data_1d.data_vars
|
|
535
601
|
|
|
536
602
|
# Find the maximum value in the spatial pixel dimension of the healpix map
|
|
537
|
-
bright_hp_pixel_index = hp_map.data_1d["counts"][0, :].argmax()
|
|
603
|
+
bright_hp_pixel_index = hp_map.data_1d["counts"][0, :].values.argmax()
|
|
538
604
|
bright_hp_pixel_az_el = hp_map.az_el_points[bright_hp_pixel_index]
|
|
539
605
|
|
|
540
606
|
np.testing.assert_allclose(
|
|
@@ -573,7 +639,7 @@ class TestHealpixSkyMap:
|
|
|
573
639
|
|
|
574
640
|
# Create a PointingSet with a bright spot
|
|
575
641
|
mock_pset_input_frame = ena_maps.RectangularPointingSet(
|
|
576
|
-
|
|
642
|
+
self.rectangular_l1c_pset_products[0],
|
|
577
643
|
spice_reference_frame=geometry.SpiceFrame.IMAP_DPS,
|
|
578
644
|
)
|
|
579
645
|
mock_pset_input_frame.data["counts"].values = np.zeros_like(
|
|
@@ -584,10 +650,13 @@ class TestHealpixSkyMap:
|
|
|
584
650
|
mock_pset_input_frame.data["counts"].values[
|
|
585
651
|
:,
|
|
586
652
|
:,
|
|
587
|
-
int(
|
|
653
|
+
int(
|
|
654
|
+
input_bright_pixel_az_el_deg[0]
|
|
655
|
+
// mock_pset_input_frame.sky_grid.spacing_deg
|
|
656
|
+
),
|
|
588
657
|
int(
|
|
589
658
|
(90 + input_bright_pixel_az_el_deg[1])
|
|
590
|
-
// mock_pset_input_frame.spacing_deg
|
|
659
|
+
// mock_pset_input_frame.sky_grid.spacing_deg
|
|
591
660
|
),
|
|
592
661
|
] = 1
|
|
593
662
|
|
|
@@ -625,12 +694,12 @@ class TestHealpixSkyMap:
|
|
|
625
694
|
assert "counts" in hp_map_ds.data_vars
|
|
626
695
|
assert hp_map_ds["counts"].shape == (
|
|
627
696
|
1,
|
|
628
|
-
mock_pset_input_frame.data["counts"].sizes[CoordNames.
|
|
697
|
+
mock_pset_input_frame.data["counts"].sizes[CoordNames.ENERGY_ULTRA.value],
|
|
629
698
|
hp_map.num_points,
|
|
630
699
|
)
|
|
631
700
|
assert hp_map_ds["counts"].dims == (
|
|
632
701
|
CoordNames.TIME.value,
|
|
633
|
-
CoordNames.
|
|
702
|
+
CoordNames.ENERGY_ULTRA.value,
|
|
634
703
|
CoordNames.HEALPIX_INDEX.value,
|
|
635
704
|
)
|
|
636
705
|
np.testing.assert_array_equal(
|
|
@@ -638,6 +707,221 @@ class TestHealpixSkyMap:
|
|
|
638
707
|
hp_map.data_1d["counts"].values,
|
|
639
708
|
)
|
|
640
709
|
|
|
710
|
+
@mock.patch("astropy_healpix.healpy.ang2pix")
|
|
711
|
+
def test_calculate_rect_pixel_value_from_healpix_map_n_subdivisions(
|
|
712
|
+
self,
|
|
713
|
+
mock_ang2pix,
|
|
714
|
+
):
|
|
715
|
+
"""Test getting rectangular pixel values from HealpixSkyMap via subdivision."""
|
|
716
|
+
|
|
717
|
+
# Mock ang2pix to return fixed values based on a dict
|
|
718
|
+
pixel_dict = {
|
|
719
|
+
# 0 subdiv - just 1 pixel
|
|
720
|
+
(180, 0): 0,
|
|
721
|
+
# 1 subdiv - all subpix have same solid angle because centered on equator
|
|
722
|
+
(179, -1): 1,
|
|
723
|
+
(179, 1): 2,
|
|
724
|
+
(181, -1): 3,
|
|
725
|
+
(181, 1): 4,
|
|
726
|
+
# 2 subdiv - 'Inner' subpix have larger solid angle than 'outer' subpix
|
|
727
|
+
(178.5, -1.5): 5,
|
|
728
|
+
(178.5, -0.5): 6,
|
|
729
|
+
(178.5, 0.5): 7,
|
|
730
|
+
(178.5, 1.5): 8,
|
|
731
|
+
(179.5, -1.5): 9,
|
|
732
|
+
(179.5, -0.5): 10,
|
|
733
|
+
(179.5, 0.5): 11,
|
|
734
|
+
(179.5, 1.5): 12,
|
|
735
|
+
(180.5, -1.5): 12,
|
|
736
|
+
(180.5, -0.5): 14,
|
|
737
|
+
(180.5, 0.5): 15,
|
|
738
|
+
(180.5, 1.5): 16,
|
|
739
|
+
(181.5, -1.5): 17,
|
|
740
|
+
(181.5, -0.5): 18,
|
|
741
|
+
(181.5, 0.5): 19,
|
|
742
|
+
(181.5, 1.5): 20,
|
|
743
|
+
}
|
|
744
|
+
expected_mean_0_subdivisions = 0
|
|
745
|
+
expected_mean_1_subdivisions = 2.5
|
|
746
|
+
expected_mean_2_subdivisions = 12.5
|
|
747
|
+
|
|
748
|
+
def mock_ang2pix_fn(nside, theta, phi, nest=True, lonlat=False):
|
|
749
|
+
vals = []
|
|
750
|
+
for pix_num in range(len(theta)):
|
|
751
|
+
key = (theta[pix_num], phi[pix_num])
|
|
752
|
+
vals.append(pixel_dict.get(key, 0))
|
|
753
|
+
return np.array(vals)
|
|
754
|
+
|
|
755
|
+
hp_map = ena_maps.HealpixSkyMap(
|
|
756
|
+
nside=16,
|
|
757
|
+
spice_frame=geometry.SpiceFrame.ECLIPJ2000,
|
|
758
|
+
nested=True,
|
|
759
|
+
)
|
|
760
|
+
hp_map.data_1d["counts"] = xr.DataArray(
|
|
761
|
+
data=[
|
|
762
|
+
np.arange(hp_map.num_points),
|
|
763
|
+
],
|
|
764
|
+
dims=["epoch", "pixel"],
|
|
765
|
+
)
|
|
766
|
+
|
|
767
|
+
for num_subdiv, (expected_value, atol) in enumerate(
|
|
768
|
+
[
|
|
769
|
+
# The first subdivs have all the same solid angle
|
|
770
|
+
(expected_mean_0_subdivisions, 1e-9),
|
|
771
|
+
(expected_mean_1_subdivisions, 1e-9),
|
|
772
|
+
# Slight difference from not taking into account asym solid angle
|
|
773
|
+
(expected_mean_2_subdivisions, 0.1),
|
|
774
|
+
]
|
|
775
|
+
):
|
|
776
|
+
mock_ang2pix.reset_mock()
|
|
777
|
+
mock_ang2pix.side_effect = mock_ang2pix_fn
|
|
778
|
+
mean_value = (
|
|
779
|
+
hp_map.calculate_rect_pixel_value_from_healpix_map_n_subdivisions(
|
|
780
|
+
rect_pix_center_lon_lat=(180, 0),
|
|
781
|
+
rect_pix_spacing_deg=4,
|
|
782
|
+
value_array=hp_map.data_1d["counts"],
|
|
783
|
+
num_subdivisions=num_subdiv,
|
|
784
|
+
)
|
|
785
|
+
)
|
|
786
|
+
np.testing.assert_allclose(
|
|
787
|
+
mean_value,
|
|
788
|
+
expected_value,
|
|
789
|
+
atol=atol,
|
|
790
|
+
err_msg=f"Failed for num_subdivisions: {num_subdiv}",
|
|
791
|
+
)
|
|
792
|
+
hp_map.calculate_rect_pixel_value_from_healpix_map_n_subdivisions(
|
|
793
|
+
rect_pix_center_lon_lat=(180, 0),
|
|
794
|
+
rect_pix_spacing_deg=2,
|
|
795
|
+
value_array=hp_map.data_1d["counts"],
|
|
796
|
+
num_subdivisions=0,
|
|
797
|
+
)
|
|
798
|
+
|
|
799
|
+
@mock.patch(
|
|
800
|
+
"imap_processing.ena_maps.ena_maps.HealpixSkyMap.calculate_rect_pixel_value_from_healpix_map_n_subdivisions"
|
|
801
|
+
)
|
|
802
|
+
def test_get_rect_pixel_value_recursive_subdivs(
|
|
803
|
+
self,
|
|
804
|
+
mock_calculate_rect_pixel_value_from_healpix_map_n_subdivisions,
|
|
805
|
+
):
|
|
806
|
+
"""Test that the recursive subdivision works as expected with different rtol."""
|
|
807
|
+
|
|
808
|
+
# Mock the function to return a fixed value for a number of subdivisions
|
|
809
|
+
value_by_subdivisions = {
|
|
810
|
+
0: 100.0,
|
|
811
|
+
1: 110.0, # 10/110 = 0.09090909 change
|
|
812
|
+
2: 105.0, # 5/105 = 0.04761905 change
|
|
813
|
+
3: 107.0, # 2/107 = 0.01869159 change
|
|
814
|
+
4: 107.5, # 0.5/107.5 = 0.00465116 change
|
|
815
|
+
5: 107.51, # 0.01/107.51 = 0.00009301 change
|
|
816
|
+
6: 107.5099, # 0.0001/107.5099 = 0.00000093 change
|
|
817
|
+
7: 120, # Big change - but will stop because of MAX SUBDIVS
|
|
818
|
+
}
|
|
819
|
+
required_rtols = [
|
|
820
|
+
0.1,
|
|
821
|
+
0.05,
|
|
822
|
+
0.02,
|
|
823
|
+
0.005,
|
|
824
|
+
0.0001,
|
|
825
|
+
0.000001,
|
|
826
|
+
1e-12,
|
|
827
|
+
]
|
|
828
|
+
|
|
829
|
+
mock_calculate_rect_pixel_value_from_healpix_map_n_subdivisions.side_effect = (
|
|
830
|
+
lambda *args, **kwargs: np.array(
|
|
831
|
+
[
|
|
832
|
+
value_by_subdivisions[kwargs["num_subdivisions"]],
|
|
833
|
+
]
|
|
834
|
+
)
|
|
835
|
+
)
|
|
836
|
+
hp_map = ena_maps.HealpixSkyMap(
|
|
837
|
+
nside=16,
|
|
838
|
+
spice_frame=geometry.SpiceFrame.ECLIPJ2000,
|
|
839
|
+
)
|
|
840
|
+
|
|
841
|
+
# Test the recursive subdivision by setting different tolerances to get the
|
|
842
|
+
# expected number of subdivisions and resultant mean value.
|
|
843
|
+
for expected_subdiv_level in range(1, len(required_rtols)):
|
|
844
|
+
mean, depth = hp_map.get_rect_pixel_value_recursive_subdivs(
|
|
845
|
+
rect_pix_center_lon_lat=(180, 0),
|
|
846
|
+
rect_pix_spacing_deg=4,
|
|
847
|
+
value_array=[],
|
|
848
|
+
rtol=required_rtols[expected_subdiv_level - 1],
|
|
849
|
+
max_subdivision_depth=7,
|
|
850
|
+
)
|
|
851
|
+
assert depth == expected_subdiv_level
|
|
852
|
+
np.testing.assert_equal(
|
|
853
|
+
mean,
|
|
854
|
+
value_by_subdivisions[expected_subdiv_level],
|
|
855
|
+
err_msg=f"Failed for expected_subdiv_level: {expected_subdiv_level}",
|
|
856
|
+
)
|
|
857
|
+
|
|
858
|
+
def test_to_rectangular_skymap(
|
|
859
|
+
self,
|
|
860
|
+
):
|
|
861
|
+
hp_map = ena_maps.HealpixSkyMap(
|
|
862
|
+
nside=64,
|
|
863
|
+
spice_frame=geometry.SpiceFrame.ECLIPJ2000,
|
|
864
|
+
)
|
|
865
|
+
|
|
866
|
+
hp_map.data_1d["counts"] = xr.DataArray(
|
|
867
|
+
data=np.fromfunction(
|
|
868
|
+
lambda time, energy, pixel: 1000 + pixel * (10 * (energy + 1)),
|
|
869
|
+
shape=(1, 10, hp_map.num_points),
|
|
870
|
+
dtype=np.float32,
|
|
871
|
+
),
|
|
872
|
+
dims=["epoch", "energy", "pixel"],
|
|
873
|
+
)
|
|
874
|
+
hp_map.data_1d["exposure_factor"] = xr.DataArray(
|
|
875
|
+
data=np.ones((10, hp_map.num_points)),
|
|
876
|
+
dims=["energy", "pixel"],
|
|
877
|
+
)
|
|
878
|
+
hp_map.data_1d["observation_date"] = xr.DataArray(
|
|
879
|
+
data=np.ones(hp_map.num_points),
|
|
880
|
+
dims=["pixel"],
|
|
881
|
+
)
|
|
882
|
+
|
|
883
|
+
rect_map, subdiv_depth_dict = hp_map.to_rectangular_skymap(
|
|
884
|
+
rect_spacing_deg=2,
|
|
885
|
+
value_keys=["counts", "exposure_factor", "observation_date"],
|
|
886
|
+
)
|
|
887
|
+
|
|
888
|
+
for value_key, subdiv_depth in subdiv_depth_dict.items():
|
|
889
|
+
# subdiv depth should always be between 1 and
|
|
890
|
+
# ena_maps.MAX_SUBDIV_RECURSION_DEPTH
|
|
891
|
+
np.testing.assert_array_less(
|
|
892
|
+
0,
|
|
893
|
+
subdiv_depth,
|
|
894
|
+
err_msg=f"subdiv <1 for: {value_key}",
|
|
895
|
+
)
|
|
896
|
+
np.testing.assert_array_less(
|
|
897
|
+
subdiv_depth,
|
|
898
|
+
ena_maps.MAX_SUBDIV_RECURSION_DEPTH + 1,
|
|
899
|
+
err_msg=f"subdiv >MAX for: {value_key}",
|
|
900
|
+
)
|
|
901
|
+
|
|
902
|
+
# The min and max values of the rect and healpix maps should be close
|
|
903
|
+
# The min will have a larger relative tolerance because the variation
|
|
904
|
+
# in the test data is larger in comparison to the min value than to the max
|
|
905
|
+
np.testing.assert_allclose(
|
|
906
|
+
rect_map.data_1d[value_key].min(),
|
|
907
|
+
hp_map.data_1d[value_key].min(),
|
|
908
|
+
rtol=5e-2,
|
|
909
|
+
err_msg=f"Min values of {value_key} do not match",
|
|
910
|
+
)
|
|
911
|
+
np.testing.assert_allclose(
|
|
912
|
+
rect_map.data_1d[value_key].max(),
|
|
913
|
+
hp_map.data_1d[value_key].max(),
|
|
914
|
+
rtol=1e-3,
|
|
915
|
+
err_msg=f"Max values of {value_key} do not match",
|
|
916
|
+
)
|
|
917
|
+
|
|
918
|
+
# The dims of the rect map should be the same as the healpix map,
|
|
919
|
+
# except for the final pixel dimension
|
|
920
|
+
assert (
|
|
921
|
+
rect_map.data_1d[value_key].dims[:-1]
|
|
922
|
+
== hp_map.data_1d[value_key].dims[:-1]
|
|
923
|
+
)
|
|
924
|
+
|
|
641
925
|
|
|
642
926
|
class TestIndexMatching:
|
|
643
927
|
@pytest.fixture(autouse=True)
|
|
@@ -649,8 +933,8 @@ class TestIndexMatching:
|
|
|
649
933
|
)
|
|
650
934
|
self.rectangular_psets = [
|
|
651
935
|
ena_maps.RectangularPointingSet(
|
|
936
|
+
l1c_product,
|
|
652
937
|
spice_reference_frame=geometry.SpiceFrame.IMAP_DPS,
|
|
653
|
-
l1c_dataset=l1c_product,
|
|
654
938
|
)
|
|
655
939
|
for l1c_product in self.rectangular_l1c_pset_products
|
|
656
940
|
]
|
|
@@ -670,7 +954,7 @@ class TestIndexMatching:
|
|
|
670
954
|
|
|
671
955
|
# Mock a PSET, overriding the az/el points
|
|
672
956
|
mock_pset_input_frame = ena_maps.RectangularPointingSet(
|
|
673
|
-
|
|
957
|
+
self.rectangular_l1c_pset_products[0],
|
|
674
958
|
spice_reference_frame=geometry.SpiceFrame.IMAP_DPS,
|
|
675
959
|
)
|
|
676
960
|
manual_az_el_coords = np.array(
|
|
@@ -748,7 +1032,7 @@ class TestIndexMatching:
|
|
|
748
1032
|
|
|
749
1033
|
# Make a PointingSet
|
|
750
1034
|
mock_pset_input_frame = ena_maps.RectangularPointingSet(
|
|
751
|
-
|
|
1035
|
+
self.rectangular_l1c_pset_products[0],
|
|
752
1036
|
spice_reference_frame=geometry.SpiceFrame.IMAP_DPS,
|
|
753
1037
|
)
|
|
754
1038
|
|
|
@@ -787,7 +1071,7 @@ class TestIndexMatching:
|
|
|
787
1071
|
self,
|
|
788
1072
|
):
|
|
789
1073
|
mock_pset_input_frame = ena_maps.RectangularPointingSet(
|
|
790
|
-
|
|
1074
|
+
self.rectangular_l1c_pset_products[0],
|
|
791
1075
|
spice_reference_frame=geometry.SpiceFrame.ECLIPJ2000,
|
|
792
1076
|
)
|
|
793
1077
|
# Until implemented, just change the tiling on a RectangularSkyMap
|
|
@@ -803,11 +1087,11 @@ class TestIndexMatching:
|
|
|
803
1087
|
|
|
804
1088
|
def test_match_coords_to_indices_pset_to_pset_error(self):
|
|
805
1089
|
mock_pset_input_frame = ena_maps.RectangularPointingSet(
|
|
806
|
-
|
|
1090
|
+
self.rectangular_l1c_pset_products[0],
|
|
807
1091
|
spice_reference_frame=geometry.SpiceFrame.IMAP_DPS,
|
|
808
1092
|
)
|
|
809
1093
|
mock_pset_output_frame = ena_maps.RectangularPointingSet(
|
|
810
|
-
|
|
1094
|
+
self.rectangular_l1c_pset_products[1],
|
|
811
1095
|
spice_reference_frame=geometry.SpiceFrame.IMAP_DPS,
|
|
812
1096
|
)
|
|
813
1097
|
with pytest.raises(
|
|
@@ -836,3 +1120,148 @@ class TestIndexMatching:
|
|
|
836
1120
|
_ = ena_maps.match_coords_to_indices(
|
|
837
1121
|
mock_rect_map_1, mock_rect_map_2, event_et=0
|
|
838
1122
|
)
|
|
1123
|
+
|
|
1124
|
+
|
|
1125
|
+
class TestAbstractSkyMap:
|
|
1126
|
+
@pytest.mark.parametrize(
|
|
1127
|
+
"skymap_props_dict",
|
|
1128
|
+
[
|
|
1129
|
+
pytest.param(
|
|
1130
|
+
# HealpixSkyMap properties
|
|
1131
|
+
{
|
|
1132
|
+
"sky_tiling_type": "HEALPIX",
|
|
1133
|
+
"nside": 32,
|
|
1134
|
+
"nested": True,
|
|
1135
|
+
"spice_reference_frame": geometry.SpiceFrame.ECLIPJ2000.name,
|
|
1136
|
+
"values_to_push_project": ["foo", "bar"],
|
|
1137
|
+
},
|
|
1138
|
+
id="healpix-skymap",
|
|
1139
|
+
),
|
|
1140
|
+
pytest.param(
|
|
1141
|
+
{
|
|
1142
|
+
"sky_tiling_type": "RECTANGULAR",
|
|
1143
|
+
"spacing_deg": 2,
|
|
1144
|
+
"spice_reference_frame": geometry.SpiceFrame.ECLIPJ2000.name,
|
|
1145
|
+
"values_to_pull_project": ["potato", "po-tah-to"],
|
|
1146
|
+
},
|
|
1147
|
+
id="rectangular-skymap",
|
|
1148
|
+
),
|
|
1149
|
+
],
|
|
1150
|
+
)
|
|
1151
|
+
def test_to_dict_and_from_dict(self, skymap_props_dict):
|
|
1152
|
+
"""Test serialization to and from dictionary"""
|
|
1153
|
+
# Make a SkyMap from the original properties dict
|
|
1154
|
+
skymap_from_dict = ena_maps.AbstractSkyMap.from_dict(skymap_props_dict)
|
|
1155
|
+
|
|
1156
|
+
# Use the SkyMap to create a new properties dict
|
|
1157
|
+
dict_from_skymap = skymap_from_dict.to_dict()
|
|
1158
|
+
|
|
1159
|
+
assert (
|
|
1160
|
+
skymap_from_dict.spice_reference_frame
|
|
1161
|
+
== geometry.SpiceFrame[skymap_props_dict["spice_reference_frame"]]
|
|
1162
|
+
)
|
|
1163
|
+
|
|
1164
|
+
if skymap_props_dict["sky_tiling_type"] == "HEALPIX":
|
|
1165
|
+
assert isinstance(skymap_from_dict, ena_maps.HealpixSkyMap), (
|
|
1166
|
+
"from_dict should return a HealpixSkyMap object"
|
|
1167
|
+
)
|
|
1168
|
+
assert skymap_from_dict.nside == skymap_props_dict["nside"]
|
|
1169
|
+
assert skymap_from_dict.nested == skymap_props_dict["nested"]
|
|
1170
|
+
assert (
|
|
1171
|
+
skymap_from_dict.values_to_push_project
|
|
1172
|
+
== skymap_props_dict["values_to_push_project"]
|
|
1173
|
+
)
|
|
1174
|
+
assert skymap_from_dict.values_to_pull_project == []
|
|
1175
|
+
|
|
1176
|
+
elif skymap_props_dict["sky_tiling_type"] == "RECTANGULAR":
|
|
1177
|
+
assert isinstance(skymap_from_dict, ena_maps.RectangularSkyMap), (
|
|
1178
|
+
"from_dict should return a RectangularSkyMap object"
|
|
1179
|
+
)
|
|
1180
|
+
assert skymap_from_dict.spacing_deg == skymap_props_dict["spacing_deg"]
|
|
1181
|
+
assert skymap_from_dict.values_to_push_project == []
|
|
1182
|
+
assert (
|
|
1183
|
+
skymap_from_dict.values_to_pull_project
|
|
1184
|
+
== skymap_props_dict["values_to_pull_project"]
|
|
1185
|
+
)
|
|
1186
|
+
|
|
1187
|
+
for key in [
|
|
1188
|
+
"sky_tiling_type",
|
|
1189
|
+
"spice_reference_frame",
|
|
1190
|
+
"nside",
|
|
1191
|
+
"nested",
|
|
1192
|
+
"spacing_deg",
|
|
1193
|
+
]:
|
|
1194
|
+
if key in skymap_props_dict:
|
|
1195
|
+
assert dict_from_skymap[key] == skymap_props_dict[key]
|
|
1196
|
+
|
|
1197
|
+
# Check that the dict from the SkyMap matches the original dict ONLY after
|
|
1198
|
+
# adding automatically added "values_to_push_project"/"values_to_pull_project"
|
|
1199
|
+
# key to the original dict
|
|
1200
|
+
assert dict_from_skymap != skymap_props_dict
|
|
1201
|
+
|
|
1202
|
+
# In the dicts passed in above, the HEALPIX one is missing the pull key
|
|
1203
|
+
# and the RECTANGULAR one is missing the push key
|
|
1204
|
+
if skymap_props_dict["sky_tiling_type"] == "HEALPIX":
|
|
1205
|
+
skymap_props_dict["values_to_pull_project"] = []
|
|
1206
|
+
elif skymap_props_dict["sky_tiling_type"] == "RECTANGULAR":
|
|
1207
|
+
skymap_props_dict["values_to_push_project"] = []
|
|
1208
|
+
assert dict_from_skymap == skymap_props_dict
|
|
1209
|
+
|
|
1210
|
+
# Change a value in the new dict and check that it is not equal to the original
|
|
1211
|
+
dict_from_skymap["spice_reference_frame"] = "SPACE!"
|
|
1212
|
+
assert (
|
|
1213
|
+
dict_from_skymap["spice_reference_frame"]
|
|
1214
|
+
!= skymap_props_dict["spice_reference_frame"]
|
|
1215
|
+
)
|
|
1216
|
+
|
|
1217
|
+
def test_to_json_and_from_json(self):
|
|
1218
|
+
"""Test serialization to and from JSON"""
|
|
1219
|
+
# Make a SkyMap from the original properties dict
|
|
1220
|
+
skymap_props_dict = {
|
|
1221
|
+
"sky_tiling_type": "HEALPIX",
|
|
1222
|
+
"nside": 32,
|
|
1223
|
+
"nested": True,
|
|
1224
|
+
"spice_reference_frame": geometry.SpiceFrame.ECLIPJ2000.name,
|
|
1225
|
+
"values_to_push_project": ["foo", "bar"],
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
# Write a temporary json file with the properties dict
|
|
1229
|
+
|
|
1230
|
+
with tempfile.NamedTemporaryFile(
|
|
1231
|
+
delete=False, suffix=".json", mode="w"
|
|
1232
|
+
) as temp_file:
|
|
1233
|
+
json.dump(skymap_props_dict, temp_file)
|
|
1234
|
+
temp_file_path_input = temp_file.name
|
|
1235
|
+
|
|
1236
|
+
# Read the json file and create a new SkyMap from it
|
|
1237
|
+
skymap_from_json = ena_maps.AbstractSkyMap.from_json(temp_file_path_input)
|
|
1238
|
+
|
|
1239
|
+
# Create json output from the SkyMap at a separate temporary file path
|
|
1240
|
+
temp_file_path_output = tempfile.NamedTemporaryFile(
|
|
1241
|
+
delete=False, suffix=".json", mode="w"
|
|
1242
|
+
).name
|
|
1243
|
+
skymap_from_json.to_json(json_path=temp_file_path_output)
|
|
1244
|
+
|
|
1245
|
+
assert skymap_from_json.spice_reference_frame == geometry.SpiceFrame.ECLIPJ2000
|
|
1246
|
+
assert skymap_from_json.tiling_type is ena_maps.SkyTilingType.HEALPIX
|
|
1247
|
+
assert skymap_from_json.nside == 32
|
|
1248
|
+
assert skymap_from_json.nested is True
|
|
1249
|
+
assert skymap_from_json.values_to_push_project == ["foo", "bar"]
|
|
1250
|
+
assert skymap_from_json.values_to_pull_project == []
|
|
1251
|
+
|
|
1252
|
+
# Expect there to be a AttributeError when accessing a non-existent key
|
|
1253
|
+
with pytest.raises(AttributeError):
|
|
1254
|
+
_ = skymap_from_json.spacing_deg
|
|
1255
|
+
|
|
1256
|
+
# Check that the json output is the same as the original input ONLY
|
|
1257
|
+
# after adding automatically added
|
|
1258
|
+
# "values_to_push_project"/"values_to_pull_project" key to the original dict
|
|
1259
|
+
with open(temp_file_path_input) as f:
|
|
1260
|
+
original_json = json.load(f)
|
|
1261
|
+
with open(temp_file_path_output) as f:
|
|
1262
|
+
output_json = json.load(f)
|
|
1263
|
+
# The output json will have added an empty list for values_to_pull_project
|
|
1264
|
+
assert original_json != output_json
|
|
1265
|
+
# add the values_to_pull_project key to the original json
|
|
1266
|
+
original_json["values_to_pull_project"] = []
|
|
1267
|
+
assert original_json == output_json
|